Merge "Remove SlashState and SignalState from QSTile" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index a5178cf..599e5cf 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -12,30 +12,37 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+aconfig_srcjars = [
+ ":android.app.usage.flags-aconfig-java{.generated_srcjars}",
+ ":android.content.pm.flags-aconfig-java{.generated_srcjars}",
+ ":android.os.flags-aconfig-java{.generated_srcjars}",
+ ":android.os.vibrator.flags-aconfig-java{.generated_srcjars}",
+ ":android.security.flags-aconfig-java{.generated_srcjars}",
+ ":android.view.flags-aconfig-java{.generated_srcjars}",
+ ":camera_platform_flags_core_java_lib{.generated_srcjars}",
+ ":com.android.window.flags.window-aconfig-java{.generated_srcjars}",
+ ":com.android.hardware.input-aconfig-java{.generated_srcjars}",
+ ":com.android.text.flags-aconfig-java{.generated_srcjars}",
+ ":telecom_flags_core_java_lib{.generated_srcjars}",
+ ":telephony_flags_core_java_lib{.generated_srcjars}",
+ ":android.companion.virtual.flags-aconfig-java{.generated_srcjars}",
+ ":android.view.inputmethod.flags-aconfig-java{.generated_srcjars}",
+ ":android.widget.flags-aconfig-java{.generated_srcjars}",
+ ":com.android.media.flags.bettertogether-aconfig-java{.generated_srcjars}",
+ ":sdk_sandbox_flags_lib{.generated_srcjars}",
+ ":android.permission.flags-aconfig-java{.generated_srcjars}",
+]
+
+filegroup {
+ name: "framework-minus-apex-aconfig-srcjars",
+ srcs: aconfig_srcjars,
+}
+
// Aconfig declarations and libraries for the core framework
java_defaults {
name: "framework-minus-apex-aconfig-libraries",
-
// Add java_aconfig_libraries to here to add them to the core framework
- srcs: [
- ":android.app.usage.flags-aconfig-java{.generated_srcjars}",
- ":android.content.pm.flags-aconfig-java{.generated_srcjars}",
- ":android.os.flags-aconfig-java{.generated_srcjars}",
- ":android.os.vibrator.flags-aconfig-java{.generated_srcjars}",
- ":android.security.flags-aconfig-java{.generated_srcjars}",
- ":android.view.flags-aconfig-java{.generated_srcjars}",
- ":camera_platform_flags_core_java_lib{.generated_srcjars}",
- ":com.android.window.flags.window-aconfig-java{.generated_srcjars}",
- ":com.android.hardware.input-aconfig-java{.generated_srcjars}",
- ":com.android.text.flags-aconfig-java{.generated_srcjars}",
- ":telecom_flags_core_java_lib{.generated_srcjars}",
- ":telephony_flags_core_java_lib{.generated_srcjars}",
- ":android.companion.virtual.flags-aconfig-java{.generated_srcjars}",
- ":android.view.inputmethod.flags-aconfig-java{.generated_srcjars}",
- ":android.widget.flags-aconfig-java{.generated_srcjars}",
- ":com.android.media.flags.bettertogether-aconfig-java{.generated_srcjars}",
- ":sdk_sandbox_flags_lib{.generated_srcjars}",
- ],
+ srcs: aconfig_srcjars,
// Add aconfig-annotations-lib as a dependency for the optimization
libs: ["aconfig-annotations-lib"],
}
@@ -130,7 +137,6 @@
name: "android.security.flags-aconfig-java-host",
aconfig_declarations: "android.security.flags-aconfig",
host_supported: true,
- test: true,
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
@@ -250,3 +256,16 @@
aconfig_declarations: "com.android.media.flags.bettertogether-aconfig",
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+
+// Permissions
+aconfig_declarations {
+ name: "android.permission.flags-aconfig",
+ package: "android.permission.flags",
+ srcs: ["core/java/android/permission/flags.aconfig"],
+}
+
+java_aconfig_library {
+ name: "android.permission.flags-aconfig-java",
+ aconfig_declarations: "android.permission.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
diff --git a/Android.bp b/Android.bp
index 57a5a3c..f1a3af2 100644
--- a/Android.bp
+++ b/Android.bp
@@ -621,6 +621,7 @@
"--api-lint-ignore-prefix org. " +
"--error NoSettingsProvider " +
"--error UnhiddenSystemApi " +
+ "--error UnflaggedApi " +
"--force-convert-to-warning-nullability-annotations +*:-android.*:+android.icu.*:-dalvik.* " +
"--hide BroadcastBehavior " +
"--hide CallbackInterface " +
diff --git a/apex/jobscheduler/framework/java/android/os/WearModeManagerInternal.java b/apex/jobscheduler/framework/java/android/os/WearModeManagerInternal.java
index 9699757..75838c2 100644
--- a/apex/jobscheduler/framework/java/android/os/WearModeManagerInternal.java
+++ b/apex/jobscheduler/framework/java/android/os/WearModeManagerInternal.java
@@ -49,13 +49,25 @@
String QUICK_DOZE_REQUEST_IDENTIFIER = "quick_doze_request";
/**
+ * Mode manager off body state identifier.
+ *
+ * <p>Unique identifier that can be used as identifier parameter in
+ * registerInternalStateObserver
+ * to listen to changes in quick doze request state from mode manager.
+ *
+ * TODO(b/288276510): convert to int constant
+ */
+ String OFFBODY_STATE_ID = "off_body";
+
+ /**
* StringDef for Mode manager identifiers.
*
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
@StringDef({
- QUICK_DOZE_REQUEST_IDENTIFIER
+ QUICK_DOZE_REQUEST_IDENTIFIER,
+ OFFBODY_STATE_ID
})
@Target(ElementType.TYPE_USE)
@interface Identifier {
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index b8596d5..f252a0b 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -377,7 +377,11 @@
@GuardedBy("this")
private boolean mModeManagerRequestedQuickDoze;
@GuardedBy("this")
+ private boolean mIsOffBody;
+ @GuardedBy("this")
private boolean mForceModeManagerQuickDozeRequest;
+ @GuardedBy("this")
+ private boolean mForceModeManagerOffBodyState;
/** Time in the elapsed realtime timebase when this listener last received a motion event. */
@GuardedBy("this")
@@ -437,6 +441,7 @@
private static final int ACTIVE_REASON_ALARM = 7;
private static final int ACTIVE_REASON_EMERGENCY_CALL = 8;
private static final int ACTIVE_REASON_MODE_MANAGER = 9;
+ private static final int ACTIVE_REASON_ONBODY = 10;
@VisibleForTesting
static String stateToString(int state) {
@@ -837,7 +842,7 @@
class ModeManagerQuickDozeRequestConsumer implements Consumer<Boolean> {
@Override
public void accept(Boolean enabled) {
- Slog.d(TAG, "Mode manager quick doze request: " + enabled);
+ Slog.i(TAG, "Mode manager quick doze request: " + enabled);
synchronized (DeviceIdleController.this) {
if (!mForceModeManagerQuickDozeRequest
&& mModeManagerRequestedQuickDoze != enabled) {
@@ -848,13 +853,46 @@
}
@GuardedBy("DeviceIdleController.this")
- public void onModeManagerRequestChangedLocked() {
+ private void onModeManagerRequestChangedLocked() {
// Get into quick doze faster when mode manager requests instead of taking
// traditional multi-stage approach.
+ maybeBecomeActiveOnModeManagerEventsLocked();
updateQuickDozeFlagLocked();
- if (!mModeManagerRequestedQuickDoze && !mBatterySaverEnabled) {
- mActiveReason = ACTIVE_REASON_MODE_MANAGER;
- becomeActiveLocked("mode_manager", Process.myUid());
+ }
+ }
+
+ @VisibleForTesting
+ class ModeManagerOffBodyStateConsumer implements Consumer<Boolean> {
+ @Override
+ public void accept(Boolean isOffBody) {
+ Slog.i(TAG, "Offbody event from mode manager: " + isOffBody);
+ synchronized (DeviceIdleController.this) {
+ if (!mForceModeManagerOffBodyState && mIsOffBody != isOffBody) {
+ mIsOffBody = isOffBody;
+ onModeManagerOffBodyChangedLocked();
+ }
+ }
+ }
+
+ @GuardedBy("DeviceIdleController.this")
+ private void onModeManagerOffBodyChangedLocked() {
+ maybeBecomeActiveOnModeManagerEventsLocked();
+ }
+ }
+
+ @GuardedBy("DeviceIdleController.this")
+ private void maybeBecomeActiveOnModeManagerEventsLocked() {
+ synchronized (DeviceIdleController.this) {
+ if (mQuickDozeActivated) {
+ // Quick doze is enabled so don't turn the device active.
+ return;
+ }
+ // Fall through when quick doze is not requested.
+
+ if (!mIsOffBody) {
+ // Quick doze was not requested and device is on body so turn the device active.
+ mActiveReason = ACTIVE_REASON_ONBODY;
+ becomeActiveLocked("on_body", Process.myUid());
}
}
}
@@ -864,6 +902,10 @@
new ModeManagerQuickDozeRequestConsumer();
@VisibleForTesting
+ final ModeManagerOffBodyStateConsumer mModeManagerOffBodyStateConsumer =
+ new ModeManagerOffBodyStateConsumer();
+
+ @VisibleForTesting
final class MotionListener extends TriggerEventListener
implements SensorEventListener {
@@ -2648,6 +2690,12 @@
WearModeManagerInternal.QUICK_DOZE_REQUEST_IDENTIFIER,
AppSchedulingModuleThread.getExecutor(),
mModeManagerQuickDozeRequestConsumer);
+
+ modeManagerInternal.addActiveStateChangeListener(
+ WearModeManagerInternal.OFFBODY_STATE_ID,
+ AppSchedulingModuleThread.getExecutor(),
+ mModeManagerOffBodyStateConsumer
+ );
}
}
mLocalPowerManager.registerLowPowerModeObserver(ServiceType.QUICK_DOZE,
@@ -4463,7 +4511,7 @@
pw.println(
" Resume normal functioning after force-idle or force-inactive or "
+ "force-modemanager-quickdoze.");
- pw.println(" get [light|deep|force|screen|charging|network|offbody|forcebodystate]");
+ pw.println(" get [light|deep|force|screen|charging|network|offbody|forceoffbody]");
pw.println(" Retrieve the current given state.");
pw.println(" disable [light|deep|all]");
pw.println(" Completely disable device idle mode.");
@@ -4500,6 +4548,10 @@
pw.println(" force-modemanager-quickdoze [true|false]");
pw.println(" Simulate mode manager request to enable (true) or disable (false) "
+ "quick doze. Mode manager changes will be ignored until unforce is called.");
+ pw.println(" force-modemanager-offbody [true|false]");
+ pw.println(" Force mode manager offbody state, this can be used to simulate "
+ + "device being off-body (true) or on-body (false). Mode manager changes "
+ + "will be ignored until unforce is called.");
}
class Shell extends ShellCommand {
@@ -4634,6 +4686,9 @@
mForceModeManagerQuickDozeRequest = false;
pw.println("mForceModeManagerQuickDozeRequest: "
+ mForceModeManagerQuickDozeRequest);
+ mForceModeManagerOffBodyState = false;
+ pw.println("mForceModeManagerOffBodyState: "
+ + mForceModeManagerOffBodyState);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -4660,6 +4715,8 @@
case "forcemodemanagerquick":
pw.println(mForceModeManagerQuickDozeRequest);
break;
+ case "offbody": pw.println(mIsOffBody); break;
+ case "forceoffbody": pw.println(mForceModeManagerOffBodyState); break;
default: pw.println("Unknown get option: " + arg); break;
}
} finally {
@@ -4982,6 +5039,31 @@
pw.println("Provide true or false argument after force-modemanager-quickdoze");
return -1;
}
+ } else if ("force-modemanager-offbody".equals(cmd)) {
+ getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER,
+ null);
+ String arg = shell.getNextArg();
+
+ if ("true".equalsIgnoreCase(arg) || "false".equalsIgnoreCase(arg)) {
+ boolean isOffBody = Boolean.parseBoolean(arg);
+
+ synchronized (DeviceIdleController.this) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mForceModeManagerOffBodyState = true;
+ pw.println("mForceModeManagerOffBodyState: "
+ + mForceModeManagerOffBodyState);
+ mIsOffBody = isOffBody;
+ pw.println("mIsOffBody: " + mIsOffBody);
+ mModeManagerOffBodyStateConsumer.onModeManagerOffBodyChangedLocked();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ } else {
+ pw.println("Provide true or false argument after force-modemanager-offbody");
+ return -1;
+ }
} else {
return shell.handleDefaultCommands(cmd);
}
@@ -5233,6 +5315,12 @@
if (mAlarmsActive) {
pw.print(" mAlarmsActive="); pw.println(mAlarmsActive);
}
+ if (mConstants.USE_MODE_MANAGER) {
+ pw.print(" mModeManagerRequestedQuickDoze=");
+ pw.println(mModeManagerRequestedQuickDoze);
+ pw.print(" mIsOffBody=");
+ pw.println(mIsOffBody);
+ }
}
}
diff --git a/api/StubLibraries.bp b/api/StubLibraries.bp
index 2f84df7..8989b10 100644
--- a/api/StubLibraries.bp
+++ b/api/StubLibraries.bp
@@ -29,11 +29,14 @@
droidstubs {
name: "api-stubs-docs-non-updatable",
+ srcs: [
+ ":framework-minus-apex-aconfig-srcjars",
+ ],
defaults: [
"android-non-updatable-stubs-defaults",
"module-classpath-stubs-defaults",
],
- args: metalava_framework_docs_args + "--error UnflaggedApi ",
+ args: metalava_framework_docs_args,
check_api: {
current: {
api_file: ":non-updatable-current.txt",
@@ -74,8 +77,7 @@
"client=android.annotation.SystemApi.Client.PRIVILEGED_APPS" +
"\\)"
-test = " --show-annotation android.annotation.TestApi" +
- " --hide UnflaggedApi" // TODO(b/297362755): TestApi lint doesn't ignore existing APIs.
+test = " --show-annotation android.annotation.TestApi"
module_libs = " --show-annotation android.annotation.SystemApi\\(" +
"client=android.annotation.SystemApi.Client.MODULE_LIBRARIES" +
@@ -134,6 +136,7 @@
},
api_lint: {
enabled: true,
+ new_since: ":android.api.test.latest",
baseline_file: ":non-updatable-test-lint-baseline.txt",
},
},
diff --git a/api/api.go b/api/api.go
index 6095a9a..83804c6 100644
--- a/api/api.go
+++ b/api/api.go
@@ -433,7 +433,7 @@
}
// combined_apis bp2build converter
-func (a *CombinedApis) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+func (a *CombinedApis) ConvertWithBp2build(ctx android.Bp2buildMutatorContext) {
basePrefix := "non-updatable"
scopeToSuffix := map[string]string{
"public": "-current.txt",
diff --git a/core/api/current.txt b/core/api/current.txt
index f488c82..d48685b 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -9681,22 +9681,22 @@
public final class VirtualDevice implements android.os.Parcelable {
method public int describeContents();
method public int getDeviceId();
- method @FlaggedApi(Flags.FLAG_VDM_PUBLIC_APIS) @NonNull public int[] getDisplayIds();
+ method @FlaggedApi("android.companion.virtual.flags.vdm_public_apis") @NonNull public int[] getDisplayIds();
method @Nullable public String getName();
method @Nullable public String getPersistentDeviceId();
- method @FlaggedApi(Flags.FLAG_VDM_PUBLIC_APIS) public boolean hasCustomSensorSupport();
+ method @FlaggedApi("android.companion.virtual.flags.vdm_public_apis") public boolean hasCustomSensorSupport();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.companion.virtual.VirtualDevice> CREATOR;
}
public final class VirtualDeviceManager {
- method @FlaggedApi(Flags.FLAG_VDM_PUBLIC_APIS) @Nullable public android.companion.virtual.VirtualDevice getVirtualDevice(int);
+ method @FlaggedApi("android.companion.virtual.flags.vdm_public_apis") @Nullable public android.companion.virtual.VirtualDevice getVirtualDevice(int);
method @NonNull public java.util.List<android.companion.virtual.VirtualDevice> getVirtualDevices();
- method @FlaggedApi(Flags.FLAG_VDM_PUBLIC_APIS) public void registerVirtualDeviceListener(@NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.VirtualDeviceManager.VirtualDeviceListener);
- method @FlaggedApi(Flags.FLAG_VDM_PUBLIC_APIS) public void unregisterVirtualDeviceListener(@NonNull android.companion.virtual.VirtualDeviceManager.VirtualDeviceListener);
+ method @FlaggedApi("android.companion.virtual.flags.vdm_public_apis") public void registerVirtualDeviceListener(@NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.VirtualDeviceManager.VirtualDeviceListener);
+ method @FlaggedApi("android.companion.virtual.flags.vdm_public_apis") public void unregisterVirtualDeviceListener(@NonNull android.companion.virtual.VirtualDeviceManager.VirtualDeviceListener);
}
- @FlaggedApi(Flags.FLAG_VDM_PUBLIC_APIS) public static interface VirtualDeviceManager.VirtualDeviceListener {
+ @FlaggedApi("android.companion.virtual.flags.vdm_public_apis") public static interface VirtualDeviceManager.VirtualDeviceListener {
method public default void onVirtualDeviceClosed(int);
method public default void onVirtualDeviceCreated(int);
}
@@ -17653,13 +17653,13 @@
package android.graphics.text {
public final class LineBreakConfig {
- method @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN) public int getHyphenation();
+ method @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public int getHyphenation();
method public int getLineBreakStyle();
method public int getLineBreakWordStyle();
method @NonNull public android.graphics.text.LineBreakConfig merge(@NonNull android.graphics.text.LineBreakConfig);
- field @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN) public static final int HYPHENATION_DISABLED = 0; // 0x0
- field @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN) public static final int HYPHENATION_ENABLED = 1; // 0x1
- field @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN) public static final int HYPHENATION_UNSPECIFIED = -1; // 0xffffffff
+ field @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public static final int HYPHENATION_DISABLED = 0; // 0x0
+ field @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public static final int HYPHENATION_ENABLED = 1; // 0x1
+ field @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public static final int HYPHENATION_UNSPECIFIED = -1; // 0xffffffff
field public static final int LINE_BREAK_STYLE_LOOSE = 1; // 0x1
field public static final int LINE_BREAK_STYLE_NONE = 0; // 0x0
field public static final int LINE_BREAK_STYLE_NORMAL = 2; // 0x2
@@ -17674,7 +17674,7 @@
ctor public LineBreakConfig.Builder();
method @NonNull public android.graphics.text.LineBreakConfig build();
method @NonNull public android.graphics.text.LineBreakConfig.Builder merge(@NonNull android.graphics.text.LineBreakConfig);
- method @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN) @NonNull public android.graphics.text.LineBreakConfig.Builder setHyphenation(int);
+ method @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") @NonNull public android.graphics.text.LineBreakConfig.Builder setHyphenation(int);
method @NonNull public android.graphics.text.LineBreakConfig.Builder setLineBreakStyle(int);
method @NonNull public android.graphics.text.LineBreakConfig.Builder setLineBreakWordStyle(int);
}
@@ -23951,12 +23951,12 @@
method @Nullable public android.media.MediaRouter2.RoutingController getController(@NonNull String);
method @NonNull public java.util.List<android.media.MediaRouter2.RoutingController> getControllers();
method @NonNull public static android.media.MediaRouter2 getInstance(@NonNull android.content.Context);
- method @FlaggedApi(FLAG_ENABLE_RLP_CALLBACKS_IN_MEDIA_ROUTER2) @Nullable public android.media.RouteListingPreference getRouteListingPreference();
+ method @FlaggedApi("com.android.media.flags.enable_rlp_callbacks_in_media_router2") @Nullable public android.media.RouteListingPreference getRouteListingPreference();
method @NonNull public java.util.List<android.media.MediaRoute2Info> getRoutes();
method @NonNull public android.media.MediaRouter2.RoutingController getSystemController();
method public void registerControllerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.MediaRouter2.ControllerCallback);
method public void registerRouteCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.MediaRouter2.RouteCallback, @NonNull android.media.RouteDiscoveryPreference);
- method @FlaggedApi(FLAG_ENABLE_RLP_CALLBACKS_IN_MEDIA_ROUTER2) public void registerRouteListingPreferenceCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.MediaRouter2.RouteListingPreferenceCallback);
+ method @FlaggedApi("com.android.media.flags.enable_rlp_callbacks_in_media_router2") public void registerRouteListingPreferenceCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.MediaRouter2.RouteListingPreferenceCallback);
method public void registerTransferCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.MediaRouter2.TransferCallback);
method public void setOnGetControllerHintsListener(@Nullable android.media.MediaRouter2.OnGetControllerHintsListener);
method public void setRouteListingPreference(@Nullable android.media.RouteListingPreference);
@@ -23965,7 +23965,7 @@
method public void transferTo(@NonNull android.media.MediaRoute2Info);
method public void unregisterControllerCallback(@NonNull android.media.MediaRouter2.ControllerCallback);
method public void unregisterRouteCallback(@NonNull android.media.MediaRouter2.RouteCallback);
- method @FlaggedApi(FLAG_ENABLE_RLP_CALLBACKS_IN_MEDIA_ROUTER2) public void unregisterRouteListingPreferenceCallback(@NonNull android.media.MediaRouter2.RouteListingPreferenceCallback);
+ method @FlaggedApi("com.android.media.flags.enable_rlp_callbacks_in_media_router2") public void unregisterRouteListingPreferenceCallback(@NonNull android.media.MediaRouter2.RouteListingPreferenceCallback);
method public void unregisterTransferCallback(@NonNull android.media.MediaRouter2.TransferCallback);
}
@@ -23986,9 +23986,9 @@
method public void onRoutesUpdated(@NonNull java.util.List<android.media.MediaRoute2Info>);
}
- @FlaggedApi(FLAG_ENABLE_RLP_CALLBACKS_IN_MEDIA_ROUTER2) public abstract static class MediaRouter2.RouteListingPreferenceCallback {
- ctor @FlaggedApi(FLAG_ENABLE_RLP_CALLBACKS_IN_MEDIA_ROUTER2) public MediaRouter2.RouteListingPreferenceCallback();
- method @FlaggedApi(FLAG_ENABLE_RLP_CALLBACKS_IN_MEDIA_ROUTER2) public void onRouteListingPreferenceChanged(@Nullable android.media.RouteListingPreference);
+ @FlaggedApi("com.android.media.flags.enable_rlp_callbacks_in_media_router2") public abstract static class MediaRouter2.RouteListingPreferenceCallback {
+ ctor @FlaggedApi("com.android.media.flags.enable_rlp_callbacks_in_media_router2") public MediaRouter2.RouteListingPreferenceCallback();
+ method @FlaggedApi("com.android.media.flags.enable_rlp_callbacks_in_media_router2") public void onRouteListingPreferenceChanged(@Nullable android.media.RouteListingPreference);
}
public class MediaRouter2.RoutingController {
@@ -32285,6 +32285,7 @@
method public static long getNativeHeapFreeSize();
method public static long getNativeHeapSize();
method public static long getPss();
+ method @FlaggedApi("android.os.remove_app_profiler_pss_collection") public static long getRss();
method public static String getRuntimeStat(String);
method public static java.util.Map<java.lang.String,java.lang.String> getRuntimeStats();
method @Deprecated public static int getThreadAllocCount();
@@ -38656,10 +38657,10 @@
}
public final class FileIntegrityManager {
- method @FlaggedApi(Flags.FLAG_FSVERITY_API) @Nullable public byte[] getFsVerityDigest(@NonNull java.io.File) throws java.io.IOException;
+ method @FlaggedApi("android.security.fsverity_api") @Nullable public byte[] getFsVerityDigest(@NonNull java.io.File) throws java.io.IOException;
method public boolean isApkVeritySupported();
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.INSTALL_PACKAGES, android.Manifest.permission.REQUEST_INSTALL_PACKAGES}) public boolean isAppSourceCertificateTrusted(@NonNull java.security.cert.X509Certificate) throws java.security.cert.CertificateEncodingException;
- method @FlaggedApi(Flags.FLAG_FSVERITY_API) public void setupFsVerity(@NonNull java.io.File) throws java.io.IOException;
+ method @FlaggedApi("android.security.fsverity_api") public void setupFsVerity(@NonNull java.io.File) throws java.io.IOException;
}
public final class KeyChain {
@@ -41578,7 +41579,7 @@
field public static final int PROPERTY_HIGH_DEF_AUDIO = 16; // 0x10
field public static final int PROPERTY_IS_ADHOC_CONFERENCE = 8192; // 0x2000
field public static final int PROPERTY_IS_EXTERNAL_CALL = 64; // 0x40
- field @FlaggedApi(Flags.FLAG_VOIP_APP_ACTIONS_SUPPORT) public static final int PROPERTY_IS_TRANSACTIONAL = 32768; // 0x8000
+ field @FlaggedApi("com.android.server.telecom.flags.voip_app_actions_support") public static final int PROPERTY_IS_TRANSACTIONAL = 32768; // 0x8000
field public static final int PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL = 2048; // 0x800
field public static final int PROPERTY_RTT = 1024; // 0x400
field public static final int PROPERTY_SELF_MANAGED = 256; // 0x100
@@ -46614,7 +46615,7 @@
public static class BoringLayout.Metrics extends android.graphics.Paint.FontMetricsInt {
ctor public BoringLayout.Metrics();
- method @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) @NonNull public android.graphics.RectF getDrawingBoundingBox();
+ method @FlaggedApi("com.android.text.flags.use_bounds_for_width") @NonNull public android.graphics.RectF getDrawingBoundingBox();
field public int width;
}
@@ -46799,7 +46800,7 @@
public abstract class Layout {
ctor protected Layout(CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float);
- method @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) @NonNull public android.graphics.RectF computeDrawingBoundingBox();
+ method @FlaggedApi("com.android.text.flags.use_bounds_for_width") @NonNull public android.graphics.RectF computeDrawingBoundingBox();
method public void draw(android.graphics.Canvas);
method public void draw(android.graphics.Canvas, android.graphics.Path, android.graphics.Paint, int);
method public void draw(@NonNull android.graphics.Canvas, @Nullable java.util.List<android.graphics.Path>, @Nullable java.util.List<android.graphics.Paint>, @Nullable android.graphics.Path, @Nullable android.graphics.Paint, int);
@@ -46808,24 +46809,24 @@
method public void fillCharacterBounds(@IntRange(from=0) int, @IntRange(from=0) int, @NonNull float[], @IntRange(from=0) int);
method @NonNull public final android.text.Layout.Alignment getAlignment();
method public abstract int getBottomPadding();
- method @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) public final int getBreakStrategy();
+ method @FlaggedApi("com.android.text.flags.use_bounds_for_width") public final int getBreakStrategy();
method public void getCursorPath(int, android.graphics.Path, CharSequence);
method public static float getDesiredWidth(CharSequence, android.text.TextPaint);
method public static float getDesiredWidth(CharSequence, int, int, android.text.TextPaint);
method public abstract int getEllipsisCount(int);
method public abstract int getEllipsisStart(int);
- method @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) @Nullable public final android.text.TextUtils.TruncateAt getEllipsize();
+ method @FlaggedApi("com.android.text.flags.use_bounds_for_width") @Nullable public final android.text.TextUtils.TruncateAt getEllipsize();
method @IntRange(from=0) public int getEllipsizedWidth();
method public int getHeight();
- method @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) public final int getHyphenationFrequency();
- method @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) public final int getJustificationMode();
- method @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) @Nullable public final int[] getLeftIndents();
+ method @FlaggedApi("com.android.text.flags.use_bounds_for_width") public final int getHyphenationFrequency();
+ method @FlaggedApi("com.android.text.flags.use_bounds_for_width") public final int getJustificationMode();
+ method @FlaggedApi("com.android.text.flags.use_bounds_for_width") @Nullable public final int[] getLeftIndents();
method public final int getLineAscent(int);
method public final int getLineBaseline(int);
method public final int getLineBottom(int);
method public int getLineBottom(int, boolean);
method public int getLineBounds(int, android.graphics.Rect);
- method @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) @NonNull public android.graphics.text.LineBreakConfig getLineBreakConfig();
+ method @FlaggedApi("com.android.text.flags.use_bounds_for_width") @NonNull public android.graphics.text.LineBreakConfig getLineBreakConfig();
method public abstract boolean getLineContainsTab(int);
method public abstract int getLineCount();
method public abstract int getLineDescent(int);
@@ -46836,13 +46837,13 @@
method public float getLineLeft(int);
method public float getLineMax(int);
method public float getLineRight(int);
- method @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) public final float getLineSpacingAmount();
- method @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) public final float getLineSpacingMultiplier();
+ method @FlaggedApi("com.android.text.flags.use_bounds_for_width") public final float getLineSpacingAmount();
+ method @FlaggedApi("com.android.text.flags.use_bounds_for_width") public final float getLineSpacingMultiplier();
method public abstract int getLineStart(int);
method public abstract int getLineTop(int);
method public int getLineVisibleEnd(int);
method public float getLineWidth(int);
- method @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) @IntRange(from=1) public final int getMaxLines();
+ method @FlaggedApi("com.android.text.flags.use_bounds_for_width") @IntRange(from=1) public final int getMaxLines();
method public int getOffsetForHorizontal(int, float);
method public int getOffsetToLeftOf(int);
method public int getOffsetToRightOf(int);
@@ -46853,19 +46854,19 @@
method public final int getParagraphRight(int);
method public float getPrimaryHorizontal(int);
method @Nullable public int[] getRangeForRect(@NonNull android.graphics.RectF, @NonNull android.text.SegmentFinder, @NonNull android.text.Layout.TextInclusionStrategy);
- method @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) @Nullable public final int[] getRightIndents();
+ method @FlaggedApi("com.android.text.flags.use_bounds_for_width") @Nullable public final int[] getRightIndents();
method public float getSecondaryHorizontal(int);
method public void getSelectionPath(int, int, android.graphics.Path);
method public final float getSpacingAdd();
method public final float getSpacingMultiplier();
- method @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) @NonNull public final CharSequence getText();
- method @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) @NonNull public final android.text.TextDirectionHeuristic getTextDirectionHeuristic();
+ method @FlaggedApi("com.android.text.flags.use_bounds_for_width") @NonNull public final CharSequence getText();
+ method @FlaggedApi("com.android.text.flags.use_bounds_for_width") @NonNull public final android.text.TextDirectionHeuristic getTextDirectionHeuristic();
method public abstract int getTopPadding();
- method @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) public boolean getUseBoundsForWidth();
+ method @FlaggedApi("com.android.text.flags.use_bounds_for_width") public boolean getUseBoundsForWidth();
method @IntRange(from=0) public final int getWidth();
method public final void increaseWidthTo(int);
method public boolean isFallbackLineSpacingEnabled();
- method @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) public final boolean isFontPaddingIncluded();
+ method @FlaggedApi("com.android.text.flags.use_bounds_for_width") public final boolean isFontPaddingIncluded();
method public boolean isRtlCharAt(int);
method protected final boolean isSpanned();
field public static final int BREAK_STRATEGY_BALANCED = 2; // 0x2
@@ -46893,7 +46894,7 @@
enum_constant public static final android.text.Layout.Alignment ALIGN_OPPOSITE;
}
- @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) public static final class Layout.Builder {
+ @FlaggedApi("com.android.text.flags.use_bounds_for_width") public static final class Layout.Builder {
ctor public Layout.Builder(@NonNull CharSequence, @IntRange(from=0) int, @IntRange(from=0) int, @NonNull android.text.TextPaint, @IntRange(from=0) int);
method @NonNull public android.text.Layout build();
method @NonNull public android.text.Layout.Builder setAlignment(@NonNull android.text.Layout.Alignment);
@@ -46911,7 +46912,7 @@
method @NonNull public android.text.Layout.Builder setMaxLines(@IntRange(from=1) int);
method @NonNull public android.text.Layout.Builder setRightIndents(@Nullable int[]);
method @NonNull public android.text.Layout.Builder setTextDirectionHeuristic(@NonNull android.text.TextDirectionHeuristic);
- method @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) @NonNull public android.text.Layout.Builder setUseBoundsForWidth(boolean);
+ method @FlaggedApi("com.android.text.flags.use_bounds_for_width") @NonNull public android.text.Layout.Builder setUseBoundsForWidth(boolean);
}
public static class Layout.Directions {
@@ -47888,13 +47889,13 @@
method public void writeToParcel(@NonNull android.os.Parcel, int);
}
- @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN) public class LineBreakConfigSpan {
+ @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public class LineBreakConfigSpan {
ctor public LineBreakConfigSpan(@NonNull android.graphics.text.LineBreakConfig);
method @NonNull public android.graphics.text.LineBreakConfig getLineBreakConfig();
}
- @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN) public static final class LineBreakConfigSpan.NoHyphenationSpan extends android.text.style.LineBreakConfigSpan {
- ctor @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN) public LineBreakConfigSpan.NoHyphenationSpan();
+ @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public static final class LineBreakConfigSpan.NoHyphenationSpan extends android.text.style.LineBreakConfigSpan {
+ ctor @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public LineBreakConfigSpan.NoHyphenationSpan();
}
public interface LineHeightSpan extends android.text.style.ParagraphStyle android.text.style.WrapTogetherSpan {
@@ -50057,7 +50058,7 @@
field public static final int VIRTUAL_KEY_RELEASE = 8; // 0x8
}
- @FlaggedApi(Flags.FLAG_SCROLL_FEEDBACK_API) public class HapticScrollFeedbackProvider implements android.view.ScrollFeedbackProvider {
+ @FlaggedApi("android.view.flags.scroll_feedback_api") public class HapticScrollFeedbackProvider implements android.view.ScrollFeedbackProvider {
ctor public HapticScrollFeedbackProvider(@NonNull android.view.View);
method public void onScrollLimit(int, int, int, boolean);
method public void onScrollProgress(int, int, int, int);
@@ -51229,7 +51230,7 @@
method @UiThread public void updatePositionInWindow();
}
- @FlaggedApi(Flags.FLAG_SCROLL_FEEDBACK_API) public interface ScrollFeedbackProvider {
+ @FlaggedApi("android.view.flags.scroll_feedback_api") public interface ScrollFeedbackProvider {
method public void onScrollLimit(int, int, int, boolean);
method public void onScrollProgress(int, int, int, int);
method public void onSnapToItem(int, int, int);
@@ -52512,7 +52513,7 @@
method @Deprecated public static int getEdgeSlop();
method @Deprecated public static int getFadingEdgeLength();
method @Deprecated public static long getGlobalActionKeyTimeout();
- method @FlaggedApi(Flags.FLAG_SCROLL_FEEDBACK_API) public int getHapticScrollFeedbackTickInterval(int, int, int);
+ method @FlaggedApi("android.view.flags.scroll_feedback_api") public int getHapticScrollFeedbackTickInterval(int, int, int);
method public static int getJumpTapTimeout();
method public static int getKeyRepeatDelay();
method public static int getKeyRepeatTimeout();
@@ -52552,7 +52553,7 @@
method @Deprecated public static int getWindowTouchSlop();
method public static long getZoomControlsTimeout();
method public boolean hasPermanentMenuKey();
- method @FlaggedApi(Flags.FLAG_SCROLL_FEEDBACK_API) public boolean isHapticScrollFeedbackEnabled(int, int, int);
+ method @FlaggedApi("android.view.flags.scroll_feedback_api") public boolean isHapticScrollFeedbackEnabled(int, int, int);
method public boolean shouldShowMenuShortcutsWhenKeyboardPresent();
}
@@ -59885,7 +59886,7 @@
method public final android.text.method.TransformationMethod getTransformationMethod();
method public android.graphics.Typeface getTypeface();
method public android.text.style.URLSpan[] getUrls();
- method @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) public boolean getUseBoundsForWidth();
+ method @FlaggedApi("com.android.text.flags.use_bounds_for_width") public boolean getUseBoundsForWidth();
method public boolean hasSelection();
method public boolean isAllCaps();
method public boolean isCursorVisible();
@@ -60028,7 +60029,7 @@
method public final void setTransformationMethod(android.text.method.TransformationMethod);
method public void setTypeface(@Nullable android.graphics.Typeface, int);
method public void setTypeface(@Nullable android.graphics.Typeface);
- method @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) public void setUseBoundsForWidth(boolean);
+ method @FlaggedApi("com.android.text.flags.use_bounds_for_width") public void setUseBoundsForWidth(boolean);
method public void setWidth(int);
field public static final int AUTO_SIZE_TEXT_TYPE_NONE = 0; // 0x0
field public static final int AUTO_SIZE_TEXT_TYPE_UNIFORM = 1; // 0x1
diff --git a/core/api/lint-baseline.txt b/core/api/lint-baseline.txt
index 6b7910a..afb10f5 100644
--- a/core/api/lint-baseline.txt
+++ b/core/api/lint-baseline.txt
@@ -265,8 +265,6 @@
New API must be flagged with @FlaggedApi: method android.database.sqlite.SQLiteRawStatement.reset()
UnflaggedApi: android.database.sqlite.SQLiteRawStatement#step():
New API must be flagged with @FlaggedApi: method android.database.sqlite.SQLiteRawStatement.step()
-UnflaggedApi: android.database.sqlite.SQLiteRawStatement#toString():
- New API must be flagged with @FlaggedApi: method android.database.sqlite.SQLiteRawStatement.toString()
UnflaggedApi: android.graphics.Gainmap#Gainmap(android.graphics.Gainmap, android.graphics.Bitmap):
New API must be flagged with @FlaggedApi: constructor android.graphics.Gainmap(android.graphics.Gainmap,android.graphics.Bitmap)
UnflaggedApi: android.graphics.Path#computeBounds(android.graphics.RectF):
@@ -321,8 +319,6 @@
New API must be flagged with @FlaggedApi: method android.media.midi.MidiUmpDeviceService.onBind(android.content.Intent)
UnflaggedApi: android.media.midi.MidiUmpDeviceService#onClose():
New API must be flagged with @FlaggedApi: method android.media.midi.MidiUmpDeviceService.onClose()
-UnflaggedApi: android.media.midi.MidiUmpDeviceService#onCreate():
- New API must be flagged with @FlaggedApi: method android.media.midi.MidiUmpDeviceService.onCreate()
UnflaggedApi: android.media.midi.MidiUmpDeviceService#onDeviceStatusChanged(android.media.midi.MidiDeviceStatus):
New API must be flagged with @FlaggedApi: method android.media.midi.MidiUmpDeviceService.onDeviceStatusChanged(android.media.midi.MidiDeviceStatus)
UnflaggedApi: android.media.midi.MidiUmpDeviceService#onGetInputPortReceivers():
@@ -335,8 +331,6 @@
New API must be flagged with @FlaggedApi: field android.os.UserManager.DISALLOW_NEAR_FIELD_COMMUNICATION_RADIO
UnflaggedApi: android.provider.Settings#ACTION_CREDENTIAL_PROVIDER:
New API must be flagged with @FlaggedApi: field android.provider.Settings.ACTION_CREDENTIAL_PROVIDER
-UnflaggedApi: android.telecom.Call.Details#PROPERTY_IS_TRANSACTIONAL:
- New API must be flagged with @FlaggedApi: field android.telecom.Call.Details.PROPERTY_IS_TRANSACTIONAL
UnflaggedApi: android.telecom.Call.Details#getId():
New API must be flagged with @FlaggedApi: method android.telecom.Call.Details.getId()
UnflaggedApi: android.telephony.CarrierConfigManager#KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE:
@@ -355,46 +349,10 @@
New API must be flagged with @FlaggedApi: field android.telephony.TelephonyManager.EVENT_DISPLAY_SOS_MESSAGE
UnflaggedApi: android.telephony.TelephonyManager#PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_DISABLED:
New API must be flagged with @FlaggedApi: field android.telephony.TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_DISABLED
-UnflaggedApi: android.text.BoringLayout#computeDrawingBoundingBox():
- New API must be flagged with @FlaggedApi: method android.text.BoringLayout.computeDrawingBoundingBox()
-UnflaggedApi: android.text.BoringLayout.Metrics#getDrawingBoundingBox():
- New API must be flagged with @FlaggedApi: method android.text.BoringLayout.Metrics.getDrawingBoundingBox()
-UnflaggedApi: android.text.DynamicLayout#getLineBreakConfig():
- New API must be flagged with @FlaggedApi: method android.text.DynamicLayout.getLineBreakConfig()
UnflaggedApi: android.text.DynamicLayout.Builder#setLineBreakConfig(android.graphics.text.LineBreakConfig):
New API must be flagged with @FlaggedApi: method android.text.DynamicLayout.Builder.setLineBreakConfig(android.graphics.text.LineBreakConfig)
UnflaggedApi: android.text.DynamicLayout.Builder#setUseBoundsForWidth(boolean):
New API must be flagged with @FlaggedApi: method android.text.DynamicLayout.Builder.setUseBoundsForWidth(boolean)
-UnflaggedApi: android.text.Layout#computeDrawingBoundingBox():
- New API must be flagged with @FlaggedApi: method android.text.Layout.computeDrawingBoundingBox()
-UnflaggedApi: android.text.Layout#getBreakStrategy():
- New API must be flagged with @FlaggedApi: method android.text.Layout.getBreakStrategy()
-UnflaggedApi: android.text.Layout#getEllipsize():
- New API must be flagged with @FlaggedApi: method android.text.Layout.getEllipsize()
-UnflaggedApi: android.text.Layout#getHyphenationFrequency():
- New API must be flagged with @FlaggedApi: method android.text.Layout.getHyphenationFrequency()
-UnflaggedApi: android.text.Layout#getJustificationMode():
- New API must be flagged with @FlaggedApi: method android.text.Layout.getJustificationMode()
-UnflaggedApi: android.text.Layout#getLeftIndents():
- New API must be flagged with @FlaggedApi: method android.text.Layout.getLeftIndents()
-UnflaggedApi: android.text.Layout#getLineBreakConfig():
- New API must be flagged with @FlaggedApi: method android.text.Layout.getLineBreakConfig()
-UnflaggedApi: android.text.Layout#getLineSpacingAmount():
- New API must be flagged with @FlaggedApi: method android.text.Layout.getLineSpacingAmount()
-UnflaggedApi: android.text.Layout#getLineSpacingMultiplier():
- New API must be flagged with @FlaggedApi: method android.text.Layout.getLineSpacingMultiplier()
-UnflaggedApi: android.text.Layout#getMaxLines():
- New API must be flagged with @FlaggedApi: method android.text.Layout.getMaxLines()
-UnflaggedApi: android.text.Layout#getRightIndents():
- New API must be flagged with @FlaggedApi: method android.text.Layout.getRightIndents()
-UnflaggedApi: android.text.Layout#getTextDirectionHeuristic():
- New API must be flagged with @FlaggedApi: method android.text.Layout.getTextDirectionHeuristic()
-UnflaggedApi: android.text.Layout#getUseBoundsForWidth():
- New API must be flagged with @FlaggedApi: method android.text.Layout.getUseBoundsForWidth()
-UnflaggedApi: android.text.Layout#isFontPaddingIncluded():
- New API must be flagged with @FlaggedApi: method android.text.Layout.isFontPaddingIncluded()
-UnflaggedApi: android.text.Layout.Builder:
- New API must be flagged with @FlaggedApi: class android.text.Layout.Builder
UnflaggedApi: android.text.Layout.Builder#Builder(CharSequence, int, int, android.text.TextPaint, int):
New API must be flagged with @FlaggedApi: constructor android.text.Layout.Builder(CharSequence,int,int,android.text.TextPaint,int)
UnflaggedApi: android.text.Layout.Builder#build():
@@ -429,24 +387,12 @@
New API must be flagged with @FlaggedApi: method android.text.Layout.Builder.setRightIndents(int[])
UnflaggedApi: android.text.Layout.Builder#setTextDirectionHeuristic(android.text.TextDirectionHeuristic):
New API must be flagged with @FlaggedApi: method android.text.Layout.Builder.setTextDirectionHeuristic(android.text.TextDirectionHeuristic)
-UnflaggedApi: android.text.Layout.Builder#setUseBoundsForWidth(boolean):
- New API must be flagged with @FlaggedApi: method android.text.Layout.Builder.setUseBoundsForWidth(boolean)
-UnflaggedApi: android.text.StaticLayout#computeDrawingBoundingBox():
- New API must be flagged with @FlaggedApi: method android.text.StaticLayout.computeDrawingBoundingBox()
UnflaggedApi: android.text.StaticLayout.Builder#setUseBoundsForWidth(boolean):
New API must be flagged with @FlaggedApi: method android.text.StaticLayout.Builder.setUseBoundsForWidth(boolean)
-UnflaggedApi: android.text.style.LineBreakConfigSpan:
- New API must be flagged with @FlaggedApi: class android.text.style.LineBreakConfigSpan
UnflaggedApi: android.text.style.LineBreakConfigSpan#LineBreakConfigSpan(android.graphics.text.LineBreakConfig):
New API must be flagged with @FlaggedApi: constructor android.text.style.LineBreakConfigSpan(android.graphics.text.LineBreakConfig)
-UnflaggedApi: android.text.style.LineBreakConfigSpan#equals(Object):
- New API must be flagged with @FlaggedApi: method android.text.style.LineBreakConfigSpan.equals(Object)
UnflaggedApi: android.text.style.LineBreakConfigSpan#getLineBreakConfig():
New API must be flagged with @FlaggedApi: method android.text.style.LineBreakConfigSpan.getLineBreakConfig()
-UnflaggedApi: android.text.style.LineBreakConfigSpan#hashCode():
- New API must be flagged with @FlaggedApi: method android.text.style.LineBreakConfigSpan.hashCode()
-UnflaggedApi: android.text.style.LineBreakConfigSpan#toString():
- New API must be flagged with @FlaggedApi: method android.text.style.LineBreakConfigSpan.toString()
UnflaggedApi: android.view.HapticScrollFeedbackProvider#HapticScrollFeedbackProvider(android.view.View):
New API must be flagged with @FlaggedApi: constructor android.view.HapticScrollFeedbackProvider(android.view.View)
UnflaggedApi: android.view.HapticScrollFeedbackProvider#onScrollLimit(int, int, int, boolean):
@@ -455,16 +401,10 @@
New API must be flagged with @FlaggedApi: method android.view.HapticScrollFeedbackProvider.onScrollProgress(int,int,int,int)
UnflaggedApi: android.view.HapticScrollFeedbackProvider#onSnapToItem(int, int, int):
New API must be flagged with @FlaggedApi: method android.view.HapticScrollFeedbackProvider.onSnapToItem(int,int,int)
-UnflaggedApi: android.view.ScrollFeedbackProvider#onScrollLimit(android.view.MotionEvent, int, boolean):
- New API must be flagged with @FlaggedApi: method android.view.ScrollFeedbackProvider.onScrollLimit(android.view.MotionEvent,int,boolean)
UnflaggedApi: android.view.ScrollFeedbackProvider#onScrollLimit(int, int, int, boolean):
New API must be flagged with @FlaggedApi: method android.view.ScrollFeedbackProvider.onScrollLimit(int,int,int,boolean)
-UnflaggedApi: android.view.ScrollFeedbackProvider#onScrollProgress(android.view.MotionEvent, int, int):
- New API must be flagged with @FlaggedApi: method android.view.ScrollFeedbackProvider.onScrollProgress(android.view.MotionEvent,int,int)
UnflaggedApi: android.view.ScrollFeedbackProvider#onScrollProgress(int, int, int, int):
New API must be flagged with @FlaggedApi: method android.view.ScrollFeedbackProvider.onScrollProgress(int,int,int,int)
-UnflaggedApi: android.view.ScrollFeedbackProvider#onSnapToItem(android.view.MotionEvent, int):
- New API must be flagged with @FlaggedApi: method android.view.ScrollFeedbackProvider.onSnapToItem(android.view.MotionEvent,int)
UnflaggedApi: android.view.ScrollFeedbackProvider#onSnapToItem(int, int, int):
New API must be flagged with @FlaggedApi: method android.view.ScrollFeedbackProvider.onSnapToItem(int,int,int)
UnflaggedApi: android.view.accessibility.AccessibilityNodeInfo#ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT:
@@ -515,7 +455,3 @@
New API must be flagged with @FlaggedApi: method android.view.inputmethod.InlineSuggestionsRequest.Builder.setClientSupported(boolean)
UnflaggedApi: android.view.inputmethod.InlineSuggestionsRequest.Builder#setServiceSupported(boolean):
New API must be flagged with @FlaggedApi: method android.view.inputmethod.InlineSuggestionsRequest.Builder.setServiceSupported(boolean)
-UnflaggedApi: android.widget.TextView#getUseBoundsForWidth():
- New API must be flagged with @FlaggedApi: method android.widget.TextView.getUseBoundsForWidth()
-UnflaggedApi: android.widget.TextView#setUseBoundsForWidth(boolean):
- New API must be flagged with @FlaggedApi: method android.widget.TextView.setUseBoundsForWidth(boolean)
diff --git a/core/api/module-lib-lint-baseline.txt b/core/api/module-lib-lint-baseline.txt
index a0d3858..1633835 100644
--- a/core/api/module-lib-lint-baseline.txt
+++ b/core/api/module-lib-lint-baseline.txt
@@ -35,26 +35,8 @@
SAM-compatible parameters (such as parameter 1, "listener", in android.media.session.MediaSessionManager.setOnMediaKeyListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
SamShouldBeLast: android.media.session.MediaSessionManager#setOnVolumeKeyLongPressListener(android.media.session.MediaSessionManager.OnVolumeKeyLongPressListener, android.os.Handler):
SAM-compatible parameters (such as parameter 1, "listener", in android.media.session.MediaSessionManager.setOnVolumeKeyLongPressListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.os.Binder#attachInterface(android.os.IInterface, String):
- SAM-compatible parameters (such as parameter 1, "owner", in android.os.Binder.attachInterface) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.os.Binder#linkToDeath(android.os.IBinder.DeathRecipient, int):
- SAM-compatible parameters (such as parameter 1, "recipient", in android.os.Binder.linkToDeath) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.os.Binder#unlinkToDeath(android.os.IBinder.DeathRecipient, int):
- SAM-compatible parameters (such as parameter 1, "recipient", in android.os.Binder.unlinkToDeath) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.os.IBinder#linkToDeath(android.os.IBinder.DeathRecipient, int):
- SAM-compatible parameters (such as parameter 1, "recipient", in android.os.IBinder.linkToDeath) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.os.IBinder#unlinkToDeath(android.os.IBinder.DeathRecipient, int):
- SAM-compatible parameters (such as parameter 1, "recipient", in android.os.IBinder.unlinkToDeath) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-UnflaggedApi: android.Manifest.permission#BLUETOOTH_STACK:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BLUETOOTH_STACK
-UnflaggedApi: android.Manifest.permission#CONTROL_AUTOMOTIVE_GNSS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.CONTROL_AUTOMOTIVE_GNSS
-UnflaggedApi: android.Manifest.permission#GET_INTENT_SENDER_INTENT:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.GET_INTENT_SENDER_INTENT
-UnflaggedApi: android.Manifest.permission#MAKE_UID_VISIBLE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MAKE_UID_VISIBLE
UnflaggedApi: android.Manifest.permission#MANAGE_REMOTE_AUTH:
New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_REMOTE_AUTH
UnflaggedApi: android.Manifest.permission#USE_COMPANION_TRANSPORTS:
@@ -89,13 +71,7 @@
New API must be flagged with @FlaggedApi: method android.companion.CompanionDeviceManager.OnTransportsChangedListener.onTransportsChanged(java.util.List<android.companion.AssociationInfo>)
UnflaggedApi: android.content.Context#REMOTE_AUTH_SERVICE:
New API must be flagged with @FlaggedApi: field android.content.Context.REMOTE_AUTH_SERVICE
-UnflaggedApi: android.content.ContextWrapper#createContextForSdkInSandbox(android.content.pm.ApplicationInfo, int):
- New API must be flagged with @FlaggedApi: method android.content.ContextWrapper.createContextForSdkInSandbox(android.content.pm.ApplicationInfo,int)
-UnflaggedApi: android.media.session.MediaController.PlaybackInfo#PlaybackInfo(int, int, int, int, android.media.AudioAttributes, String):
- New API must be flagged with @FlaggedApi: constructor android.media.session.MediaController.PlaybackInfo(int,int,int,int,android.media.AudioAttributes,String)
UnflaggedApi: android.os.IpcDataCache#MODULE_TELEPHONY:
New API must be flagged with @FlaggedApi: field android.os.IpcDataCache.MODULE_TELEPHONY
-UnflaggedApi: android.provider.ContactsContract.RawContactsEntity#queryRawContactEntity(android.content.ContentResolver, long):
- New API must be flagged with @FlaggedApi: method android.provider.ContactsContract.RawContactsEntity.queryRawContactEntity(android.content.ContentResolver,long)
UnflaggedApi: android.provider.Settings.Config#getAllStrings():
New API must be flagged with @FlaggedApi: method android.provider.Settings.Config.getAllStrings()
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index e49ed18..5c48b21 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -3800,10 +3800,6 @@
field @RequiresPermission(android.Manifest.permission.ACCESS_SHORTCUTS) public static final int FLAG_GET_PERSONS_DATA = 2048; // 0x800
}
- public class PackageInfo implements android.os.Parcelable {
- field public boolean isArchived;
- }
-
public class PackageInstaller {
method @NonNull public android.content.pm.PackageInstaller.InstallInfo readInstallInfo(@NonNull java.io.File, int) throws android.content.pm.PackageInstaller.PackageParsingException;
method @NonNull public android.content.pm.PackageInstaller.InstallInfo readInstallInfo(@NonNull android.os.ParcelFileDescriptor, @Nullable String, int) throws android.content.pm.PackageInstaller.PackageParsingException;
@@ -3881,6 +3877,7 @@
method public static void forceSafeLabels();
method @Deprecated @NonNull public CharSequence loadSafeLabel(@NonNull android.content.pm.PackageManager);
method @NonNull public CharSequence loadSafeLabel(@NonNull android.content.pm.PackageManager, @FloatRange(from=0) float, int);
+ field @FlaggedApi(Flags.FLAG_ARCHIVING) public boolean isArchived;
}
public abstract class PackageManager {
diff --git a/core/api/system-lint-baseline.txt b/core/api/system-lint-baseline.txt
index d62bea8..ee031db 100644
--- a/core/api/system-lint-baseline.txt
+++ b/core/api/system-lint-baseline.txt
@@ -21,28 +21,10 @@
Listeners should always be at end of argument list (method `stopSatelliteTransmissionUpdates`)
-MissingGetterMatchingBuilder: android.telecom.CallScreeningService.CallResponse.Builder#setShouldScreenCallViaAudioProcessing(boolean):
- android.telecom.CallScreeningService.CallResponse does not declare a `shouldScreenCallViaAudioProcessing()` method matching method android.telecom.CallScreeningService.CallResponse.Builder.setShouldScreenCallViaAudioProcessing(boolean)
-MissingGetterMatchingBuilder: android.telephony.mbms.DownloadRequest.Builder#setServiceId(String):
- android.telephony.mbms.DownloadRequest does not declare a `getServiceId()` method matching method android.telephony.mbms.DownloadRequest.Builder.setServiceId(String)
-
-
MissingNullability: android.media.soundtrigger.SoundTriggerDetectionService#onUnbind(android.content.Intent) parameter #0:
Missing nullability on parameter `intent` in method `onUnbind`
-MissingNullability: android.media.tv.TvRecordingClient.RecordingCallback#onEvent(String, String, android.os.Bundle) parameter #0:
- Missing nullability on parameter `inputId` in method `onEvent`
-MissingNullability: android.media.tv.TvRecordingClient.RecordingCallback#onEvent(String, String, android.os.Bundle) parameter #1:
- Missing nullability on parameter `eventType` in method `onEvent`
-MissingNullability: android.media.tv.TvRecordingClient.RecordingCallback#onEvent(String, String, android.os.Bundle) parameter #2:
- Missing nullability on parameter `eventArgs` in method `onEvent`
MissingNullability: android.printservice.recommendation.RecommendationService#attachBaseContext(android.content.Context) parameter #0:
Missing nullability on parameter `base` in method `attachBaseContext`
-MissingNullability: android.provider.ContactsContract.MetadataSync#CONTENT_URI:
- Missing nullability on field `CONTENT_URI` in class `class android.provider.ContactsContract.MetadataSync`
-MissingNullability: android.provider.ContactsContract.MetadataSync#METADATA_AUTHORITY_URI:
- Missing nullability on field `METADATA_AUTHORITY_URI` in class `class android.provider.ContactsContract.MetadataSync`
-MissingNullability: android.provider.ContactsContract.MetadataSyncState#CONTENT_URI:
- Missing nullability on field `CONTENT_URI` in class `class android.provider.ContactsContract.MetadataSyncState`
MissingNullability: android.provider.SearchIndexablesProvider#attachInfo(android.content.Context, android.content.pm.ProviderInfo) parameter #0:
Missing nullability on parameter `context` in method `attachInfo`
MissingNullability: android.provider.SearchIndexablesProvider#attachInfo(android.content.Context, android.content.pm.ProviderInfo) parameter #1:
@@ -61,10 +43,6 @@
Missing nullability on parameter `intent` in method `onUnbind`
MissingNullability: android.telephony.data.DataService#onUnbind(android.content.Intent) parameter #0:
Missing nullability on parameter `intent` in method `onUnbind`
-MissingNullability: android.telephony.mbms.DownloadRequest.Builder#setServiceId(String):
- Missing nullability on method `setServiceId` return
-MissingNullability: android.telephony.mbms.DownloadRequest.Builder#setServiceId(String) parameter #0:
- Missing nullability on parameter `serviceId` in method `setServiceId`
ProtectedMember: android.printservice.recommendation.RecommendationService#attachBaseContext(android.content.Context):
@@ -173,12 +151,6 @@
SAM-compatible parameters (such as parameter 2, "callback", in android.nfc.NfcAdapter.enableReaderMode) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
SamShouldBeLast: android.nfc.NfcAdapter#ignore(android.nfc.Tag, int, android.nfc.NfcAdapter.OnTagRemovedListener, android.os.Handler):
SAM-compatible parameters (such as parameter 3, "tagRemovedListener", in android.nfc.NfcAdapter.ignore) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.nfc.NfcAdapter#setBeamPushUrisCallback(android.nfc.NfcAdapter.CreateBeamUrisCallback, android.app.Activity):
- SAM-compatible parameters (such as parameter 1, "callback", in android.nfc.NfcAdapter.setBeamPushUrisCallback) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.nfc.NfcAdapter#setNdefPushMessageCallback(android.nfc.NfcAdapter.CreateNdefMessageCallback, android.app.Activity, android.app.Activity...):
- SAM-compatible parameters (such as parameter 1, "callback", in android.nfc.NfcAdapter.setNdefPushMessageCallback) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.nfc.NfcAdapter#setOnNdefPushCompleteCallback(android.nfc.NfcAdapter.OnNdefPushCompleteCallback, android.app.Activity, android.app.Activity...):
- SAM-compatible parameters (such as parameter 1, "callback", in android.nfc.NfcAdapter.setOnNdefPushCompleteCallback) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
SamShouldBeLast: android.os.Binder#attachInterface(android.os.IInterface, String):
SAM-compatible parameters (such as parameter 1, "owner", in android.os.Binder.attachInterface) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
SamShouldBeLast: android.os.Binder#linkToDeath(android.os.IBinder.DeathRecipient, int):
@@ -225,1350 +197,32 @@
SAM-compatible parameters (such as parameter 2, "filePathCallback", in android.webkit.WebChromeClient.onShowFileChooser) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-UnflaggedApi: android.Manifest.permission#ACCESS_AMBIENT_CONTEXT_EVENT:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT
-UnflaggedApi: android.Manifest.permission#ACCESS_AMBIENT_LIGHT_STATS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_AMBIENT_LIGHT_STATS
-UnflaggedApi: android.Manifest.permission#ACCESS_BROADCAST_RADIO:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_BROADCAST_RADIO
-UnflaggedApi: android.Manifest.permission#ACCESS_BROADCAST_RESPONSE_STATS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_BROADCAST_RESPONSE_STATS
-UnflaggedApi: android.Manifest.permission#ACCESS_CACHE_FILESYSTEM:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_CACHE_FILESYSTEM
-UnflaggedApi: android.Manifest.permission#ACCESS_CONTEXT_HUB:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_CONTEXT_HUB
-UnflaggedApi: android.Manifest.permission#ACCESS_DRM_CERTIFICATES:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_DRM_CERTIFICATES
-UnflaggedApi: android.Manifest.permission#ACCESS_FPS_COUNTER:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_FPS_COUNTER
-UnflaggedApi: android.Manifest.permission#ACCESS_INSTANT_APPS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_INSTANT_APPS
-UnflaggedApi: android.Manifest.permission#ACCESS_LOCUS_ID_USAGE_STATS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_LOCUS_ID_USAGE_STATS
-UnflaggedApi: android.Manifest.permission#ACCESS_MOCK_LOCATION:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_MOCK_LOCATION
-UnflaggedApi: android.Manifest.permission#ACCESS_MTP:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_MTP
-UnflaggedApi: android.Manifest.permission#ACCESS_NETWORK_CONDITIONS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_NETWORK_CONDITIONS
-UnflaggedApi: android.Manifest.permission#ACCESS_NOTIFICATIONS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_NOTIFICATIONS
-UnflaggedApi: android.Manifest.permission#ACCESS_PDB_STATE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_PDB_STATE
-UnflaggedApi: android.Manifest.permission#ACCESS_RCS_USER_CAPABILITY_EXCHANGE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE
-UnflaggedApi: android.Manifest.permission#ACCESS_SHARED_LIBRARIES:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_SHARED_LIBRARIES
-UnflaggedApi: android.Manifest.permission#ACCESS_SHORTCUTS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_SHORTCUTS
UnflaggedApi: android.Manifest.permission#ACCESS_SMARTSPACE:
New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_SMARTSPACE
-UnflaggedApi: android.Manifest.permission#ACCESS_SURFACE_FLINGER:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_SURFACE_FLINGER
-UnflaggedApi: android.Manifest.permission#ACCESS_TUNED_INFO:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_TUNED_INFO
-UnflaggedApi: android.Manifest.permission#ACCESS_TV_DESCRAMBLER:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_TV_DESCRAMBLER
-UnflaggedApi: android.Manifest.permission#ACCESS_TV_SHARED_FILTER:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_TV_SHARED_FILTER
-UnflaggedApi: android.Manifest.permission#ACCESS_TV_TUNER:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_TV_TUNER
-UnflaggedApi: android.Manifest.permission#ACCESS_ULTRASOUND:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_ULTRASOUND
-UnflaggedApi: android.Manifest.permission#ACCESS_VIBRATOR_STATE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_VIBRATOR_STATE
-UnflaggedApi: android.Manifest.permission#ACTIVITY_EMBEDDING:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACTIVITY_EMBEDDING
-UnflaggedApi: android.Manifest.permission#ADD_ALWAYS_UNLOCKED_DISPLAY:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ADD_ALWAYS_UNLOCKED_DISPLAY
-UnflaggedApi: android.Manifest.permission#ADD_TRUSTED_DISPLAY:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ADD_TRUSTED_DISPLAY
-UnflaggedApi: android.Manifest.permission#ADJUST_RUNTIME_PERMISSIONS_POLICY:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY
-UnflaggedApi: android.Manifest.permission#ALLOCATE_AGGRESSIVE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ALLOCATE_AGGRESSIVE
-UnflaggedApi: android.Manifest.permission#ALLOW_ANY_CODEC_FOR_PLAYBACK:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ALLOW_ANY_CODEC_FOR_PLAYBACK
-UnflaggedApi: android.Manifest.permission#ALLOW_PLACE_IN_MULTI_PANE_SETTINGS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ALLOW_PLACE_IN_MULTI_PANE_SETTINGS
-UnflaggedApi: android.Manifest.permission#ALLOW_SLIPPERY_TOUCHES:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ALLOW_SLIPPERY_TOUCHES
UnflaggedApi: android.Manifest.permission#ALWAYS_UPDATE_WALLPAPER:
New API must be flagged with @FlaggedApi: field android.Manifest.permission.ALWAYS_UPDATE_WALLPAPER
-UnflaggedApi: android.Manifest.permission#AMBIENT_WALLPAPER:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.AMBIENT_WALLPAPER
-UnflaggedApi: android.Manifest.permission#APPROVE_INCIDENT_REPORTS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.APPROVE_INCIDENT_REPORTS
-UnflaggedApi: android.Manifest.permission#ASSOCIATE_COMPANION_DEVICES:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ASSOCIATE_COMPANION_DEVICES
-UnflaggedApi: android.Manifest.permission#BACKGROUND_CAMERA:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BACKGROUND_CAMERA
-UnflaggedApi: android.Manifest.permission#BACKUP:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BACKUP
-UnflaggedApi: android.Manifest.permission#BATTERY_PREDICTION:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BATTERY_PREDICTION
-UnflaggedApi: android.Manifest.permission#BIND_AMBIENT_CONTEXT_DETECTION_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_AMBIENT_CONTEXT_DETECTION_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_ATTENTION_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_ATTENTION_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_AUGMENTED_AUTOFILL_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_AUGMENTED_AUTOFILL_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_CALL_DIAGNOSTIC_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_CALL_DIAGNOSTIC_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_CALL_STREAMING_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_CALL_STREAMING_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_CELL_BROADCAST_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_CELL_BROADCAST_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_CONTENT_CAPTURE_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_CONTENT_CAPTURE_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_CONTENT_SUGGESTIONS_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_CONTENT_SUGGESTIONS_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_DIRECTORY_SEARCH:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_DIRECTORY_SEARCH
-UnflaggedApi: android.Manifest.permission#BIND_DISPLAY_HASHING_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_DISPLAY_HASHING_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_DOMAIN_VERIFICATION_AGENT:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_DOMAIN_VERIFICATION_AGENT
-UnflaggedApi: android.Manifest.permission#BIND_EUICC_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_EUICC_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_EXTERNAL_STORAGE_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_EXTERNAL_STORAGE_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_FIELD_CLASSIFICATION_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_FIELD_CLASSIFICATION_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_GBA_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_GBA_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_HOTWORD_DETECTION_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_HOTWORD_DETECTION_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_IMS_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_IMS_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_KEYGUARD_APPWIDGET:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_KEYGUARD_APPWIDGET
-UnflaggedApi: android.Manifest.permission#BIND_MUSIC_RECOGNITION_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_MUSIC_RECOGNITION_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_NETWORK_RECOMMENDATION_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_NETWORK_RECOMMENDATION_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_NOTIFICATION_ASSISTANT_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_PHONE_ACCOUNT_SUGGESTION_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_PHONE_ACCOUNT_SUGGESTION_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_PRINT_RECOMMENDATION_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_PRINT_RECOMMENDATION_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_REMOTE_LOCKSCREEN_VALIDATION_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_REMOTE_LOCKSCREEN_VALIDATION_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_RESOLVER_RANKER_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_RESOLVER_RANKER_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_RESUME_ON_REBOOT_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_RESUME_ON_REBOOT_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_ROTATION_RESOLVER_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_ROTATION_RESOLVER_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_SATELLITE_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_SATELLITE_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_SETTINGS_SUGGESTIONS_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_SETTINGS_SUGGESTIONS_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_SOUND_TRIGGER_DETECTION_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_SOUND_TRIGGER_DETECTION_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_TELEPHONY_DATA_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_TELEPHONY_DATA_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_TELEPHONY_NETWORK_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_TELEPHONY_NETWORK_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_TEXTCLASSIFIER_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_TEXTCLASSIFIER_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_TIME_ZONE_PROVIDER_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_TIME_ZONE_PROVIDER_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_TRACE_REPORT_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_TRACE_REPORT_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_TRANSLATION_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_TRANSLATION_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_TRUST_AGENT:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_TRUST_AGENT
-UnflaggedApi: android.Manifest.permission#BIND_TV_REMOTE_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_TV_REMOTE_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_VISUAL_QUERY_DETECTION_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_VISUAL_QUERY_DETECTION_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_WALLPAPER_EFFECTS_GENERATION_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_WALLPAPER_EFFECTS_GENERATION_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_WEARABLE_SENSING_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_WEARABLE_SENSING_SERVICE
-UnflaggedApi: android.Manifest.permission#BLUETOOTH_MAP:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BLUETOOTH_MAP
-UnflaggedApi: android.Manifest.permission#BRICK:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BRICK
-UnflaggedApi: android.Manifest.permission#BRIGHTNESS_SLIDER_USAGE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BRIGHTNESS_SLIDER_USAGE
-UnflaggedApi: android.Manifest.permission#BROADCAST_CLOSE_SYSTEM_DIALOGS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS
-UnflaggedApi: android.Manifest.permission#BYPASS_ROLE_QUALIFICATION:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BYPASS_ROLE_QUALIFICATION
-UnflaggedApi: android.Manifest.permission#CALL_AUDIO_INTERCEPTION:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.CALL_AUDIO_INTERCEPTION
-UnflaggedApi: android.Manifest.permission#CAMERA_DISABLE_TRANSMIT_LED:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.CAMERA_DISABLE_TRANSMIT_LED
-UnflaggedApi: android.Manifest.permission#CAMERA_OPEN_CLOSE_LISTENER:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.CAMERA_OPEN_CLOSE_LISTENER
-UnflaggedApi: android.Manifest.permission#CAPTURE_AUDIO_HOTWORD:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.CAPTURE_AUDIO_HOTWORD
-UnflaggedApi: android.Manifest.permission#CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD
-UnflaggedApi: android.Manifest.permission#CAPTURE_MEDIA_OUTPUT:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.CAPTURE_MEDIA_OUTPUT
-UnflaggedApi: android.Manifest.permission#CAPTURE_TUNER_AUDIO_INPUT:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.CAPTURE_TUNER_AUDIO_INPUT
-UnflaggedApi: android.Manifest.permission#CAPTURE_TV_INPUT:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.CAPTURE_TV_INPUT
-UnflaggedApi: android.Manifest.permission#CAPTURE_VOICE_COMMUNICATION_OUTPUT:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.CAPTURE_VOICE_COMMUNICATION_OUTPUT
-UnflaggedApi: android.Manifest.permission#CHANGE_APP_IDLE_STATE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.CHANGE_APP_IDLE_STATE
-UnflaggedApi: android.Manifest.permission#CHANGE_APP_LAUNCH_TIME_ESTIMATE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.CHANGE_APP_LAUNCH_TIME_ESTIMATE
-UnflaggedApi: android.Manifest.permission#CHANGE_DEVICE_IDLE_TEMP_WHITELIST:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST
-UnflaggedApi: android.Manifest.permission#CHECK_REMOTE_LOCKSCREEN:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.CHECK_REMOTE_LOCKSCREEN
-UnflaggedApi: android.Manifest.permission#CLEAR_APP_USER_DATA:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.CLEAR_APP_USER_DATA
-UnflaggedApi: android.Manifest.permission#COMPANION_APPROVE_WIFI_CONNECTIONS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.COMPANION_APPROVE_WIFI_CONNECTIONS
-UnflaggedApi: android.Manifest.permission#CONFIGURE_DISPLAY_BRIGHTNESS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS
-UnflaggedApi: android.Manifest.permission#CONFIGURE_INTERACT_ACROSS_PROFILES:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES
-UnflaggedApi: android.Manifest.permission#CONNECTIVITY_USE_RESTRICTED_NETWORKS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS
-UnflaggedApi: android.Manifest.permission#CONTROL_DEVICE_LIGHTS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.CONTROL_DEVICE_LIGHTS
-UnflaggedApi: android.Manifest.permission#CONTROL_DISPLAY_COLOR_TRANSFORMS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS
-UnflaggedApi: android.Manifest.permission#CONTROL_DISPLAY_SATURATION:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.CONTROL_DISPLAY_SATURATION
-UnflaggedApi: android.Manifest.permission#CONTROL_INCALL_EXPERIENCE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.CONTROL_INCALL_EXPERIENCE
-UnflaggedApi: android.Manifest.permission#CONTROL_KEYGUARD_SECURE_NOTIFICATIONS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS
-UnflaggedApi: android.Manifest.permission#CONTROL_OEM_PAID_NETWORK_PREFERENCE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.CONTROL_OEM_PAID_NETWORK_PREFERENCE
-UnflaggedApi: android.Manifest.permission#CONTROL_VPN:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.CONTROL_VPN
-UnflaggedApi: android.Manifest.permission#CREATE_USERS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.CREATE_USERS
-UnflaggedApi: android.Manifest.permission#CREATE_VIRTUAL_DEVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.CREATE_VIRTUAL_DEVICE
-UnflaggedApi: android.Manifest.permission#CRYPT_KEEPER:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.CRYPT_KEEPER
-UnflaggedApi: android.Manifest.permission#DEVICE_POWER:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.DEVICE_POWER
-UnflaggedApi: android.Manifest.permission#DISABLE_SYSTEM_SOUND_EFFECTS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.DISABLE_SYSTEM_SOUND_EFFECTS
-UnflaggedApi: android.Manifest.permission#DISPATCH_PROVISIONING_MESSAGE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.DISPATCH_PROVISIONING_MESSAGE
-UnflaggedApi: android.Manifest.permission#DOMAIN_VERIFICATION_AGENT:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.DOMAIN_VERIFICATION_AGENT
-UnflaggedApi: android.Manifest.permission#ENTER_CAR_MODE_PRIORITIZED:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ENTER_CAR_MODE_PRIORITIZED
-UnflaggedApi: android.Manifest.permission#EXEMPT_FROM_AUDIO_RECORD_RESTRICTIONS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.EXEMPT_FROM_AUDIO_RECORD_RESTRICTIONS
-UnflaggedApi: android.Manifest.permission#FORCE_BACK:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.FORCE_BACK
-UnflaggedApi: android.Manifest.permission#FORCE_STOP_PACKAGES:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.FORCE_STOP_PACKAGES
-UnflaggedApi: android.Manifest.permission#GET_APP_METADATA:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.GET_APP_METADATA
-UnflaggedApi: android.Manifest.permission#GET_APP_OPS_STATS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.GET_APP_OPS_STATS
-UnflaggedApi: android.Manifest.permission#GET_HISTORICAL_APP_OPS_STATS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.GET_HISTORICAL_APP_OPS_STATS
-UnflaggedApi: android.Manifest.permission#GET_PROCESS_STATE_AND_OOM_SCORE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.GET_PROCESS_STATE_AND_OOM_SCORE
-UnflaggedApi: android.Manifest.permission#GET_RUNTIME_PERMISSIONS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.GET_RUNTIME_PERMISSIONS
-UnflaggedApi: android.Manifest.permission#GET_TOP_ACTIVITY_INFO:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.GET_TOP_ACTIVITY_INFO
-UnflaggedApi: android.Manifest.permission#GRANT_RUNTIME_PERMISSIONS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS
-UnflaggedApi: android.Manifest.permission#GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS
-UnflaggedApi: android.Manifest.permission#HANDLE_CAR_MODE_CHANGES:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.HANDLE_CAR_MODE_CHANGES
-UnflaggedApi: android.Manifest.permission#HARDWARE_TEST:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.HARDWARE_TEST
-UnflaggedApi: android.Manifest.permission#HDMI_CEC:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.HDMI_CEC
-UnflaggedApi: android.Manifest.permission#INJECT_EVENTS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.INJECT_EVENTS
-UnflaggedApi: android.Manifest.permission#INSTALL_DPC_PACKAGES:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.INSTALL_DPC_PACKAGES
-UnflaggedApi: android.Manifest.permission#INSTALL_DYNAMIC_SYSTEM:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.INSTALL_DYNAMIC_SYSTEM
-UnflaggedApi: android.Manifest.permission#INSTALL_EXISTING_PACKAGES:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.INSTALL_EXISTING_PACKAGES
-UnflaggedApi: android.Manifest.permission#INSTALL_GRANT_RUNTIME_PERMISSIONS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS
-UnflaggedApi: android.Manifest.permission#INSTALL_LOCATION_TIME_ZONE_PROVIDER_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.INSTALL_LOCATION_TIME_ZONE_PROVIDER_SERVICE
-UnflaggedApi: android.Manifest.permission#INSTALL_PACKAGE_UPDATES:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.INSTALL_PACKAGE_UPDATES
-UnflaggedApi: android.Manifest.permission#INSTALL_SELF_UPDATES:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.INSTALL_SELF_UPDATES
-UnflaggedApi: android.Manifest.permission#INTENT_FILTER_VERIFICATION_AGENT:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT
-UnflaggedApi: android.Manifest.permission#INTERACT_ACROSS_USERS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.INTERACT_ACROSS_USERS
-UnflaggedApi: android.Manifest.permission#INTERACT_ACROSS_USERS_FULL:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.INTERACT_ACROSS_USERS_FULL
-UnflaggedApi: android.Manifest.permission#INTERNAL_SYSTEM_WINDOW:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.INTERNAL_SYSTEM_WINDOW
-UnflaggedApi: android.Manifest.permission#INVOKE_CARRIER_SETUP:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.INVOKE_CARRIER_SETUP
-UnflaggedApi: android.Manifest.permission#KILL_ALL_BACKGROUND_PROCESSES:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.KILL_ALL_BACKGROUND_PROCESSES
-UnflaggedApi: android.Manifest.permission#KILL_UID:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.KILL_UID
-UnflaggedApi: android.Manifest.permission#LAUNCH_DEVICE_MANAGER_SETUP:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.LAUNCH_DEVICE_MANAGER_SETUP
+UnflaggedApi: android.Manifest.permission#CAMERA_HEADLESS_SYSTEM_USER:
+ New API must be flagged with @FlaggedApi: field android.Manifest.permission.CAMERA_HEADLESS_SYSTEM_USER
UnflaggedApi: android.Manifest.permission#LAUNCH_PERMISSION_SETTINGS:
New API must be flagged with @FlaggedApi: field android.Manifest.permission.LAUNCH_PERMISSION_SETTINGS
-UnflaggedApi: android.Manifest.permission#LOCAL_MAC_ADDRESS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.LOCAL_MAC_ADDRESS
-UnflaggedApi: android.Manifest.permission#LOCATION_BYPASS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.LOCATION_BYPASS
-UnflaggedApi: android.Manifest.permission#LOCK_DEVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.LOCK_DEVICE
-UnflaggedApi: android.Manifest.permission#LOG_FOREGROUND_RESOURCE_USE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.LOG_FOREGROUND_RESOURCE_USE
-UnflaggedApi: android.Manifest.permission#LOOP_RADIO:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.LOOP_RADIO
-UnflaggedApi: android.Manifest.permission#MANAGE_ACCESSIBILITY:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_ACCESSIBILITY
-UnflaggedApi: android.Manifest.permission#MANAGE_ACTIVITY_TASKS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_ACTIVITY_TASKS
-UnflaggedApi: android.Manifest.permission#MANAGE_APP_HIBERNATION:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_APP_HIBERNATION
-UnflaggedApi: android.Manifest.permission#MANAGE_APP_OPS_RESTRICTIONS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_APP_OPS_RESTRICTIONS
-UnflaggedApi: android.Manifest.permission#MANAGE_APP_PREDICTIONS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_APP_PREDICTIONS
-UnflaggedApi: android.Manifest.permission#MANAGE_APP_TOKENS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_APP_TOKENS
-UnflaggedApi: android.Manifest.permission#MANAGE_AUTO_FILL:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_AUTO_FILL
-UnflaggedApi: android.Manifest.permission#MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED
-UnflaggedApi: android.Manifest.permission#MANAGE_CARRIER_OEM_UNLOCK_STATE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_CARRIER_OEM_UNLOCK_STATE
-UnflaggedApi: android.Manifest.permission#MANAGE_CA_CERTIFICATES:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_CA_CERTIFICATES
-UnflaggedApi: android.Manifest.permission#MANAGE_CLIPBOARD_ACCESS_NOTIFICATION:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_CLIPBOARD_ACCESS_NOTIFICATION
-UnflaggedApi: android.Manifest.permission#MANAGE_CLOUDSEARCH:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_CLOUDSEARCH
-UnflaggedApi: android.Manifest.permission#MANAGE_CONTENT_CAPTURE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_CONTENT_CAPTURE
-UnflaggedApi: android.Manifest.permission#MANAGE_CONTENT_SUGGESTIONS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_CONTENT_SUGGESTIONS
-UnflaggedApi: android.Manifest.permission#MANAGE_DEBUGGING:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_DEBUGGING
-UnflaggedApi: android.Manifest.permission#MANAGE_DEFAULT_APPLICATIONS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_DEFAULT_APPLICATIONS
-UnflaggedApi: android.Manifest.permission#MANAGE_DEVICE_ADMINS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_DEVICE_ADMINS
-UnflaggedApi: android.Manifest.permission#MANAGE_DEVICE_POLICY_APP_EXEMPTIONS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_DEVICE_POLICY_APP_EXEMPTIONS
-UnflaggedApi: android.Manifest.permission#MANAGE_ETHERNET_NETWORKS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_ETHERNET_NETWORKS
-UnflaggedApi: android.Manifest.permission#MANAGE_FACTORY_RESET_PROTECTION:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_FACTORY_RESET_PROTECTION
-UnflaggedApi: android.Manifest.permission#MANAGE_GAME_ACTIVITY:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_GAME_ACTIVITY
-UnflaggedApi: android.Manifest.permission#MANAGE_GAME_MODE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_GAME_MODE
-UnflaggedApi: android.Manifest.permission#MANAGE_HOTWORD_DETECTION:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_HOTWORD_DETECTION
-UnflaggedApi: android.Manifest.permission#MANAGE_IPSEC_TUNNELS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_IPSEC_TUNNELS
-UnflaggedApi: android.Manifest.permission#MANAGE_LOW_POWER_STANDBY:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_LOW_POWER_STANDBY
-UnflaggedApi: android.Manifest.permission#MANAGE_MUSIC_RECOGNITION:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_MUSIC_RECOGNITION
-UnflaggedApi: android.Manifest.permission#MANAGE_NOTIFICATION_LISTENERS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS
-UnflaggedApi: android.Manifest.permission#MANAGE_ONE_TIME_PERMISSION_SESSIONS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS
-UnflaggedApi: android.Manifest.permission#MANAGE_PROFILE_AND_DEVICE_OWNERS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS
-UnflaggedApi: android.Manifest.permission#MANAGE_ROLE_HOLDERS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_ROLE_HOLDERS
-UnflaggedApi: android.Manifest.permission#MANAGE_ROLLBACKS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_ROLLBACKS
-UnflaggedApi: android.Manifest.permission#MANAGE_ROTATION_RESOLVER:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_ROTATION_RESOLVER
-UnflaggedApi: android.Manifest.permission#MANAGE_SAFETY_CENTER:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_SAFETY_CENTER
-UnflaggedApi: android.Manifest.permission#MANAGE_SEARCH_UI:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_SEARCH_UI
-UnflaggedApi: android.Manifest.permission#MANAGE_SENSOR_PRIVACY:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_SENSOR_PRIVACY
-UnflaggedApi: android.Manifest.permission#MANAGE_SMARTSPACE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_SMARTSPACE
-UnflaggedApi: android.Manifest.permission#MANAGE_SOUND_TRIGGER:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_SOUND_TRIGGER
-UnflaggedApi: android.Manifest.permission#MANAGE_SPEECH_RECOGNITION:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_SPEECH_RECOGNITION
-UnflaggedApi: android.Manifest.permission#MANAGE_SUBSCRIPTION_PLANS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_SUBSCRIPTION_PLANS
-UnflaggedApi: android.Manifest.permission#MANAGE_SUBSCRIPTION_USER_ASSOCIATION:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION
-UnflaggedApi: android.Manifest.permission#MANAGE_TEST_NETWORKS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_TEST_NETWORKS
-UnflaggedApi: android.Manifest.permission#MANAGE_TIME_AND_ZONE_DETECTION:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION
-UnflaggedApi: android.Manifest.permission#MANAGE_UI_TRANSLATION:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_UI_TRANSLATION
-UnflaggedApi: android.Manifest.permission#MANAGE_USB:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_USB
-UnflaggedApi: android.Manifest.permission#MANAGE_USERS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_USERS
-UnflaggedApi: android.Manifest.permission#MANAGE_USER_OEM_UNLOCK_STATE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_USER_OEM_UNLOCK_STATE
-UnflaggedApi: android.Manifest.permission#MANAGE_WALLPAPER_EFFECTS_GENERATION:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_WALLPAPER_EFFECTS_GENERATION
-UnflaggedApi: android.Manifest.permission#MANAGE_WEAK_ESCROW_TOKEN:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_WEAK_ESCROW_TOKEN
-UnflaggedApi: android.Manifest.permission#MANAGE_WEARABLE_SENSING_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE
-UnflaggedApi: android.Manifest.permission#MANAGE_WIFI_COUNTRY_CODE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_WIFI_COUNTRY_CODE
-UnflaggedApi: android.Manifest.permission#MARK_DEVICE_ORGANIZATION_OWNED:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MARK_DEVICE_ORGANIZATION_OWNED
-UnflaggedApi: android.Manifest.permission#MEDIA_RESOURCE_OVERRIDE_PID:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MEDIA_RESOURCE_OVERRIDE_PID
-UnflaggedApi: android.Manifest.permission#MIGRATE_HEALTH_CONNECT_DATA:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MIGRATE_HEALTH_CONNECT_DATA
-UnflaggedApi: android.Manifest.permission#MODIFY_APPWIDGET_BIND_PERMISSIONS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS
-UnflaggedApi: android.Manifest.permission#MODIFY_AUDIO_ROUTING:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MODIFY_AUDIO_ROUTING
-UnflaggedApi: android.Manifest.permission#MODIFY_AUDIO_SETTINGS_PRIVILEGED:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED
-UnflaggedApi: android.Manifest.permission#MODIFY_CELL_BROADCASTS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MODIFY_CELL_BROADCASTS
-UnflaggedApi: android.Manifest.permission#MODIFY_DAY_NIGHT_MODE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MODIFY_DAY_NIGHT_MODE
-UnflaggedApi: android.Manifest.permission#MODIFY_PARENTAL_CONTROLS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MODIFY_PARENTAL_CONTROLS
-UnflaggedApi: android.Manifest.permission#MODIFY_QUIET_MODE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MODIFY_QUIET_MODE
-UnflaggedApi: android.Manifest.permission#MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE
-UnflaggedApi: android.Manifest.permission#MONITOR_DEVICE_CONFIG_ACCESS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MONITOR_DEVICE_CONFIG_ACCESS
-UnflaggedApi: android.Manifest.permission#MOVE_PACKAGE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MOVE_PACKAGE
-UnflaggedApi: android.Manifest.permission#NETWORK_AIRPLANE_MODE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.NETWORK_AIRPLANE_MODE
-UnflaggedApi: android.Manifest.permission#NETWORK_CARRIER_PROVISIONING:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.NETWORK_CARRIER_PROVISIONING
-UnflaggedApi: android.Manifest.permission#NETWORK_FACTORY:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.NETWORK_FACTORY
-UnflaggedApi: android.Manifest.permission#NETWORK_MANAGED_PROVISIONING:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.NETWORK_MANAGED_PROVISIONING
-UnflaggedApi: android.Manifest.permission#NETWORK_SCAN:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.NETWORK_SCAN
-UnflaggedApi: android.Manifest.permission#NETWORK_SETTINGS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.NETWORK_SETTINGS
-UnflaggedApi: android.Manifest.permission#NETWORK_SETUP_WIZARD:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.NETWORK_SETUP_WIZARD
-UnflaggedApi: android.Manifest.permission#NETWORK_SIGNAL_STRENGTH_WAKEUP:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP
-UnflaggedApi: android.Manifest.permission#NETWORK_STACK:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.NETWORK_STACK
-UnflaggedApi: android.Manifest.permission#NETWORK_STATS_PROVIDER:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.NETWORK_STATS_PROVIDER
-UnflaggedApi: android.Manifest.permission#NFC_SET_CONTROLLER_ALWAYS_ON:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON
-UnflaggedApi: android.Manifest.permission#NOTIFICATION_DURING_SETUP:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.NOTIFICATION_DURING_SETUP
-UnflaggedApi: android.Manifest.permission#NOTIFY_TV_INPUTS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.NOTIFY_TV_INPUTS
-UnflaggedApi: android.Manifest.permission#OBSERVE_APP_USAGE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.OBSERVE_APP_USAGE
-UnflaggedApi: android.Manifest.permission#OBSERVE_NETWORK_POLICY:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.OBSERVE_NETWORK_POLICY
-UnflaggedApi: android.Manifest.permission#OBSERVE_ROLE_HOLDERS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.OBSERVE_ROLE_HOLDERS
-UnflaggedApi: android.Manifest.permission#OBSERVE_SENSOR_PRIVACY:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.OBSERVE_SENSOR_PRIVACY
-UnflaggedApi: android.Manifest.permission#OPEN_ACCESSIBILITY_DETAILS_SETTINGS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.OPEN_ACCESSIBILITY_DETAILS_SETTINGS
-UnflaggedApi: android.Manifest.permission#OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD
-UnflaggedApi: android.Manifest.permission#PACKAGE_VERIFICATION_AGENT:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.PACKAGE_VERIFICATION_AGENT
-UnflaggedApi: android.Manifest.permission#PACKET_KEEPALIVE_OFFLOAD:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD
-UnflaggedApi: android.Manifest.permission#PEERS_MAC_ADDRESS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.PEERS_MAC_ADDRESS
-UnflaggedApi: android.Manifest.permission#PERFORM_CDMA_PROVISIONING:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.PERFORM_CDMA_PROVISIONING
-UnflaggedApi: android.Manifest.permission#PERFORM_IMS_SINGLE_REGISTRATION:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION
-UnflaggedApi: android.Manifest.permission#PERFORM_SIM_ACTIVATION:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.PERFORM_SIM_ACTIVATION
-UnflaggedApi: android.Manifest.permission#POWER_SAVER:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.POWER_SAVER
-UnflaggedApi: android.Manifest.permission#PROVIDE_DEFAULT_ENABLED_CREDENTIAL_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.PROVIDE_DEFAULT_ENABLED_CREDENTIAL_SERVICE
-UnflaggedApi: android.Manifest.permission#PROVIDE_RESOLVER_RANKER_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.PROVIDE_RESOLVER_RANKER_SERVICE
-UnflaggedApi: android.Manifest.permission#PROVIDE_TRUST_AGENT:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.PROVIDE_TRUST_AGENT
-UnflaggedApi: android.Manifest.permission#PROVISION_DEMO_DEVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.PROVISION_DEMO_DEVICE
-UnflaggedApi: android.Manifest.permission#QUERY_ADMIN_POLICY:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.QUERY_ADMIN_POLICY
-UnflaggedApi: android.Manifest.permission#QUERY_CLONED_APPS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.QUERY_CLONED_APPS
-UnflaggedApi: android.Manifest.permission#QUERY_USERS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.QUERY_USERS
-UnflaggedApi: android.Manifest.permission#RADIO_SCAN_WITHOUT_LOCATION:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.RADIO_SCAN_WITHOUT_LOCATION
-UnflaggedApi: android.Manifest.permission#READ_ACTIVE_EMERGENCY_SESSION:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION
-UnflaggedApi: android.Manifest.permission#READ_APP_SPECIFIC_LOCALES:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_APP_SPECIFIC_LOCALES
-UnflaggedApi: android.Manifest.permission#READ_CARRIER_APP_INFO:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_CARRIER_APP_INFO
-UnflaggedApi: android.Manifest.permission#READ_CELL_BROADCASTS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_CELL_BROADCASTS
-UnflaggedApi: android.Manifest.permission#READ_CLIPBOARD_IN_BACKGROUND:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_CLIPBOARD_IN_BACKGROUND
-UnflaggedApi: android.Manifest.permission#READ_CONTENT_RATING_SYSTEMS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_CONTENT_RATING_SYSTEMS
-UnflaggedApi: android.Manifest.permission#READ_DEVICE_CONFIG:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_DEVICE_CONFIG
-UnflaggedApi: android.Manifest.permission#READ_DREAM_STATE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_DREAM_STATE
-UnflaggedApi: android.Manifest.permission#READ_GLOBAL_APP_SEARCH_DATA:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_GLOBAL_APP_SEARCH_DATA
UnflaggedApi: android.Manifest.permission#READ_INSTALLED_SESSION_PATHS:
New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_INSTALLED_SESSION_PATHS
-UnflaggedApi: android.Manifest.permission#READ_INSTALL_SESSIONS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_INSTALL_SESSIONS
-UnflaggedApi: android.Manifest.permission#READ_NETWORK_USAGE_HISTORY:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_NETWORK_USAGE_HISTORY
-UnflaggedApi: android.Manifest.permission#READ_OEM_UNLOCK_STATE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_OEM_UNLOCK_STATE
-UnflaggedApi: android.Manifest.permission#READ_PEOPLE_DATA:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_PEOPLE_DATA
-UnflaggedApi: android.Manifest.permission#READ_PRINT_SERVICES:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_PRINT_SERVICES
-UnflaggedApi: android.Manifest.permission#READ_PRINT_SERVICE_RECOMMENDATIONS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_PRINT_SERVICE_RECOMMENDATIONS
-UnflaggedApi: android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE
-UnflaggedApi: android.Manifest.permission#READ_PROJECTION_STATE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_PROJECTION_STATE
-UnflaggedApi: android.Manifest.permission#READ_RESTRICTED_STATS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_RESTRICTED_STATS
-UnflaggedApi: android.Manifest.permission#READ_RUNTIME_PROFILES:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_RUNTIME_PROFILES
-UnflaggedApi: android.Manifest.permission#READ_SAFETY_CENTER_STATUS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_SAFETY_CENTER_STATUS
-UnflaggedApi: android.Manifest.permission#READ_SEARCH_INDEXABLES:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_SEARCH_INDEXABLES
-UnflaggedApi: android.Manifest.permission#READ_SYSTEM_UPDATE_INFO:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_SYSTEM_UPDATE_INFO
-UnflaggedApi: android.Manifest.permission#READ_WALLPAPER_INTERNAL:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_WALLPAPER_INTERNAL
-UnflaggedApi: android.Manifest.permission#READ_WIFI_CREDENTIAL:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_WIFI_CREDENTIAL
-UnflaggedApi: android.Manifest.permission#READ_WRITE_SYNC_DISABLED_MODE_CONFIG:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_WRITE_SYNC_DISABLED_MODE_CONFIG
-UnflaggedApi: android.Manifest.permission#REAL_GET_TASKS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.REAL_GET_TASKS
-UnflaggedApi: android.Manifest.permission#RECEIVE_BLUETOOTH_MAP:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.RECEIVE_BLUETOOTH_MAP
-UnflaggedApi: android.Manifest.permission#RECEIVE_DATA_ACTIVITY_CHANGE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.RECEIVE_DATA_ACTIVITY_CHANGE
-UnflaggedApi: android.Manifest.permission#RECEIVE_DEVICE_CUSTOMIZATION_READY:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.RECEIVE_DEVICE_CUSTOMIZATION_READY
-UnflaggedApi: android.Manifest.permission#RECEIVE_EMERGENCY_BROADCAST:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.RECEIVE_EMERGENCY_BROADCAST
-UnflaggedApi: android.Manifest.permission#RECEIVE_WIFI_CREDENTIAL_CHANGE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.RECEIVE_WIFI_CREDENTIAL_CHANGE
-UnflaggedApi: android.Manifest.permission#RECORD_BACKGROUND_AUDIO:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.RECORD_BACKGROUND_AUDIO
-UnflaggedApi: android.Manifest.permission#RECOVERY:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.RECOVERY
-UnflaggedApi: android.Manifest.permission#RECOVER_KEYSTORE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.RECOVER_KEYSTORE
-UnflaggedApi: android.Manifest.permission#REGISTER_CALL_PROVIDER:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.REGISTER_CALL_PROVIDER
-UnflaggedApi: android.Manifest.permission#REGISTER_CONNECTION_MANAGER:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.REGISTER_CONNECTION_MANAGER
UnflaggedApi: android.Manifest.permission#REGISTER_NSD_OFFLOAD_ENGINE:
New API must be flagged with @FlaggedApi: field android.Manifest.permission.REGISTER_NSD_OFFLOAD_ENGINE
-UnflaggedApi: android.Manifest.permission#REGISTER_SIM_SUBSCRIPTION:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.REGISTER_SIM_SUBSCRIPTION
-UnflaggedApi: android.Manifest.permission#REGISTER_STATS_PULL_ATOM:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.REGISTER_STATS_PULL_ATOM
-UnflaggedApi: android.Manifest.permission#REMOTE_DISPLAY_PROVIDER:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.REMOTE_DISPLAY_PROVIDER
-UnflaggedApi: android.Manifest.permission#REMOVE_DRM_CERTIFICATES:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.REMOVE_DRM_CERTIFICATES
-UnflaggedApi: android.Manifest.permission#REMOVE_TASKS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.REMOVE_TASKS
-UnflaggedApi: android.Manifest.permission#RENOUNCE_PERMISSIONS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.RENOUNCE_PERMISSIONS
UnflaggedApi: android.Manifest.permission#REPORT_USAGE_STATS:
New API must be flagged with @FlaggedApi: field android.Manifest.permission.REPORT_USAGE_STATS
-UnflaggedApi: android.Manifest.permission#REQUEST_NOTIFICATION_ASSISTANT_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE
-UnflaggedApi: android.Manifest.permission#RESET_PASSWORD:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.RESET_PASSWORD
-UnflaggedApi: android.Manifest.permission#RESTART_WIFI_SUBSYSTEM:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.RESTART_WIFI_SUBSYSTEM
-UnflaggedApi: android.Manifest.permission#RESTORE_RUNTIME_PERMISSIONS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.RESTORE_RUNTIME_PERMISSIONS
-UnflaggedApi: android.Manifest.permission#RESTRICTED_VR_ACCESS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.RESTRICTED_VR_ACCESS
-UnflaggedApi: android.Manifest.permission#RETRIEVE_WINDOW_CONTENT:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.RETRIEVE_WINDOW_CONTENT
-UnflaggedApi: android.Manifest.permission#REVIEW_ACCESSIBILITY_SERVICES:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.REVIEW_ACCESSIBILITY_SERVICES
-UnflaggedApi: android.Manifest.permission#REVOKE_RUNTIME_PERMISSIONS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS
-UnflaggedApi: android.Manifest.permission#ROTATE_SURFACE_FLINGER:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ROTATE_SURFACE_FLINGER
-UnflaggedApi: android.Manifest.permission#SATELLITE_COMMUNICATION:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SATELLITE_COMMUNICATION
-UnflaggedApi: android.Manifest.permission#SCHEDULE_PRIORITIZED_ALARM:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SCHEDULE_PRIORITIZED_ALARM
-UnflaggedApi: android.Manifest.permission#SECURE_ELEMENT_PRIVILEGED_OPERATION:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SECURE_ELEMENT_PRIVILEGED_OPERATION
-UnflaggedApi: android.Manifest.permission#SEND_CATEGORY_CAR_NOTIFICATIONS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SEND_CATEGORY_CAR_NOTIFICATIONS
-UnflaggedApi: android.Manifest.permission#SEND_DEVICE_CUSTOMIZATION_READY:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SEND_DEVICE_CUSTOMIZATION_READY
-UnflaggedApi: android.Manifest.permission#SEND_SAFETY_CENTER_UPDATE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SEND_SAFETY_CENTER_UPDATE
-UnflaggedApi: android.Manifest.permission#SEND_SHOW_SUSPENDED_APP_DETAILS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SEND_SHOW_SUSPENDED_APP_DETAILS
-UnflaggedApi: android.Manifest.permission#SEND_SMS_NO_CONFIRMATION:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SEND_SMS_NO_CONFIRMATION
-UnflaggedApi: android.Manifest.permission#SERIAL_PORT:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SERIAL_PORT
-UnflaggedApi: android.Manifest.permission#SET_ACTIVITY_WATCHER:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SET_ACTIVITY_WATCHER
-UnflaggedApi: android.Manifest.permission#SET_CLIP_SOURCE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SET_CLIP_SOURCE
-UnflaggedApi: android.Manifest.permission#SET_DEFAULT_ACCOUNT_FOR_CONTACTS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SET_DEFAULT_ACCOUNT_FOR_CONTACTS
-UnflaggedApi: android.Manifest.permission#SET_HARMFUL_APP_WARNINGS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SET_HARMFUL_APP_WARNINGS
-UnflaggedApi: android.Manifest.permission#SET_LOW_POWER_STANDBY_PORTS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SET_LOW_POWER_STANDBY_PORTS
-UnflaggedApi: android.Manifest.permission#SET_MEDIA_KEY_LISTENER:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SET_MEDIA_KEY_LISTENER
-UnflaggedApi: android.Manifest.permission#SET_ORIENTATION:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SET_ORIENTATION
-UnflaggedApi: android.Manifest.permission#SET_POINTER_SPEED:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SET_POINTER_SPEED
-UnflaggedApi: android.Manifest.permission#SET_SCREEN_COMPATIBILITY:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SET_SCREEN_COMPATIBILITY
-UnflaggedApi: android.Manifest.permission#SET_SYSTEM_AUDIO_CAPTION:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SET_SYSTEM_AUDIO_CAPTION
-UnflaggedApi: android.Manifest.permission#SET_UNRESTRICTED_KEEP_CLEAR_AREAS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SET_UNRESTRICTED_KEEP_CLEAR_AREAS
-UnflaggedApi: android.Manifest.permission#SET_VOLUME_KEY_LONG_PRESS_LISTENER:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SET_VOLUME_KEY_LONG_PRESS_LISTENER
-UnflaggedApi: android.Manifest.permission#SET_WALLPAPER_COMPONENT:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SET_WALLPAPER_COMPONENT
-UnflaggedApi: android.Manifest.permission#SET_WALLPAPER_DIM_AMOUNT:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT
-UnflaggedApi: android.Manifest.permission#SHOW_KEYGUARD_MESSAGE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SHOW_KEYGUARD_MESSAGE
-UnflaggedApi: android.Manifest.permission#SHUTDOWN:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SHUTDOWN
-UnflaggedApi: android.Manifest.permission#SIGNAL_REBOOT_READINESS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SIGNAL_REBOOT_READINESS
-UnflaggedApi: android.Manifest.permission#SOUND_TRIGGER_RUN_IN_BATTERY_SAVER:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SOUND_TRIGGER_RUN_IN_BATTERY_SAVER
-UnflaggedApi: android.Manifest.permission#STAGE_HEALTH_CONNECT_REMOTE_DATA:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.STAGE_HEALTH_CONNECT_REMOTE_DATA
-UnflaggedApi: android.Manifest.permission#START_ACTIVITIES_FROM_BACKGROUND:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND
-UnflaggedApi: android.Manifest.permission#START_CROSS_PROFILE_ACTIVITIES:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.START_CROSS_PROFILE_ACTIVITIES
-UnflaggedApi: android.Manifest.permission#START_REVIEW_PERMISSION_DECISIONS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.START_REVIEW_PERMISSION_DECISIONS
-UnflaggedApi: android.Manifest.permission#START_TASKS_FROM_RECENTS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.START_TASKS_FROM_RECENTS
-UnflaggedApi: android.Manifest.permission#STATUS_BAR_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.STATUS_BAR_SERVICE
-UnflaggedApi: android.Manifest.permission#STOP_APP_SWITCHES:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.STOP_APP_SWITCHES
-UnflaggedApi: android.Manifest.permission#SUBSTITUTE_NOTIFICATION_APP_NAME:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME
-UnflaggedApi: android.Manifest.permission#SUBSTITUTE_SHARE_TARGET_APP_NAME_AND_ICON:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SUBSTITUTE_SHARE_TARGET_APP_NAME_AND_ICON
-UnflaggedApi: android.Manifest.permission#SUGGEST_EXTERNAL_TIME:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SUGGEST_EXTERNAL_TIME
-UnflaggedApi: android.Manifest.permission#SUSPEND_APPS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SUSPEND_APPS
-UnflaggedApi: android.Manifest.permission#SYSTEM_APPLICATION_OVERLAY:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SYSTEM_APPLICATION_OVERLAY
-UnflaggedApi: android.Manifest.permission#SYSTEM_CAMERA:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SYSTEM_CAMERA
-UnflaggedApi: android.Manifest.permission#TETHER_PRIVILEGED:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.TETHER_PRIVILEGED
-UnflaggedApi: android.Manifest.permission#TIS_EXTENSION_INTERFACE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.TIS_EXTENSION_INTERFACE
-UnflaggedApi: android.Manifest.permission#TOGGLE_AUTOMOTIVE_PROJECTION:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.TOGGLE_AUTOMOTIVE_PROJECTION
-UnflaggedApi: android.Manifest.permission#TRIGGER_LOST_MODE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.TRIGGER_LOST_MODE
-UnflaggedApi: android.Manifest.permission#TV_INPUT_HARDWARE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.TV_INPUT_HARDWARE
-UnflaggedApi: android.Manifest.permission#TV_VIRTUAL_REMOTE_CONTROLLER:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.TV_VIRTUAL_REMOTE_CONTROLLER
-UnflaggedApi: android.Manifest.permission#UNLIMITED_SHORTCUTS_API_CALLS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.UNLIMITED_SHORTCUTS_API_CALLS
-UnflaggedApi: android.Manifest.permission#UPDATE_APP_OPS_STATS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.UPDATE_APP_OPS_STATS
-UnflaggedApi: android.Manifest.permission#UPDATE_DEVICE_MANAGEMENT_RESOURCES:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES
-UnflaggedApi: android.Manifest.permission#UPDATE_DOMAIN_VERIFICATION_USER_SELECTION:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION
-UnflaggedApi: android.Manifest.permission#UPDATE_FONTS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.UPDATE_FONTS
-UnflaggedApi: android.Manifest.permission#UPDATE_LOCK:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.UPDATE_LOCK
-UnflaggedApi: android.Manifest.permission#UPGRADE_RUNTIME_PERMISSIONS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.UPGRADE_RUNTIME_PERMISSIONS
-UnflaggedApi: android.Manifest.permission#USER_ACTIVITY:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.USER_ACTIVITY
-UnflaggedApi: android.Manifest.permission#USE_COLORIZED_NOTIFICATIONS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS
-UnflaggedApi: android.Manifest.permission#USE_RESERVED_DISK:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.USE_RESERVED_DISK
-UnflaggedApi: android.Manifest.permission#UWB_PRIVILEGED:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.UWB_PRIVILEGED
-UnflaggedApi: android.Manifest.permission#WHITELIST_AUTO_REVOKE_PERMISSIONS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.WHITELIST_AUTO_REVOKE_PERMISSIONS
-UnflaggedApi: android.Manifest.permission#WHITELIST_RESTRICTED_PERMISSIONS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS
-UnflaggedApi: android.Manifest.permission#WIFI_ACCESS_COEX_UNSAFE_CHANNELS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS
-UnflaggedApi: android.Manifest.permission#WIFI_SET_DEVICE_MOBILITY_STATE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.WIFI_SET_DEVICE_MOBILITY_STATE
-UnflaggedApi: android.Manifest.permission#WIFI_UPDATE_COEX_UNSAFE_CHANNELS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.WIFI_UPDATE_COEX_UNSAFE_CHANNELS
-UnflaggedApi: android.Manifest.permission#WIFI_UPDATE_USABILITY_STATS_SCORE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE
-UnflaggedApi: android.Manifest.permission#WRITE_ALLOWLISTED_DEVICE_CONFIG:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.WRITE_ALLOWLISTED_DEVICE_CONFIG
-UnflaggedApi: android.Manifest.permission#WRITE_DEVICE_CONFIG:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.WRITE_DEVICE_CONFIG
-UnflaggedApi: android.Manifest.permission#WRITE_DREAM_STATE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.WRITE_DREAM_STATE
-UnflaggedApi: android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS
-UnflaggedApi: android.Manifest.permission#WRITE_OBB:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.WRITE_OBB
-UnflaggedApi: android.Manifest.permission#WRITE_SECURITY_LOG:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.WRITE_SECURITY_LOG
-UnflaggedApi: android.Manifest.permission#WRITE_SMS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.WRITE_SMS
-UnflaggedApi: android.Manifest.permission_group#UNDEFINED:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission_group.UNDEFINED
-UnflaggedApi: android.R.array#config_keySystemUuidMapping:
- New API must be flagged with @FlaggedApi: field android.R.array.config_keySystemUuidMapping
-UnflaggedApi: android.R.array#config_optionalIpSecAlgorithms:
- New API must be flagged with @FlaggedApi: field android.R.array.config_optionalIpSecAlgorithms
-UnflaggedApi: android.R.attr#allowClearUserDataOnFailedRestore:
- New API must be flagged with @FlaggedApi: field android.R.attr.allowClearUserDataOnFailedRestore
-UnflaggedApi: android.R.attr#gameSessionService:
- New API must be flagged with @FlaggedApi: field android.R.attr.gameSessionService
-UnflaggedApi: android.R.attr#hotwordDetectionService:
- New API must be flagged with @FlaggedApi: field android.R.attr.hotwordDetectionService
-UnflaggedApi: android.R.attr#isVrOnly:
- New API must be flagged with @FlaggedApi: field android.R.attr.isVrOnly
-UnflaggedApi: android.R.attr#minExtensionVersion:
- New API must be flagged with @FlaggedApi: field android.R.attr.minExtensionVersion
-UnflaggedApi: android.R.attr#playHomeTransitionSound:
- New API must be flagged with @FlaggedApi: field android.R.attr.playHomeTransitionSound
-UnflaggedApi: android.R.attr#requiredSystemPropertyName:
- New API must be flagged with @FlaggedApi: field android.R.attr.requiredSystemPropertyName
-UnflaggedApi: android.R.attr#requiredSystemPropertyValue:
- New API must be flagged with @FlaggedApi: field android.R.attr.requiredSystemPropertyValue
-UnflaggedApi: android.R.attr#sdkVersion:
- New API must be flagged with @FlaggedApi: field android.R.attr.sdkVersion
-UnflaggedApi: android.R.attr#supportsAmbientMode:
- New API must be flagged with @FlaggedApi: field android.R.attr.supportsAmbientMode
-UnflaggedApi: android.R.attr#userRestriction:
- New API must be flagged with @FlaggedApi: field android.R.attr.userRestriction
-UnflaggedApi: android.R.attr#visualQueryDetectionService:
- New API must be flagged with @FlaggedApi: field android.R.attr.visualQueryDetectionService
-UnflaggedApi: android.R.bool#config_enableDefaultNotes:
- New API must be flagged with @FlaggedApi: field android.R.bool.config_enableDefaultNotes
-UnflaggedApi: android.R.bool#config_enableDefaultNotesForWorkProfile:
- New API must be flagged with @FlaggedApi: field android.R.bool.config_enableDefaultNotesForWorkProfile
-UnflaggedApi: android.R.bool#config_enableQrCodeScannerOnLockScreen:
- New API must be flagged with @FlaggedApi: field android.R.bool.config_enableQrCodeScannerOnLockScreen
-UnflaggedApi: android.R.bool#config_safetyProtectionEnabled:
- New API must be flagged with @FlaggedApi: field android.R.bool.config_safetyProtectionEnabled
-UnflaggedApi: android.R.bool#config_sendPackageName:
- New API must be flagged with @FlaggedApi: field android.R.bool.config_sendPackageName
-UnflaggedApi: android.R.bool#config_showDefaultAssistant:
- New API must be flagged with @FlaggedApi: field android.R.bool.config_showDefaultAssistant
-UnflaggedApi: android.R.bool#config_showDefaultEmergency:
- New API must be flagged with @FlaggedApi: field android.R.bool.config_showDefaultEmergency
-UnflaggedApi: android.R.bool#config_showDefaultHome:
- New API must be flagged with @FlaggedApi: field android.R.bool.config_showDefaultHome
-UnflaggedApi: android.R.color#system_notification_accent_color:
- New API must be flagged with @FlaggedApi: field android.R.color.system_notification_accent_color
-UnflaggedApi: android.R.dimen#config_restrictedIconSize:
- New API must be flagged with @FlaggedApi: field android.R.dimen.config_restrictedIconSize
-UnflaggedApi: android.R.dimen#config_viewConfigurationHandwritingGestureLineMargin:
- New API must be flagged with @FlaggedApi: field android.R.dimen.config_viewConfigurationHandwritingGestureLineMargin
-UnflaggedApi: android.R.drawable#ic_info:
- New API must be flagged with @FlaggedApi: field android.R.drawable.ic_info
-UnflaggedApi: android.R.drawable#ic_safety_protection:
- New API must be flagged with @FlaggedApi: field android.R.drawable.ic_safety_protection
-UnflaggedApi: android.R.raw#loaderror:
- New API must be flagged with @FlaggedApi: field android.R.raw.loaderror
-UnflaggedApi: android.R.raw#nodomain:
- New API must be flagged with @FlaggedApi: field android.R.raw.nodomain
-UnflaggedApi: android.R.string#config_customMediaKeyDispatcher:
- New API must be flagged with @FlaggedApi: field android.R.string.config_customMediaKeyDispatcher
-UnflaggedApi: android.R.string#config_customMediaSessionPolicyProvider:
- New API must be flagged with @FlaggedApi: field android.R.string.config_customMediaSessionPolicyProvider
-UnflaggedApi: android.R.string#config_defaultAssistant:
- New API must be flagged with @FlaggedApi: field android.R.string.config_defaultAssistant
-UnflaggedApi: android.R.string#config_defaultAutomotiveNavigation:
- New API must be flagged with @FlaggedApi: field android.R.string.config_defaultAutomotiveNavigation
-UnflaggedApi: android.R.string#config_defaultBrowser:
- New API must be flagged with @FlaggedApi: field android.R.string.config_defaultBrowser
-UnflaggedApi: android.R.string#config_defaultCallRedirection:
- New API must be flagged with @FlaggedApi: field android.R.string.config_defaultCallRedirection
-UnflaggedApi: android.R.string#config_defaultCallScreening:
- New API must be flagged with @FlaggedApi: field android.R.string.config_defaultCallScreening
-UnflaggedApi: android.R.string#config_defaultDialer:
- New API must be flagged with @FlaggedApi: field android.R.string.config_defaultDialer
-UnflaggedApi: android.R.string#config_defaultNotes:
- New API must be flagged with @FlaggedApi: field android.R.string.config_defaultNotes
UnflaggedApi: android.R.string#config_defaultRetailDemo:
New API must be flagged with @FlaggedApi: field android.R.string.config_defaultRetailDemo
-UnflaggedApi: android.R.string#config_defaultSms:
- New API must be flagged with @FlaggedApi: field android.R.string.config_defaultSms
-UnflaggedApi: android.R.string#config_devicePolicyManagement:
- New API must be flagged with @FlaggedApi: field android.R.string.config_devicePolicyManagement
-UnflaggedApi: android.R.string#config_feedbackIntentExtraKey:
- New API must be flagged with @FlaggedApi: field android.R.string.config_feedbackIntentExtraKey
-UnflaggedApi: android.R.string#config_feedbackIntentNameKey:
- New API must be flagged with @FlaggedApi: field android.R.string.config_feedbackIntentNameKey
-UnflaggedApi: android.R.string#config_helpIntentExtraKey:
- New API must be flagged with @FlaggedApi: field android.R.string.config_helpIntentExtraKey
-UnflaggedApi: android.R.string#config_helpIntentNameKey:
- New API must be flagged with @FlaggedApi: field android.R.string.config_helpIntentNameKey
-UnflaggedApi: android.R.string#config_helpPackageNameKey:
- New API must be flagged with @FlaggedApi: field android.R.string.config_helpPackageNameKey
-UnflaggedApi: android.R.string#config_helpPackageNameValue:
- New API must be flagged with @FlaggedApi: field android.R.string.config_helpPackageNameValue
-UnflaggedApi: android.R.string#config_systemActivityRecognizer:
- New API must be flagged with @FlaggedApi: field android.R.string.config_systemActivityRecognizer
-UnflaggedApi: android.R.string#config_systemAmbientAudioIntelligence:
- New API must be flagged with @FlaggedApi: field android.R.string.config_systemAmbientAudioIntelligence
-UnflaggedApi: android.R.string#config_systemAppProtectionService:
- New API must be flagged with @FlaggedApi: field android.R.string.config_systemAppProtectionService
-UnflaggedApi: android.R.string#config_systemAudioIntelligence:
- New API must be flagged with @FlaggedApi: field android.R.string.config_systemAudioIntelligence
-UnflaggedApi: android.R.string#config_systemAutomotiveCalendarSyncManager:
- New API must be flagged with @FlaggedApi: field android.R.string.config_systemAutomotiveCalendarSyncManager
-UnflaggedApi: android.R.string#config_systemAutomotiveCluster:
- New API must be flagged with @FlaggedApi: field android.R.string.config_systemAutomotiveCluster
-UnflaggedApi: android.R.string#config_systemAutomotiveProjection:
- New API must be flagged with @FlaggedApi: field android.R.string.config_systemAutomotiveProjection
-UnflaggedApi: android.R.string#config_systemCallStreaming:
- New API must be flagged with @FlaggedApi: field android.R.string.config_systemCallStreaming
-UnflaggedApi: android.R.string#config_systemCompanionDeviceProvider:
- New API must be flagged with @FlaggedApi: field android.R.string.config_systemCompanionDeviceProvider
-UnflaggedApi: android.R.string#config_systemContacts:
- New API must be flagged with @FlaggedApi: field android.R.string.config_systemContacts
-UnflaggedApi: android.R.string#config_systemFinancedDeviceController:
- New API must be flagged with @FlaggedApi: field android.R.string.config_systemFinancedDeviceController
-UnflaggedApi: android.R.string#config_systemGallery:
- New API must be flagged with @FlaggedApi: field android.R.string.config_systemGallery
-UnflaggedApi: android.R.string#config_systemNotificationIntelligence:
- New API must be flagged with @FlaggedApi: field android.R.string.config_systemNotificationIntelligence
-UnflaggedApi: android.R.string#config_systemSettingsIntelligence:
- New API must be flagged with @FlaggedApi: field android.R.string.config_systemSettingsIntelligence
-UnflaggedApi: android.R.string#config_systemShell:
- New API must be flagged with @FlaggedApi: field android.R.string.config_systemShell
-UnflaggedApi: android.R.string#config_systemSpeechRecognizer:
- New API must be flagged with @FlaggedApi: field android.R.string.config_systemSpeechRecognizer
-UnflaggedApi: android.R.string#config_systemSupervision:
- New API must be flagged with @FlaggedApi: field android.R.string.config_systemSupervision
-UnflaggedApi: android.R.string#config_systemTelevisionNotificationHandler:
- New API must be flagged with @FlaggedApi: field android.R.string.config_systemTelevisionNotificationHandler
-UnflaggedApi: android.R.string#config_systemTextIntelligence:
- New API must be flagged with @FlaggedApi: field android.R.string.config_systemTextIntelligence
-UnflaggedApi: android.R.string#config_systemUi:
- New API must be flagged with @FlaggedApi: field android.R.string.config_systemUi
-UnflaggedApi: android.R.string#config_systemUiIntelligence:
- New API must be flagged with @FlaggedApi: field android.R.string.config_systemUiIntelligence
-UnflaggedApi: android.R.string#config_systemVisualIntelligence:
- New API must be flagged with @FlaggedApi: field android.R.string.config_systemVisualIntelligence
-UnflaggedApi: android.R.string#config_systemWearHealthService:
- New API must be flagged with @FlaggedApi: field android.R.string.config_systemWearHealthService
-UnflaggedApi: android.R.string#config_systemWellbeing:
- New API must be flagged with @FlaggedApi: field android.R.string.config_systemWellbeing
-UnflaggedApi: android.R.string#config_systemWifiCoexManager:
- New API must be flagged with @FlaggedApi: field android.R.string.config_systemWifiCoexManager
-UnflaggedApi: android.R.string#safety_protection_display_text:
- New API must be flagged with @FlaggedApi: field android.R.string.safety_protection_display_text
-UnflaggedApi: android.R.style#Theme_DeviceDefault_DocumentsUI:
- New API must be flagged with @FlaggedApi: field android.R.style.Theme_DeviceDefault_DocumentsUI
-UnflaggedApi: android.R.style#Theme_Leanback_FormWizard:
- New API must be flagged with @FlaggedApi: field android.R.style.Theme_Leanback_FormWizard
UnflaggedApi: android.app.ActivityManager#getExternalHistoricalProcessStartReasons(String, int):
New API must be flagged with @FlaggedApi: method android.app.ActivityManager.getExternalHistoricalProcessStartReasons(String,int)
UnflaggedApi: android.app.AppOpsManager#OPSTR_RECEIVE_SANDBOX_TRIGGER_AUDIO:
New API must be flagged with @FlaggedApi: field android.app.AppOpsManager.OPSTR_RECEIVE_SANDBOX_TRIGGER_AUDIO
-UnflaggedApi: android.app.AppOpsManager.AttributedHistoricalOps#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.AppOpsManager.AttributedHistoricalOps.equals(Object)
-UnflaggedApi: android.app.AppOpsManager.AttributedHistoricalOps#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.AppOpsManager.AttributedHistoricalOps.hashCode()
-UnflaggedApi: android.app.AppOpsManager.HistoricalOp#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.AppOpsManager.HistoricalOp.equals(Object)
-UnflaggedApi: android.app.AppOpsManager.HistoricalOp#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.AppOpsManager.HistoricalOp.hashCode()
-UnflaggedApi: android.app.AppOpsManager.HistoricalOps#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.AppOpsManager.HistoricalOps.equals(Object)
-UnflaggedApi: android.app.AppOpsManager.HistoricalOps#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.AppOpsManager.HistoricalOps.hashCode()
-UnflaggedApi: android.app.AppOpsManager.HistoricalOps#toString():
- New API must be flagged with @FlaggedApi: method android.app.AppOpsManager.HistoricalOps.toString()
-UnflaggedApi: android.app.AppOpsManager.HistoricalPackageOps#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.AppOpsManager.HistoricalPackageOps.equals(Object)
-UnflaggedApi: android.app.AppOpsManager.HistoricalPackageOps#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.AppOpsManager.HistoricalPackageOps.hashCode()
-UnflaggedApi: android.app.AppOpsManager.HistoricalUidOps#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.AppOpsManager.HistoricalUidOps.equals(Object)
-UnflaggedApi: android.app.AppOpsManager.HistoricalUidOps#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.AppOpsManager.HistoricalUidOps.hashCode()
-UnflaggedApi: android.app.GameModeConfiguration#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.GameModeConfiguration.equals(Object)
-UnflaggedApi: android.app.GameModeConfiguration#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.GameModeConfiguration.hashCode()
-UnflaggedApi: android.app.StatusBarManager.DisableInfo#toString():
- New API must be flagged with @FlaggedApi: method android.app.StatusBarManager.DisableInfo.toString()
-UnflaggedApi: android.app.Vr2dDisplayProperties#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.Vr2dDisplayProperties.equals(Object)
-UnflaggedApi: android.app.Vr2dDisplayProperties#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.Vr2dDisplayProperties.hashCode()
-UnflaggedApi: android.app.Vr2dDisplayProperties#toString():
- New API must be flagged with @FlaggedApi: method android.app.Vr2dDisplayProperties.toString()
-UnflaggedApi: android.app.admin.AccountTypePolicyKey#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.admin.AccountTypePolicyKey.equals(Object)
-UnflaggedApi: android.app.admin.AccountTypePolicyKey#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.admin.AccountTypePolicyKey.hashCode()
-UnflaggedApi: android.app.admin.AccountTypePolicyKey#toString():
- New API must be flagged with @FlaggedApi: method android.app.admin.AccountTypePolicyKey.toString()
-UnflaggedApi: android.app.admin.Authority#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.admin.Authority.equals(Object)
-UnflaggedApi: android.app.admin.Authority#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.admin.Authority.hashCode()
-UnflaggedApi: android.app.admin.DeviceAdminAuthority#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.admin.DeviceAdminAuthority.equals(Object)
-UnflaggedApi: android.app.admin.DeviceAdminAuthority#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.admin.DeviceAdminAuthority.hashCode()
-UnflaggedApi: android.app.admin.DeviceAdminAuthority#toString():
- New API must be flagged with @FlaggedApi: method android.app.admin.DeviceAdminAuthority.toString()
-UnflaggedApi: android.app.admin.DevicePolicyDrawableResource#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.admin.DevicePolicyDrawableResource.equals(Object)
-UnflaggedApi: android.app.admin.DevicePolicyDrawableResource#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.admin.DevicePolicyDrawableResource.hashCode()
-UnflaggedApi: android.app.admin.DevicePolicyKeyguardService#onDestroy():
- New API must be flagged with @FlaggedApi: method android.app.admin.DevicePolicyKeyguardService.onDestroy()
-UnflaggedApi: android.app.admin.DevicePolicyResources.Strings:
- New API must be flagged with @FlaggedApi: class android.app.admin.DevicePolicyResources.Strings
-UnflaggedApi: android.app.admin.DevicePolicyResources.Strings.DefaultAppSettings:
- New API must be flagged with @FlaggedApi: class android.app.admin.DevicePolicyResources.Strings.DefaultAppSettings
-UnflaggedApi: android.app.admin.DevicePolicyResources.Strings.DefaultAppSettings#HOME_MISSING_WORK_PROFILE_SUPPORT_MESSAGE:
- New API must be flagged with @FlaggedApi: field android.app.admin.DevicePolicyResources.Strings.DefaultAppSettings.HOME_MISSING_WORK_PROFILE_SUPPORT_MESSAGE
-UnflaggedApi: android.app.admin.DevicePolicyResources.Strings.DefaultAppSettings#WORK_PROFILE_DEFAULT_APPS_TITLE:
- New API must be flagged with @FlaggedApi: field android.app.admin.DevicePolicyResources.Strings.DefaultAppSettings.WORK_PROFILE_DEFAULT_APPS_TITLE
-UnflaggedApi: android.app.admin.DevicePolicyResources.Strings.PermissionSettings:
- New API must be flagged with @FlaggedApi: class android.app.admin.DevicePolicyResources.Strings.PermissionSettings
-UnflaggedApi: android.app.admin.DevicePolicyResources.Strings.PermissionSettings#BACKGROUND_ACCESS_DISABLED_BY_ADMIN_MESSAGE:
- New API must be flagged with @FlaggedApi: field android.app.admin.DevicePolicyResources.Strings.PermissionSettings.BACKGROUND_ACCESS_DISABLED_BY_ADMIN_MESSAGE
-UnflaggedApi: android.app.admin.DevicePolicyResources.Strings.PermissionSettings#BACKGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE:
- New API must be flagged with @FlaggedApi: field android.app.admin.DevicePolicyResources.Strings.PermissionSettings.BACKGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE
-UnflaggedApi: android.app.admin.DevicePolicyResources.Strings.PermissionSettings#FOREGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE:
- New API must be flagged with @FlaggedApi: field android.app.admin.DevicePolicyResources.Strings.PermissionSettings.FOREGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE
-UnflaggedApi: android.app.admin.DevicePolicyResources.Strings.PermissionSettings#LOCATION_AUTO_GRANTED_MESSAGE:
- New API must be flagged with @FlaggedApi: field android.app.admin.DevicePolicyResources.Strings.PermissionSettings.LOCATION_AUTO_GRANTED_MESSAGE
-UnflaggedApi: android.app.admin.DevicePolicyState#toString():
- New API must be flagged with @FlaggedApi: method android.app.admin.DevicePolicyState.toString()
-UnflaggedApi: android.app.admin.DevicePolicyStringResource#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.admin.DevicePolicyStringResource.equals(Object)
-UnflaggedApi: android.app.admin.DevicePolicyStringResource#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.admin.DevicePolicyStringResource.hashCode()
-UnflaggedApi: android.app.admin.DpcAuthority#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.admin.DpcAuthority.equals(Object)
-UnflaggedApi: android.app.admin.DpcAuthority#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.admin.DpcAuthority.hashCode()
-UnflaggedApi: android.app.admin.DpcAuthority#toString():
- New API must be flagged with @FlaggedApi: method android.app.admin.DpcAuthority.toString()
-UnflaggedApi: android.app.admin.EnforcingAdmin#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.admin.EnforcingAdmin.equals(Object)
-UnflaggedApi: android.app.admin.EnforcingAdmin#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.admin.EnforcingAdmin.hashCode()
-UnflaggedApi: android.app.admin.EnforcingAdmin#toString():
- New API must be flagged with @FlaggedApi: method android.app.admin.EnforcingAdmin.toString()
-UnflaggedApi: android.app.admin.IntentFilterPolicyKey#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.admin.IntentFilterPolicyKey.equals(Object)
-UnflaggedApi: android.app.admin.IntentFilterPolicyKey#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.admin.IntentFilterPolicyKey.hashCode()
-UnflaggedApi: android.app.admin.IntentFilterPolicyKey#toString():
- New API must be flagged with @FlaggedApi: method android.app.admin.IntentFilterPolicyKey.toString()
-UnflaggedApi: android.app.admin.LockTaskPolicy#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.admin.LockTaskPolicy.equals(Object)
-UnflaggedApi: android.app.admin.LockTaskPolicy#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.admin.LockTaskPolicy.hashCode()
-UnflaggedApi: android.app.admin.LockTaskPolicy#toString():
- New API must be flagged with @FlaggedApi: method android.app.admin.LockTaskPolicy.toString()
-UnflaggedApi: android.app.admin.NoArgsPolicyKey#toString():
- New API must be flagged with @FlaggedApi: method android.app.admin.NoArgsPolicyKey.toString()
-UnflaggedApi: android.app.admin.PackagePermissionPolicyKey#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.admin.PackagePermissionPolicyKey.equals(Object)
-UnflaggedApi: android.app.admin.PackagePermissionPolicyKey#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.admin.PackagePermissionPolicyKey.hashCode()
-UnflaggedApi: android.app.admin.PackagePermissionPolicyKey#toString():
- New API must be flagged with @FlaggedApi: method android.app.admin.PackagePermissionPolicyKey.toString()
-UnflaggedApi: android.app.admin.PackagePolicyKey#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.admin.PackagePolicyKey.equals(Object)
-UnflaggedApi: android.app.admin.PackagePolicyKey#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.admin.PackagePolicyKey.hashCode()
-UnflaggedApi: android.app.admin.PackagePolicyKey#toString():
- New API must be flagged with @FlaggedApi: method android.app.admin.PackagePolicyKey.toString()
-UnflaggedApi: android.app.admin.PolicyKey#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.admin.PolicyKey.equals(Object)
-UnflaggedApi: android.app.admin.PolicyKey#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.admin.PolicyKey.hashCode()
-UnflaggedApi: android.app.admin.PolicyState#toString():
- New API must be flagged with @FlaggedApi: method android.app.admin.PolicyState.toString()
-UnflaggedApi: android.app.admin.RoleAuthority#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.admin.RoleAuthority.equals(Object)
-UnflaggedApi: android.app.admin.RoleAuthority#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.admin.RoleAuthority.hashCode()
-UnflaggedApi: android.app.admin.RoleAuthority#toString():
- New API must be flagged with @FlaggedApi: method android.app.admin.RoleAuthority.toString()
-UnflaggedApi: android.app.admin.UnknownAuthority#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.admin.UnknownAuthority.equals(Object)
-UnflaggedApi: android.app.admin.UnknownAuthority#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.admin.UnknownAuthority.hashCode()
-UnflaggedApi: android.app.admin.UnknownAuthority#toString():
- New API must be flagged with @FlaggedApi: method android.app.admin.UnknownAuthority.toString()
-UnflaggedApi: android.app.admin.UserRestrictionPolicyKey#toString():
- New API must be flagged with @FlaggedApi: method android.app.admin.UserRestrictionPolicyKey.toString()
-UnflaggedApi: android.app.ambientcontext.AmbientContextEvent#toString():
- New API must be flagged with @FlaggedApi: method android.app.ambientcontext.AmbientContextEvent.toString()
-UnflaggedApi: android.app.ambientcontext.AmbientContextEventRequest#toString():
- New API must be flagged with @FlaggedApi: method android.app.ambientcontext.AmbientContextEventRequest.toString()
-UnflaggedApi: android.app.assist.ActivityId#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.assist.ActivityId.equals(Object)
-UnflaggedApi: android.app.assist.ActivityId#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.assist.ActivityId.hashCode()
-UnflaggedApi: android.app.assist.ActivityId#toString():
- New API must be flagged with @FlaggedApi: method android.app.assist.ActivityId.toString()
-UnflaggedApi: android.app.assist.AssistStructure.ViewNode#ViewNode():
- New API must be flagged with @FlaggedApi: constructor android.app.assist.AssistStructure.ViewNode()
-UnflaggedApi: android.app.backup.RestoreDescription#toString():
- New API must be flagged with @FlaggedApi: method android.app.backup.RestoreDescription.toString()
-UnflaggedApi: android.app.cloudsearch.SearchRequest#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.cloudsearch.SearchRequest.equals(Object)
-UnflaggedApi: android.app.cloudsearch.SearchRequest#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.cloudsearch.SearchRequest.hashCode()
-UnflaggedApi: android.app.cloudsearch.SearchRequest#toString():
- New API must be flagged with @FlaggedApi: method android.app.cloudsearch.SearchRequest.toString()
-UnflaggedApi: android.app.cloudsearch.SearchResponse#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.cloudsearch.SearchResponse.equals(Object)
-UnflaggedApi: android.app.cloudsearch.SearchResponse#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.cloudsearch.SearchResponse.hashCode()
-UnflaggedApi: android.app.cloudsearch.SearchResult#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.cloudsearch.SearchResult.equals(Object)
-UnflaggedApi: android.app.cloudsearch.SearchResult#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.cloudsearch.SearchResult.hashCode()
-UnflaggedApi: android.app.prediction.AppPredictionContext#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.prediction.AppPredictionContext.equals(Object)
-UnflaggedApi: android.app.prediction.AppPredictionSessionId#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.prediction.AppPredictionSessionId.equals(Object)
-UnflaggedApi: android.app.prediction.AppPredictionSessionId#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.prediction.AppPredictionSessionId.hashCode()
-UnflaggedApi: android.app.prediction.AppPredictionSessionId#toString():
- New API must be flagged with @FlaggedApi: method android.app.prediction.AppPredictionSessionId.toString()
-UnflaggedApi: android.app.prediction.AppPredictor#finalize():
- New API must be flagged with @FlaggedApi: method android.app.prediction.AppPredictor.finalize()
-UnflaggedApi: android.app.prediction.AppTarget#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.prediction.AppTarget.equals(Object)
-UnflaggedApi: android.app.prediction.AppTargetEvent#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.prediction.AppTargetEvent.equals(Object)
-UnflaggedApi: android.app.prediction.AppTargetId#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.prediction.AppTargetId.equals(Object)
-UnflaggedApi: android.app.prediction.AppTargetId#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.prediction.AppTargetId.hashCode()
-UnflaggedApi: android.app.search.SearchAction#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.search.SearchAction.equals(Object)
-UnflaggedApi: android.app.search.SearchAction#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.search.SearchAction.hashCode()
-UnflaggedApi: android.app.search.SearchAction#toString():
- New API must be flagged with @FlaggedApi: method android.app.search.SearchAction.toString()
-UnflaggedApi: android.app.search.SearchSessionId#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.search.SearchSessionId.equals(Object)
-UnflaggedApi: android.app.search.SearchSessionId#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.search.SearchSessionId.hashCode()
-UnflaggedApi: android.app.search.SearchSessionId#toString():
- New API must be flagged with @FlaggedApi: method android.app.search.SearchSessionId.toString()
-UnflaggedApi: android.app.search.SearchTargetEvent#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.search.SearchTargetEvent.equals(Object)
-UnflaggedApi: android.app.search.SearchTargetEvent#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.search.SearchTargetEvent.hashCode()
-UnflaggedApi: android.app.smartspace.SmartspaceAction#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.smartspace.SmartspaceAction.equals(Object)
-UnflaggedApi: android.app.smartspace.SmartspaceAction#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.SmartspaceAction.hashCode()
-UnflaggedApi: android.app.smartspace.SmartspaceAction#toString():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.SmartspaceAction.toString()
-UnflaggedApi: android.app.smartspace.SmartspaceConfig#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.smartspace.SmartspaceConfig.equals(Object)
-UnflaggedApi: android.app.smartspace.SmartspaceConfig#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.SmartspaceConfig.hashCode()
-UnflaggedApi: android.app.smartspace.SmartspaceSessionId#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.smartspace.SmartspaceSessionId.equals(Object)
-UnflaggedApi: android.app.smartspace.SmartspaceSessionId#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.SmartspaceSessionId.hashCode()
-UnflaggedApi: android.app.smartspace.SmartspaceSessionId#toString():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.SmartspaceSessionId.toString()
-UnflaggedApi: android.app.smartspace.SmartspaceTarget#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.smartspace.SmartspaceTarget.equals(Object)
-UnflaggedApi: android.app.smartspace.SmartspaceTarget#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.SmartspaceTarget.hashCode()
-UnflaggedApi: android.app.smartspace.SmartspaceTarget#toString():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.SmartspaceTarget.toString()
-UnflaggedApi: android.app.smartspace.SmartspaceTargetEvent#toString():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.SmartspaceTargetEvent.toString()
-UnflaggedApi: android.app.smartspace.uitemplatedata.BaseTemplateData#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.BaseTemplateData.equals(Object)
-UnflaggedApi: android.app.smartspace.uitemplatedata.BaseTemplateData#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.BaseTemplateData.hashCode()
-UnflaggedApi: android.app.smartspace.uitemplatedata.BaseTemplateData#toString():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.BaseTemplateData.toString()
-UnflaggedApi: android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemInfo#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemInfo.equals(Object)
-UnflaggedApi: android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemInfo#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemInfo.hashCode()
-UnflaggedApi: android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemInfo#toString():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemInfo.toString()
-UnflaggedApi: android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemLoggingInfo#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemLoggingInfo.equals(Object)
-UnflaggedApi: android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemLoggingInfo#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemLoggingInfo.hashCode()
-UnflaggedApi: android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemLoggingInfo#toString():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemLoggingInfo.toString()
-UnflaggedApi: android.app.smartspace.uitemplatedata.CarouselTemplateData#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.CarouselTemplateData.equals(Object)
-UnflaggedApi: android.app.smartspace.uitemplatedata.CarouselTemplateData#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.CarouselTemplateData.hashCode()
-UnflaggedApi: android.app.smartspace.uitemplatedata.CarouselTemplateData#toString():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.CarouselTemplateData.toString()
-UnflaggedApi: android.app.smartspace.uitemplatedata.CarouselTemplateData.CarouselItem#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.CarouselTemplateData.CarouselItem.equals(Object)
-UnflaggedApi: android.app.smartspace.uitemplatedata.CarouselTemplateData.CarouselItem#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.CarouselTemplateData.CarouselItem.hashCode()
-UnflaggedApi: android.app.smartspace.uitemplatedata.CarouselTemplateData.CarouselItem#toString():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.CarouselTemplateData.CarouselItem.toString()
-UnflaggedApi: android.app.smartspace.uitemplatedata.CombinedCardsTemplateData#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.CombinedCardsTemplateData.equals(Object)
-UnflaggedApi: android.app.smartspace.uitemplatedata.CombinedCardsTemplateData#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.CombinedCardsTemplateData.hashCode()
-UnflaggedApi: android.app.smartspace.uitemplatedata.CombinedCardsTemplateData#toString():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.CombinedCardsTemplateData.toString()
-UnflaggedApi: android.app.smartspace.uitemplatedata.HeadToHeadTemplateData#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.HeadToHeadTemplateData.equals(Object)
-UnflaggedApi: android.app.smartspace.uitemplatedata.HeadToHeadTemplateData#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.HeadToHeadTemplateData.hashCode()
-UnflaggedApi: android.app.smartspace.uitemplatedata.HeadToHeadTemplateData#toString():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.HeadToHeadTemplateData.toString()
-UnflaggedApi: android.app.smartspace.uitemplatedata.Icon#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.Icon.equals(Object)
-UnflaggedApi: android.app.smartspace.uitemplatedata.Icon#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.Icon.hashCode()
-UnflaggedApi: android.app.smartspace.uitemplatedata.Icon#toString():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.Icon.toString()
-UnflaggedApi: android.app.smartspace.uitemplatedata.SubCardTemplateData#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.SubCardTemplateData.equals(Object)
-UnflaggedApi: android.app.smartspace.uitemplatedata.SubCardTemplateData#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.SubCardTemplateData.hashCode()
-UnflaggedApi: android.app.smartspace.uitemplatedata.SubCardTemplateData#toString():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.SubCardTemplateData.toString()
-UnflaggedApi: android.app.smartspace.uitemplatedata.SubImageTemplateData#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.SubImageTemplateData.equals(Object)
-UnflaggedApi: android.app.smartspace.uitemplatedata.SubImageTemplateData#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.SubImageTemplateData.hashCode()
-UnflaggedApi: android.app.smartspace.uitemplatedata.SubImageTemplateData#toString():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.SubImageTemplateData.toString()
-UnflaggedApi: android.app.smartspace.uitemplatedata.SubListTemplateData#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.SubListTemplateData.equals(Object)
-UnflaggedApi: android.app.smartspace.uitemplatedata.SubListTemplateData#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.SubListTemplateData.hashCode()
-UnflaggedApi: android.app.smartspace.uitemplatedata.SubListTemplateData#toString():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.SubListTemplateData.toString()
-UnflaggedApi: android.app.smartspace.uitemplatedata.TapAction#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.TapAction.equals(Object)
-UnflaggedApi: android.app.smartspace.uitemplatedata.TapAction#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.TapAction.hashCode()
-UnflaggedApi: android.app.smartspace.uitemplatedata.TapAction#toString():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.TapAction.toString()
-UnflaggedApi: android.app.smartspace.uitemplatedata.Text#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.Text.equals(Object)
-UnflaggedApi: android.app.smartspace.uitemplatedata.Text#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.Text.hashCode()
-UnflaggedApi: android.app.smartspace.uitemplatedata.Text#toString():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.Text.toString()
-UnflaggedApi: android.app.time.ExternalTimeSuggestion#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.time.ExternalTimeSuggestion.equals(Object)
-UnflaggedApi: android.app.time.ExternalTimeSuggestion#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.time.ExternalTimeSuggestion.hashCode()
-UnflaggedApi: android.app.time.ExternalTimeSuggestion#toString():
- New API must be flagged with @FlaggedApi: method android.app.time.ExternalTimeSuggestion.toString()
-UnflaggedApi: android.app.time.TimeCapabilities#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.time.TimeCapabilities.equals(Object)
-UnflaggedApi: android.app.time.TimeCapabilities#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.time.TimeCapabilities.hashCode()
-UnflaggedApi: android.app.time.TimeCapabilities#toString():
- New API must be flagged with @FlaggedApi: method android.app.time.TimeCapabilities.toString()
-UnflaggedApi: android.app.time.TimeCapabilitiesAndConfig#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.time.TimeCapabilitiesAndConfig.equals(Object)
-UnflaggedApi: android.app.time.TimeCapabilitiesAndConfig#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.time.TimeCapabilitiesAndConfig.hashCode()
-UnflaggedApi: android.app.time.TimeCapabilitiesAndConfig#toString():
- New API must be flagged with @FlaggedApi: method android.app.time.TimeCapabilitiesAndConfig.toString()
-UnflaggedApi: android.app.time.TimeConfiguration#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.time.TimeConfiguration.equals(Object)
-UnflaggedApi: android.app.time.TimeConfiguration#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.time.TimeConfiguration.hashCode()
-UnflaggedApi: android.app.time.TimeConfiguration#toString():
- New API must be flagged with @FlaggedApi: method android.app.time.TimeConfiguration.toString()
-UnflaggedApi: android.app.time.TimeState#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.time.TimeState.equals(Object)
-UnflaggedApi: android.app.time.TimeState#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.time.TimeState.hashCode()
-UnflaggedApi: android.app.time.TimeState#toString():
- New API must be flagged with @FlaggedApi: method android.app.time.TimeState.toString()
-UnflaggedApi: android.app.time.TimeZoneCapabilities#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.time.TimeZoneCapabilities.equals(Object)
-UnflaggedApi: android.app.time.TimeZoneCapabilities#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.time.TimeZoneCapabilities.hashCode()
-UnflaggedApi: android.app.time.TimeZoneCapabilities#toString():
- New API must be flagged with @FlaggedApi: method android.app.time.TimeZoneCapabilities.toString()
-UnflaggedApi: android.app.time.TimeZoneCapabilitiesAndConfig#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.time.TimeZoneCapabilitiesAndConfig.equals(Object)
-UnflaggedApi: android.app.time.TimeZoneCapabilitiesAndConfig#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.time.TimeZoneCapabilitiesAndConfig.hashCode()
-UnflaggedApi: android.app.time.TimeZoneCapabilitiesAndConfig#toString():
- New API must be flagged with @FlaggedApi: method android.app.time.TimeZoneCapabilitiesAndConfig.toString()
-UnflaggedApi: android.app.time.TimeZoneConfiguration#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.time.TimeZoneConfiguration.equals(Object)
-UnflaggedApi: android.app.time.TimeZoneConfiguration#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.time.TimeZoneConfiguration.hashCode()
-UnflaggedApi: android.app.time.TimeZoneConfiguration#toString():
- New API must be flagged with @FlaggedApi: method android.app.time.TimeZoneConfiguration.toString()
-UnflaggedApi: android.app.time.TimeZoneState#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.time.TimeZoneState.equals(Object)
-UnflaggedApi: android.app.time.TimeZoneState#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.time.TimeZoneState.hashCode()
-UnflaggedApi: android.app.time.TimeZoneState#toString():
- New API must be flagged with @FlaggedApi: method android.app.time.TimeZoneState.toString()
-UnflaggedApi: android.app.time.UnixEpochTime#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.time.UnixEpochTime.equals(Object)
-UnflaggedApi: android.app.time.UnixEpochTime#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.time.UnixEpochTime.hashCode()
-UnflaggedApi: android.app.time.UnixEpochTime#toString():
- New API must be flagged with @FlaggedApi: method android.app.time.UnixEpochTime.toString()
-UnflaggedApi: android.app.usage.BroadcastResponseStats#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.usage.BroadcastResponseStats.equals(Object)
-UnflaggedApi: android.app.usage.BroadcastResponseStats#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.usage.BroadcastResponseStats.hashCode()
-UnflaggedApi: android.app.usage.BroadcastResponseStats#toString():
- New API must be flagged with @FlaggedApi: method android.app.usage.BroadcastResponseStats.toString()
-UnflaggedApi: android.app.usage.CacheQuotaHint#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.usage.CacheQuotaHint.equals(Object)
-UnflaggedApi: android.app.usage.CacheQuotaHint#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.usage.CacheQuotaHint.hashCode()
-UnflaggedApi: android.app.usage.CacheQuotaService#onCreate():
- New API must be flagged with @FlaggedApi: method android.app.usage.CacheQuotaService.onCreate()
-UnflaggedApi: android.app.usage.UsageEvents.Event#NOTIFICATION_INTERRUPTION:
- New API must be flagged with @FlaggedApi: field android.app.usage.UsageEvents.Event.NOTIFICATION_INTERRUPTION
-UnflaggedApi: android.app.usage.UsageEvents.Event#NOTIFICATION_SEEN:
- New API must be flagged with @FlaggedApi: field android.app.usage.UsageEvents.Event.NOTIFICATION_SEEN
-UnflaggedApi: android.app.usage.UsageEvents.Event#SLICE_PINNED:
- New API must be flagged with @FlaggedApi: field android.app.usage.UsageEvents.Event.SLICE_PINNED
-UnflaggedApi: android.app.usage.UsageEvents.Event#SLICE_PINNED_PRIV:
- New API must be flagged with @FlaggedApi: field android.app.usage.UsageEvents.Event.SLICE_PINNED_PRIV
-UnflaggedApi: android.app.usage.UsageEvents.Event#SYSTEM_INTERACTION:
- New API must be flagged with @FlaggedApi: field android.app.usage.UsageEvents.Event.SYSTEM_INTERACTION
-UnflaggedApi: android.app.usage.UsageEvents.Event#getInstanceId():
- New API must be flagged with @FlaggedApi: method android.app.usage.UsageEvents.Event.getInstanceId()
-UnflaggedApi: android.app.usage.UsageEvents.Event#getNotificationChannelId():
- New API must be flagged with @FlaggedApi: method android.app.usage.UsageEvents.Event.getNotificationChannelId()
-UnflaggedApi: android.app.usage.UsageEvents.Event#getTaskRootClassName():
- New API must be flagged with @FlaggedApi: method android.app.usage.UsageEvents.Event.getTaskRootClassName()
-UnflaggedApi: android.app.usage.UsageEvents.Event#getTaskRootPackageName():
- New API must be flagged with @FlaggedApi: method android.app.usage.UsageEvents.Event.getTaskRootPackageName()
-UnflaggedApi: android.app.usage.UsageEvents.Event#isInstantApp():
- New API must be flagged with @FlaggedApi: method android.app.usage.UsageEvents.Event.isInstantApp()
-UnflaggedApi: android.app.wallpapereffectsgeneration.CinematicEffectRequest#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.wallpapereffectsgeneration.CinematicEffectRequest.equals(Object)
-UnflaggedApi: android.app.wallpapereffectsgeneration.CinematicEffectRequest#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.wallpapereffectsgeneration.CinematicEffectRequest.hashCode()
-UnflaggedApi: android.app.wallpapereffectsgeneration.CinematicEffectResponse#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.wallpapereffectsgeneration.CinematicEffectResponse.equals(Object)
-UnflaggedApi: android.app.wallpapereffectsgeneration.CinematicEffectResponse#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.wallpapereffectsgeneration.CinematicEffectResponse.hashCode()
UnflaggedApi: android.companion.virtual.VirtualDeviceManager.VirtualDevice#getPersistentDeviceId():
New API must be flagged with @FlaggedApi: method android.companion.virtual.VirtualDeviceManager.VirtualDevice.getPersistentDeviceId()
-UnflaggedApi: android.companion.virtual.VirtualDeviceParams#equals(Object):
- New API must be flagged with @FlaggedApi: method android.companion.virtual.VirtualDeviceParams.equals(Object)
-UnflaggedApi: android.companion.virtual.VirtualDeviceParams#hashCode():
- New API must be flagged with @FlaggedApi: method android.companion.virtual.VirtualDeviceParams.hashCode()
-UnflaggedApi: android.companion.virtual.VirtualDeviceParams#toString():
- New API must be flagged with @FlaggedApi: method android.companion.virtual.VirtualDeviceParams.toString()
-UnflaggedApi: android.companion.virtual.sensor.VirtualSensorConfig#toString():
- New API must be flagged with @FlaggedApi: method android.companion.virtual.sensor.VirtualSensorConfig.toString()
+UnflaggedApi: android.content.Context#THREAD_NETWORK_SERVICE:
+ New API must be flagged with @FlaggedApi: field android.content.Context.THREAD_NETWORK_SERVICE
UnflaggedApi: android.content.Intent#ACTION_UNARCHIVE_PACKAGE:
New API must be flagged with @FlaggedApi: field android.content.Intent.ACTION_UNARCHIVE_PACKAGE
-UnflaggedApi: android.content.integrity.Rule#equals(Object):
- New API must be flagged with @FlaggedApi: method android.content.integrity.Rule.equals(Object)
-UnflaggedApi: android.content.integrity.Rule#hashCode():
- New API must be flagged with @FlaggedApi: method android.content.integrity.Rule.hashCode()
-UnflaggedApi: android.content.integrity.Rule#toString():
- New API must be flagged with @FlaggedApi: method android.content.integrity.Rule.toString()
-UnflaggedApi: android.content.pm.PackageArchiver:
- New API must be flagged with @FlaggedApi: class android.content.pm.PackageArchiver
-UnflaggedApi: android.content.pm.PackageArchiver#EXTRA_UNARCHIVE_ALL_USERS:
- New API must be flagged with @FlaggedApi: field android.content.pm.PackageArchiver.EXTRA_UNARCHIVE_ALL_USERS
-UnflaggedApi: android.content.pm.PackageArchiver#EXTRA_UNARCHIVE_PACKAGE_NAME:
- New API must be flagged with @FlaggedApi: field android.content.pm.PackageArchiver.EXTRA_UNARCHIVE_PACKAGE_NAME
-UnflaggedApi: android.content.pm.PackageArchiver#requestArchive(String, android.content.IntentSender):
- New API must be flagged with @FlaggedApi: method android.content.pm.PackageArchiver.requestArchive(String,android.content.IntentSender)
-UnflaggedApi: android.content.pm.PackageArchiver#requestUnarchive(String):
- New API must be flagged with @FlaggedApi: method android.content.pm.PackageArchiver.requestUnarchive(String)
-UnflaggedApi: android.content.pm.PackageInfo#isArchived:
- New API must be flagged with @FlaggedApi: field android.content.pm.PackageInfo.isArchived
UnflaggedApi: android.content.pm.PackageInstaller#readInstallInfo(android.os.ParcelFileDescriptor, String, int):
New API must be flagged with @FlaggedApi: method android.content.pm.PackageInstaller.readInstallInfo(android.os.ParcelFileDescriptor,String,int)
UnflaggedApi: android.content.pm.PackageInstaller.InstallInfo#calculateInstalledSize(android.content.pm.PackageInstaller.SessionParams, android.os.ParcelFileDescriptor):
@@ -1579,320 +233,6 @@
New API must be flagged with @FlaggedApi: field android.content.pm.PackageManager.EXTRA_REQUEST_PERMISSIONS_DEVICE_ID
UnflaggedApi: android.content.pm.PackageManager#MATCH_ARCHIVED_PACKAGES:
New API must be flagged with @FlaggedApi: field android.content.pm.PackageManager.MATCH_ARCHIVED_PACKAGES
-UnflaggedApi: android.content.pm.PackageManager#getPackageArchiver():
- New API must be flagged with @FlaggedApi: method android.content.pm.PackageManager.getPackageArchiver()
-UnflaggedApi: android.content.pm.SuspendDialogInfo#equals(Object):
- New API must be flagged with @FlaggedApi: method android.content.pm.SuspendDialogInfo.equals(Object)
-UnflaggedApi: android.content.pm.SuspendDialogInfo#hashCode():
- New API must be flagged with @FlaggedApi: method android.content.pm.SuspendDialogInfo.hashCode()
-UnflaggedApi: android.content.pm.SuspendDialogInfo#toString():
- New API must be flagged with @FlaggedApi: method android.content.pm.SuspendDialogInfo.toString()
-UnflaggedApi: android.content.pm.UserProperties#toString():
- New API must be flagged with @FlaggedApi: method android.content.pm.UserProperties.toString()
-UnflaggedApi: android.content.pm.verify.domain.DomainOwner#equals(Object):
- New API must be flagged with @FlaggedApi: method android.content.pm.verify.domain.DomainOwner.equals(Object)
-UnflaggedApi: android.content.pm.verify.domain.DomainOwner#hashCode():
- New API must be flagged with @FlaggedApi: method android.content.pm.verify.domain.DomainOwner.hashCode()
-UnflaggedApi: android.content.pm.verify.domain.DomainOwner#toString():
- New API must be flagged with @FlaggedApi: method android.content.pm.verify.domain.DomainOwner.toString()
-UnflaggedApi: android.content.pm.verify.domain.DomainVerificationInfo#equals(Object):
- New API must be flagged with @FlaggedApi: method android.content.pm.verify.domain.DomainVerificationInfo.equals(Object)
-UnflaggedApi: android.content.pm.verify.domain.DomainVerificationInfo#hashCode():
- New API must be flagged with @FlaggedApi: method android.content.pm.verify.domain.DomainVerificationInfo.hashCode()
-UnflaggedApi: android.content.pm.verify.domain.DomainVerificationInfo#toString():
- New API must be flagged with @FlaggedApi: method android.content.pm.verify.domain.DomainVerificationInfo.toString()
-UnflaggedApi: android.content.pm.verify.domain.DomainVerificationRequest#equals(Object):
- New API must be flagged with @FlaggedApi: method android.content.pm.verify.domain.DomainVerificationRequest.equals(Object)
-UnflaggedApi: android.content.pm.verify.domain.DomainVerificationRequest#hashCode():
- New API must be flagged with @FlaggedApi: method android.content.pm.verify.domain.DomainVerificationRequest.hashCode()
-UnflaggedApi: android.hardware.biometrics.BiometricManager.Authenticators#BIOMETRIC_CONVENIENCE:
- New API must be flagged with @FlaggedApi: field android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE
-UnflaggedApi: android.hardware.biometrics.BiometricManager.Authenticators#EMPTY_SET:
- New API must be flagged with @FlaggedApi: field android.hardware.biometrics.BiometricManager.Authenticators.EMPTY_SET
-UnflaggedApi: android.hardware.display.AmbientBrightnessDayStats#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.display.AmbientBrightnessDayStats.equals(Object)
-UnflaggedApi: android.hardware.display.AmbientBrightnessDayStats#hashCode():
- New API must be flagged with @FlaggedApi: method android.hardware.display.AmbientBrightnessDayStats.hashCode()
-UnflaggedApi: android.hardware.display.AmbientBrightnessDayStats#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.display.AmbientBrightnessDayStats.toString()
-UnflaggedApi: android.hardware.display.BrightnessChangeEvent#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.display.BrightnessChangeEvent.toString()
-UnflaggedApi: android.hardware.display.BrightnessConfiguration#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.display.BrightnessConfiguration.equals(Object)
-UnflaggedApi: android.hardware.display.BrightnessConfiguration#hashCode():
- New API must be flagged with @FlaggedApi: method android.hardware.display.BrightnessConfiguration.hashCode()
-UnflaggedApi: android.hardware.display.BrightnessConfiguration#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.display.BrightnessConfiguration.toString()
-UnflaggedApi: android.hardware.display.BrightnessCorrection#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.display.BrightnessCorrection.equals(Object)
-UnflaggedApi: android.hardware.display.BrightnessCorrection#hashCode():
- New API must be flagged with @FlaggedApi: method android.hardware.display.BrightnessCorrection.hashCode()
-UnflaggedApi: android.hardware.display.BrightnessCorrection#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.display.BrightnessCorrection.toString()
-UnflaggedApi: android.hardware.hdmi.HdmiDeviceInfo#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.hdmi.HdmiDeviceInfo.equals(Object)
-UnflaggedApi: android.hardware.hdmi.HdmiDeviceInfo#hashCode():
- New API must be flagged with @FlaggedApi: method android.hardware.hdmi.HdmiDeviceInfo.hashCode()
-UnflaggedApi: android.hardware.hdmi.HdmiDeviceInfo#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.hdmi.HdmiDeviceInfo.toString()
-UnflaggedApi: android.hardware.hdmi.HdmiPortInfo#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.hdmi.HdmiPortInfo.equals(Object)
-UnflaggedApi: android.hardware.hdmi.HdmiPortInfo#hashCode():
- New API must be flagged with @FlaggedApi: method android.hardware.hdmi.HdmiPortInfo.hashCode()
-UnflaggedApi: android.hardware.hdmi.HdmiPortInfo#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.hdmi.HdmiPortInfo.toString()
-UnflaggedApi: android.hardware.location.ContextHubClient#finalize():
- New API must be flagged with @FlaggedApi: method android.hardware.location.ContextHubClient.finalize()
-UnflaggedApi: android.hardware.location.ContextHubInfo#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.location.ContextHubInfo.equals(Object)
-UnflaggedApi: android.hardware.location.ContextHubInfo#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.location.ContextHubInfo.toString()
-UnflaggedApi: android.hardware.location.ContextHubIntentEvent#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.location.ContextHubIntentEvent.equals(Object)
-UnflaggedApi: android.hardware.location.ContextHubIntentEvent#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.location.ContextHubIntentEvent.toString()
-UnflaggedApi: android.hardware.location.ContextHubMessage#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.location.ContextHubMessage.toString()
-UnflaggedApi: android.hardware.location.GeofenceHardwareMonitorEvent#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.location.GeofenceHardwareMonitorEvent.toString()
-UnflaggedApi: android.hardware.location.MemoryRegion#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.location.MemoryRegion.equals(Object)
-UnflaggedApi: android.hardware.location.MemoryRegion#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.location.MemoryRegion.toString()
-UnflaggedApi: android.hardware.location.NanoApp#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.location.NanoApp.toString()
-UnflaggedApi: android.hardware.location.NanoAppFilter#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.location.NanoAppFilter.toString()
-UnflaggedApi: android.hardware.location.NanoAppInstanceInfo#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.location.NanoAppInstanceInfo.toString()
-UnflaggedApi: android.hardware.location.NanoAppMessage#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.location.NanoAppMessage.equals(Object)
-UnflaggedApi: android.hardware.location.NanoAppMessage#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.location.NanoAppMessage.toString()
-UnflaggedApi: android.hardware.location.NanoAppRpcService#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.location.NanoAppRpcService.equals(Object)
-UnflaggedApi: android.hardware.location.NanoAppRpcService#hashCode():
- New API must be flagged with @FlaggedApi: method android.hardware.location.NanoAppRpcService.hashCode()
-UnflaggedApi: android.hardware.location.NanoAppRpcService#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.location.NanoAppRpcService.toString()
-UnflaggedApi: android.hardware.radio.ProgramList.Filter#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.radio.ProgramList.Filter.equals(Object)
-UnflaggedApi: android.hardware.radio.ProgramList.Filter#hashCode():
- New API must be flagged with @FlaggedApi: method android.hardware.radio.ProgramList.Filter.hashCode()
-UnflaggedApi: android.hardware.radio.ProgramList.Filter#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.radio.ProgramList.Filter.toString()
-UnflaggedApi: android.hardware.radio.ProgramSelector#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.radio.ProgramSelector.equals(Object)
-UnflaggedApi: android.hardware.radio.ProgramSelector#hashCode():
- New API must be flagged with @FlaggedApi: method android.hardware.radio.ProgramSelector.hashCode()
-UnflaggedApi: android.hardware.radio.ProgramSelector#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.radio.ProgramSelector.toString()
-UnflaggedApi: android.hardware.radio.ProgramSelector.Identifier#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.radio.ProgramSelector.Identifier.equals(Object)
-UnflaggedApi: android.hardware.radio.ProgramSelector.Identifier#hashCode():
- New API must be flagged with @FlaggedApi: method android.hardware.radio.ProgramSelector.Identifier.hashCode()
-UnflaggedApi: android.hardware.radio.ProgramSelector.Identifier#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.radio.ProgramSelector.Identifier.toString()
-UnflaggedApi: android.hardware.radio.RadioManager.AmBandConfig#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.AmBandConfig.equals(Object)
-UnflaggedApi: android.hardware.radio.RadioManager.AmBandConfig#hashCode():
- New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.AmBandConfig.hashCode()
-UnflaggedApi: android.hardware.radio.RadioManager.AmBandConfig#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.AmBandConfig.toString()
-UnflaggedApi: android.hardware.radio.RadioManager.AmBandDescriptor#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.AmBandDescriptor.equals(Object)
-UnflaggedApi: android.hardware.radio.RadioManager.AmBandDescriptor#hashCode():
- New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.AmBandDescriptor.hashCode()
-UnflaggedApi: android.hardware.radio.RadioManager.AmBandDescriptor#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.AmBandDescriptor.toString()
-UnflaggedApi: android.hardware.radio.RadioManager.BandConfig#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.BandConfig.equals(Object)
-UnflaggedApi: android.hardware.radio.RadioManager.BandConfig#hashCode():
- New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.BandConfig.hashCode()
-UnflaggedApi: android.hardware.radio.RadioManager.BandConfig#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.BandConfig.toString()
-UnflaggedApi: android.hardware.radio.RadioManager.BandDescriptor#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.BandDescriptor.equals(Object)
-UnflaggedApi: android.hardware.radio.RadioManager.BandDescriptor#hashCode():
- New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.BandDescriptor.hashCode()
-UnflaggedApi: android.hardware.radio.RadioManager.BandDescriptor#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.BandDescriptor.toString()
-UnflaggedApi: android.hardware.radio.RadioManager.FmBandConfig#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.FmBandConfig.equals(Object)
-UnflaggedApi: android.hardware.radio.RadioManager.FmBandConfig#hashCode():
- New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.FmBandConfig.hashCode()
-UnflaggedApi: android.hardware.radio.RadioManager.FmBandConfig#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.FmBandConfig.toString()
-UnflaggedApi: android.hardware.radio.RadioManager.FmBandDescriptor#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.FmBandDescriptor.equals(Object)
-UnflaggedApi: android.hardware.radio.RadioManager.FmBandDescriptor#hashCode():
- New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.FmBandDescriptor.hashCode()
-UnflaggedApi: android.hardware.radio.RadioManager.FmBandDescriptor#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.FmBandDescriptor.toString()
-UnflaggedApi: android.hardware.radio.RadioManager.ModuleProperties#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.ModuleProperties.equals(Object)
-UnflaggedApi: android.hardware.radio.RadioManager.ModuleProperties#hashCode():
- New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.ModuleProperties.hashCode()
-UnflaggedApi: android.hardware.radio.RadioManager.ModuleProperties#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.ModuleProperties.toString()
-UnflaggedApi: android.hardware.radio.RadioManager.ProgramInfo#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.ProgramInfo.equals(Object)
-UnflaggedApi: android.hardware.radio.RadioManager.ProgramInfo#hashCode():
- New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.ProgramInfo.hashCode()
-UnflaggedApi: android.hardware.radio.RadioManager.ProgramInfo#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.ProgramInfo.toString()
-UnflaggedApi: android.hardware.radio.RadioMetadata#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioMetadata.equals(Object)
-UnflaggedApi: android.hardware.radio.RadioMetadata#hashCode():
- New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioMetadata.hashCode()
-UnflaggedApi: android.hardware.radio.RadioMetadata#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioMetadata.toString()
-UnflaggedApi: android.hardware.soundtrigger.SoundTrigger.Keyphrase#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.soundtrigger.SoundTrigger.Keyphrase.equals(Object)
-UnflaggedApi: android.hardware.soundtrigger.SoundTrigger.Keyphrase#hashCode():
- New API must be flagged with @FlaggedApi: method android.hardware.soundtrigger.SoundTrigger.Keyphrase.hashCode()
-UnflaggedApi: android.hardware.soundtrigger.SoundTrigger.Keyphrase#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.soundtrigger.SoundTrigger.Keyphrase.toString()
-UnflaggedApi: android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra.equals(Object)
-UnflaggedApi: android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra#hashCode():
- New API must be flagged with @FlaggedApi: method android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra.hashCode()
-UnflaggedApi: android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra.toString()
-UnflaggedApi: android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel.equals(Object)
-UnflaggedApi: android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel#hashCode():
- New API must be flagged with @FlaggedApi: method android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel.hashCode()
-UnflaggedApi: android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel.toString()
-UnflaggedApi: android.hardware.soundtrigger.SoundTrigger.ModelParamRange#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.soundtrigger.SoundTrigger.ModelParamRange.equals(Object)
-UnflaggedApi: android.hardware.soundtrigger.SoundTrigger.ModelParamRange#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.soundtrigger.SoundTrigger.ModelParamRange.toString()
-UnflaggedApi: android.hardware.soundtrigger.SoundTrigger.ModuleProperties#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.soundtrigger.SoundTrigger.ModuleProperties.equals(Object)
-UnflaggedApi: android.hardware.soundtrigger.SoundTrigger.ModuleProperties#hashCode():
- New API must be flagged with @FlaggedApi: method android.hardware.soundtrigger.SoundTrigger.ModuleProperties.hashCode()
-UnflaggedApi: android.hardware.soundtrigger.SoundTrigger.ModuleProperties#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.soundtrigger.SoundTrigger.ModuleProperties.toString()
-UnflaggedApi: android.hardware.soundtrigger.SoundTrigger.RecognitionEvent#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.soundtrigger.SoundTrigger.RecognitionEvent.equals(Object)
-UnflaggedApi: android.hardware.soundtrigger.SoundTrigger.RecognitionEvent#hashCode():
- New API must be flagged with @FlaggedApi: method android.hardware.soundtrigger.SoundTrigger.RecognitionEvent.hashCode()
-UnflaggedApi: android.hardware.soundtrigger.SoundTrigger.RecognitionEvent#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.soundtrigger.SoundTrigger.RecognitionEvent.toString()
-UnflaggedApi: android.hardware.soundtrigger.SoundTrigger.SoundModel#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.soundtrigger.SoundTrigger.SoundModel.equals(Object)
-UnflaggedApi: android.hardware.soundtrigger.SoundTrigger.SoundModel#hashCode():
- New API must be flagged with @FlaggedApi: method android.hardware.soundtrigger.SoundTrigger.SoundModel.hashCode()
-UnflaggedApi: android.hardware.usb.DisplayPortAltModeInfo#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.usb.DisplayPortAltModeInfo.equals(Object)
-UnflaggedApi: android.hardware.usb.DisplayPortAltModeInfo#hashCode():
- New API must be flagged with @FlaggedApi: method android.hardware.usb.DisplayPortAltModeInfo.hashCode()
-UnflaggedApi: android.hardware.usb.DisplayPortAltModeInfo#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.usb.DisplayPortAltModeInfo.toString()
-UnflaggedApi: android.hardware.usb.UsbPort#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.usb.UsbPort.toString()
-UnflaggedApi: android.hardware.usb.UsbPortStatus#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.usb.UsbPortStatus.toString()
-UnflaggedApi: android.location.CorrelationVector#equals(Object):
- New API must be flagged with @FlaggedApi: method android.location.CorrelationVector.equals(Object)
-UnflaggedApi: android.location.CorrelationVector#hashCode():
- New API must be flagged with @FlaggedApi: method android.location.CorrelationVector.hashCode()
-UnflaggedApi: android.location.CorrelationVector#toString():
- New API must be flagged with @FlaggedApi: method android.location.CorrelationVector.toString()
-UnflaggedApi: android.location.Country#equals(Object):
- New API must be flagged with @FlaggedApi: method android.location.Country.equals(Object)
-UnflaggedApi: android.location.Country#hashCode():
- New API must be flagged with @FlaggedApi: method android.location.Country.hashCode()
-UnflaggedApi: android.location.Country#toString():
- New API must be flagged with @FlaggedApi: method android.location.Country.toString()
-UnflaggedApi: android.location.GnssExcessPathInfo#equals(Object):
- New API must be flagged with @FlaggedApi: method android.location.GnssExcessPathInfo.equals(Object)
-UnflaggedApi: android.location.GnssExcessPathInfo#hashCode():
- New API must be flagged with @FlaggedApi: method android.location.GnssExcessPathInfo.hashCode()
-UnflaggedApi: android.location.GnssExcessPathInfo#toString():
- New API must be flagged with @FlaggedApi: method android.location.GnssExcessPathInfo.toString()
-UnflaggedApi: android.location.GnssMeasurementCorrections#toString():
- New API must be flagged with @FlaggedApi: method android.location.GnssMeasurementCorrections.toString()
-UnflaggedApi: android.location.GnssMeasurementRequest#getWorkSource():
- New API must be flagged with @FlaggedApi: method android.location.GnssMeasurementRequest.getWorkSource()
-UnflaggedApi: android.location.GnssMeasurementRequest.Builder#setWorkSource(android.os.WorkSource):
- New API must be flagged with @FlaggedApi: method android.location.GnssMeasurementRequest.Builder.setWorkSource(android.os.WorkSource)
-UnflaggedApi: android.location.GnssReflectingPlane#equals(Object):
- New API must be flagged with @FlaggedApi: method android.location.GnssReflectingPlane.equals(Object)
-UnflaggedApi: android.location.GnssReflectingPlane#hashCode():
- New API must be flagged with @FlaggedApi: method android.location.GnssReflectingPlane.hashCode()
-UnflaggedApi: android.location.GnssReflectingPlane#toString():
- New API must be flagged with @FlaggedApi: method android.location.GnssReflectingPlane.toString()
-UnflaggedApi: android.location.GnssRequest#equals(Object):
- New API must be flagged with @FlaggedApi: method android.location.GnssRequest.equals(Object)
-UnflaggedApi: android.location.GnssRequest#hashCode():
- New API must be flagged with @FlaggedApi: method android.location.GnssRequest.hashCode()
-UnflaggedApi: android.location.GnssRequest#toString():
- New API must be flagged with @FlaggedApi: method android.location.GnssRequest.toString()
-UnflaggedApi: android.location.GnssSingleSatCorrection#equals(Object):
- New API must be flagged with @FlaggedApi: method android.location.GnssSingleSatCorrection.equals(Object)
-UnflaggedApi: android.location.GnssSingleSatCorrection#hashCode():
- New API must be flagged with @FlaggedApi: method android.location.GnssSingleSatCorrection.hashCode()
-UnflaggedApi: android.location.GnssSingleSatCorrection#toString():
- New API must be flagged with @FlaggedApi: method android.location.GnssSingleSatCorrection.toString()
-UnflaggedApi: android.location.GpsClock#toString():
- New API must be flagged with @FlaggedApi: method android.location.GpsClock.toString()
-UnflaggedApi: android.location.GpsMeasurement#toString():
- New API must be flagged with @FlaggedApi: method android.location.GpsMeasurement.toString()
-UnflaggedApi: android.location.GpsMeasurementsEvent#toString():
- New API must be flagged with @FlaggedApi: method android.location.GpsMeasurementsEvent.toString()
-UnflaggedApi: android.location.GpsNavigationMessage#toString():
- New API must be flagged with @FlaggedApi: method android.location.GpsNavigationMessage.toString()
-UnflaggedApi: android.location.GpsNavigationMessageEvent#toString():
- New API must be flagged with @FlaggedApi: method android.location.GpsNavigationMessageEvent.toString()
-UnflaggedApi: android.location.LastLocationRequest#equals(Object):
- New API must be flagged with @FlaggedApi: method android.location.LastLocationRequest.equals(Object)
-UnflaggedApi: android.location.LastLocationRequest#hashCode():
- New API must be flagged with @FlaggedApi: method android.location.LastLocationRequest.hashCode()
-UnflaggedApi: android.location.LastLocationRequest#toString():
- New API must be flagged with @FlaggedApi: method android.location.LastLocationRequest.toString()
-UnflaggedApi: android.location.SatellitePvt#toString():
- New API must be flagged with @FlaggedApi: method android.location.SatellitePvt.toString()
-UnflaggedApi: android.location.SatellitePvt.ClockInfo#toString():
- New API must be flagged with @FlaggedApi: method android.location.SatellitePvt.ClockInfo.toString()
-UnflaggedApi: android.location.SatellitePvt.PositionEcef#toString():
- New API must be flagged with @FlaggedApi: method android.location.SatellitePvt.PositionEcef.toString()
-UnflaggedApi: android.location.SatellitePvt.VelocityEcef#toString():
- New API must be flagged with @FlaggedApi: method android.location.SatellitePvt.VelocityEcef.toString()
-UnflaggedApi: android.location.provider.ProviderRequest#equals(Object):
- New API must be flagged with @FlaggedApi: method android.location.provider.ProviderRequest.equals(Object)
-UnflaggedApi: android.location.provider.ProviderRequest#hashCode():
- New API must be flagged with @FlaggedApi: method android.location.provider.ProviderRequest.hashCode()
-UnflaggedApi: android.location.provider.ProviderRequest#toString():
- New API must be flagged with @FlaggedApi: method android.location.provider.ProviderRequest.toString()
-UnflaggedApi: android.media.AudioDeviceAttributes#equals(Object):
- New API must be flagged with @FlaggedApi: method android.media.AudioDeviceAttributes.equals(Object)
-UnflaggedApi: android.media.AudioDeviceAttributes#hashCode():
- New API must be flagged with @FlaggedApi: method android.media.AudioDeviceAttributes.hashCode()
-UnflaggedApi: android.media.AudioDeviceAttributes#toString():
- New API must be flagged with @FlaggedApi: method android.media.AudioDeviceAttributes.toString()
-UnflaggedApi: android.media.AudioFocusInfo#equals(Object):
- New API must be flagged with @FlaggedApi: method android.media.AudioFocusInfo.equals(Object)
-UnflaggedApi: android.media.AudioFocusInfo#hashCode():
- New API must be flagged with @FlaggedApi: method android.media.AudioFocusInfo.hashCode()
-UnflaggedApi: android.media.MediaRecorder.AudioSource#ECHO_REFERENCE:
- New API must be flagged with @FlaggedApi: field android.media.MediaRecorder.AudioSource.ECHO_REFERENCE
-UnflaggedApi: android.media.MediaRecorder.AudioSource#HOTWORD:
- New API must be flagged with @FlaggedApi: field android.media.MediaRecorder.AudioSource.HOTWORD
-UnflaggedApi: android.media.MediaRecorder.AudioSource#RADIO_TUNER:
- New API must be flagged with @FlaggedApi: field android.media.MediaRecorder.AudioSource.RADIO_TUNER
-UnflaggedApi: android.media.MediaRecorder.AudioSource#ULTRASOUND:
- New API must be flagged with @FlaggedApi: field android.media.MediaRecorder.AudioSource.ULTRASOUND
-UnflaggedApi: android.media.NearbyDevice#toString():
- New API must be flagged with @FlaggedApi: method android.media.NearbyDevice.toString()
-UnflaggedApi: android.media.VolumeInfo#equals(Object):
- New API must be flagged with @FlaggedApi: method android.media.VolumeInfo.equals(Object)
-UnflaggedApi: android.media.VolumeInfo#hashCode():
- New API must be flagged with @FlaggedApi: method android.media.VolumeInfo.hashCode()
-UnflaggedApi: android.media.VolumeInfo#toString():
- New API must be flagged with @FlaggedApi: method android.media.VolumeInfo.toString()
UnflaggedApi: android.media.audiopolicy.AudioMix#CREATOR:
New API must be flagged with @FlaggedApi: field android.media.audiopolicy.AudioMix.CREATOR
UnflaggedApi: android.media.audiopolicy.AudioMix#describeContents():
@@ -1903,578 +243,22 @@
New API must be flagged with @FlaggedApi: field android.media.audiopolicy.AudioMixingRule.CREATOR
UnflaggedApi: android.media.audiopolicy.AudioMixingRule#describeContents():
New API must be flagged with @FlaggedApi: method android.media.audiopolicy.AudioMixingRule.describeContents()
-UnflaggedApi: android.media.audiopolicy.AudioMixingRule#hashCode():
- New API must be flagged with @FlaggedApi: method android.media.audiopolicy.AudioMixingRule.hashCode()
UnflaggedApi: android.media.audiopolicy.AudioMixingRule#writeToParcel(android.os.Parcel, int):
New API must be flagged with @FlaggedApi: method android.media.audiopolicy.AudioMixingRule.writeToParcel(android.os.Parcel,int)
UnflaggedApi: android.media.audiopolicy.AudioPolicy#updateMixingRules(java.util.List<android.util.Pair<android.media.audiopolicy.AudioMix,android.media.audiopolicy.AudioMixingRule>>):
New API must be flagged with @FlaggedApi: method android.media.audiopolicy.AudioPolicy.updateMixingRules(java.util.List<android.util.Pair<android.media.audiopolicy.AudioMix,android.media.audiopolicy.AudioMixingRule>>)
-UnflaggedApi: android.media.audiopolicy.AudioProductStrategy#equals(Object):
- New API must be flagged with @FlaggedApi: method android.media.audiopolicy.AudioProductStrategy.equals(Object)
-UnflaggedApi: android.media.audiopolicy.AudioProductStrategy#hashCode():
- New API must be flagged with @FlaggedApi: method android.media.audiopolicy.AudioProductStrategy.hashCode()
-UnflaggedApi: android.media.audiopolicy.AudioProductStrategy#toString():
- New API must be flagged with @FlaggedApi: method android.media.audiopolicy.AudioProductStrategy.toString()
-UnflaggedApi: android.media.audiopolicy.AudioVolumeGroup#equals(Object):
- New API must be flagged with @FlaggedApi: method android.media.audiopolicy.AudioVolumeGroup.equals(Object)
-UnflaggedApi: android.media.audiopolicy.AudioVolumeGroup#toString():
- New API must be flagged with @FlaggedApi: method android.media.audiopolicy.AudioVolumeGroup.toString()
-UnflaggedApi: android.media.musicrecognition.MusicRecognitionService#onCreate():
- New API must be flagged with @FlaggedApi: method android.media.musicrecognition.MusicRecognitionService.onCreate()
-UnflaggedApi: android.media.soundtrigger.SoundTriggerDetectionService#onUnbind(android.content.Intent):
- New API must be flagged with @FlaggedApi: method android.media.soundtrigger.SoundTriggerDetectionService.onUnbind(android.content.Intent)
-UnflaggedApi: android.media.tv.TunedInfo#equals(Object):
- New API must be flagged with @FlaggedApi: method android.media.tv.TunedInfo.equals(Object)
-UnflaggedApi: android.media.tv.TunedInfo#hashCode():
- New API must be flagged with @FlaggedApi: method android.media.tv.TunedInfo.hashCode()
-UnflaggedApi: android.media.tv.TunedInfo#toString():
- New API must be flagged with @FlaggedApi: method android.media.tv.TunedInfo.toString()
-UnflaggedApi: android.media.tv.TvInputHardwareInfo#toString():
- New API must be flagged with @FlaggedApi: method android.media.tv.TvInputHardwareInfo.toString()
-UnflaggedApi: android.media.tv.TvRecordingClient.RecordingCallback#onEvent(String, String, android.os.Bundle):
- New API must be flagged with @FlaggedApi: method android.media.tv.TvRecordingClient.RecordingCallback.onEvent(String,String,android.os.Bundle)
-UnflaggedApi: android.media.tv.TvStreamConfig#equals(Object):
- New API must be flagged with @FlaggedApi: method android.media.tv.TvStreamConfig.equals(Object)
-UnflaggedApi: android.media.tv.TvStreamConfig#toString():
- New API must be flagged with @FlaggedApi: method android.media.tv.TvStreamConfig.toString()
-UnflaggedApi: android.net.MatchAllNetworkSpecifier#equals(Object):
- New API must be flagged with @FlaggedApi: method android.net.MatchAllNetworkSpecifier.equals(Object)
-UnflaggedApi: android.net.MatchAllNetworkSpecifier#hashCode():
- New API must be flagged with @FlaggedApi: method android.net.MatchAllNetworkSpecifier.hashCode()
-UnflaggedApi: android.net.NetworkKey#equals(Object):
- New API must be flagged with @FlaggedApi: method android.net.NetworkKey.equals(Object)
-UnflaggedApi: android.net.NetworkKey#hashCode():
- New API must be flagged with @FlaggedApi: method android.net.NetworkKey.hashCode()
-UnflaggedApi: android.net.NetworkKey#toString():
- New API must be flagged with @FlaggedApi: method android.net.NetworkKey.toString()
-UnflaggedApi: android.net.RssiCurve#equals(Object):
- New API must be flagged with @FlaggedApi: method android.net.RssiCurve.equals(Object)
-UnflaggedApi: android.net.RssiCurve#hashCode():
- New API must be flagged with @FlaggedApi: method android.net.RssiCurve.hashCode()
-UnflaggedApi: android.net.RssiCurve#toString():
- New API must be flagged with @FlaggedApi: method android.net.RssiCurve.toString()
-UnflaggedApi: android.net.ScoredNetwork#equals(Object):
- New API must be flagged with @FlaggedApi: method android.net.ScoredNetwork.equals(Object)
-UnflaggedApi: android.net.ScoredNetwork#hashCode():
- New API must be flagged with @FlaggedApi: method android.net.ScoredNetwork.hashCode()
-UnflaggedApi: android.net.ScoredNetwork#toString():
- New API must be flagged with @FlaggedApi: method android.net.ScoredNetwork.toString()
-UnflaggedApi: android.net.WebAddress#toString():
- New API must be flagged with @FlaggedApi: method android.net.WebAddress.toString()
-UnflaggedApi: android.net.WifiKey#equals(Object):
- New API must be flagged with @FlaggedApi: method android.net.WifiKey.equals(Object)
-UnflaggedApi: android.net.WifiKey#hashCode():
- New API must be flagged with @FlaggedApi: method android.net.WifiKey.hashCode()
-UnflaggedApi: android.net.WifiKey#toString():
- New API must be flagged with @FlaggedApi: method android.net.WifiKey.toString()
-UnflaggedApi: android.net.metrics.ApfProgramEvent#equals(Object):
- New API must be flagged with @FlaggedApi: method android.net.metrics.ApfProgramEvent.equals(Object)
-UnflaggedApi: android.net.metrics.ApfProgramEvent#toString():
- New API must be flagged with @FlaggedApi: method android.net.metrics.ApfProgramEvent.toString()
-UnflaggedApi: android.net.metrics.ApfStats#equals(Object):
- New API must be flagged with @FlaggedApi: method android.net.metrics.ApfStats.equals(Object)
-UnflaggedApi: android.net.metrics.ApfStats#toString():
- New API must be flagged with @FlaggedApi: method android.net.metrics.ApfStats.toString()
-UnflaggedApi: android.net.metrics.DhcpClientEvent#equals(Object):
- New API must be flagged with @FlaggedApi: method android.net.metrics.DhcpClientEvent.equals(Object)
-UnflaggedApi: android.net.metrics.DhcpClientEvent#toString():
- New API must be flagged with @FlaggedApi: method android.net.metrics.DhcpClientEvent.toString()
-UnflaggedApi: android.net.metrics.DhcpErrorEvent#toString():
- New API must be flagged with @FlaggedApi: method android.net.metrics.DhcpErrorEvent.toString()
-UnflaggedApi: android.net.metrics.IpManagerEvent#equals(Object):
- New API must be flagged with @FlaggedApi: method android.net.metrics.IpManagerEvent.equals(Object)
-UnflaggedApi: android.net.metrics.IpManagerEvent#toString():
- New API must be flagged with @FlaggedApi: method android.net.metrics.IpManagerEvent.toString()
-UnflaggedApi: android.net.metrics.IpReachabilityEvent#equals(Object):
- New API must be flagged with @FlaggedApi: method android.net.metrics.IpReachabilityEvent.equals(Object)
-UnflaggedApi: android.net.metrics.IpReachabilityEvent#toString():
- New API must be flagged with @FlaggedApi: method android.net.metrics.IpReachabilityEvent.toString()
-UnflaggedApi: android.net.metrics.NetworkEvent#equals(Object):
- New API must be flagged with @FlaggedApi: method android.net.metrics.NetworkEvent.equals(Object)
-UnflaggedApi: android.net.metrics.NetworkEvent#toString():
- New API must be flagged with @FlaggedApi: method android.net.metrics.NetworkEvent.toString()
-UnflaggedApi: android.net.metrics.RaEvent#equals(Object):
- New API must be flagged with @FlaggedApi: method android.net.metrics.RaEvent.equals(Object)
-UnflaggedApi: android.net.metrics.RaEvent#toString():
- New API must be flagged with @FlaggedApi: method android.net.metrics.RaEvent.toString()
-UnflaggedApi: android.net.metrics.ValidationProbeEvent#equals(Object):
- New API must be flagged with @FlaggedApi: method android.net.metrics.ValidationProbeEvent.equals(Object)
-UnflaggedApi: android.net.metrics.ValidationProbeEvent#toString():
- New API must be flagged with @FlaggedApi: method android.net.metrics.ValidationProbeEvent.toString()
-UnflaggedApi: android.net.vcn.VcnNetworkPolicyResult#equals(Object):
- New API must be flagged with @FlaggedApi: method android.net.vcn.VcnNetworkPolicyResult.equals(Object)
-UnflaggedApi: android.net.vcn.VcnNetworkPolicyResult#hashCode():
- New API must be flagged with @FlaggedApi: method android.net.vcn.VcnNetworkPolicyResult.hashCode()
-UnflaggedApi: android.net.vcn.VcnNetworkPolicyResult#toString():
- New API must be flagged with @FlaggedApi: method android.net.vcn.VcnNetworkPolicyResult.toString()
-UnflaggedApi: android.net.wifi.nl80211.DeviceWiphyCapabilities#equals(Object):
- New API must be flagged with @FlaggedApi: method android.net.wifi.nl80211.DeviceWiphyCapabilities.equals(Object)
-UnflaggedApi: android.net.wifi.nl80211.DeviceWiphyCapabilities#hashCode():
- New API must be flagged with @FlaggedApi: method android.net.wifi.nl80211.DeviceWiphyCapabilities.hashCode()
-UnflaggedApi: android.net.wifi.nl80211.DeviceWiphyCapabilities#toString():
- New API must be flagged with @FlaggedApi: method android.net.wifi.nl80211.DeviceWiphyCapabilities.toString()
-UnflaggedApi: android.net.wifi.nl80211.NativeWifiClient#equals(Object):
- New API must be flagged with @FlaggedApi: method android.net.wifi.nl80211.NativeWifiClient.equals(Object)
-UnflaggedApi: android.net.wifi.nl80211.NativeWifiClient#hashCode():
- New API must be flagged with @FlaggedApi: method android.net.wifi.nl80211.NativeWifiClient.hashCode()
-UnflaggedApi: android.net.wifi.nl80211.PnoNetwork#equals(Object):
- New API must be flagged with @FlaggedApi: method android.net.wifi.nl80211.PnoNetwork.equals(Object)
-UnflaggedApi: android.net.wifi.nl80211.PnoNetwork#hashCode():
- New API must be flagged with @FlaggedApi: method android.net.wifi.nl80211.PnoNetwork.hashCode()
-UnflaggedApi: android.net.wifi.nl80211.PnoSettings#equals(Object):
- New API must be flagged with @FlaggedApi: method android.net.wifi.nl80211.PnoSettings.equals(Object)
-UnflaggedApi: android.net.wifi.nl80211.PnoSettings#hashCode():
- New API must be flagged with @FlaggedApi: method android.net.wifi.nl80211.PnoSettings.hashCode()
-UnflaggedApi: android.net.wifi.nl80211.RadioChainInfo#equals(Object):
- New API must be flagged with @FlaggedApi: method android.net.wifi.nl80211.RadioChainInfo.equals(Object)
-UnflaggedApi: android.net.wifi.nl80211.RadioChainInfo#hashCode():
- New API must be flagged with @FlaggedApi: method android.net.wifi.nl80211.RadioChainInfo.hashCode()
-UnflaggedApi: android.net.wifi.sharedconnectivity.app.HotspotNetwork#equals(Object):
- New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.HotspotNetwork.equals(Object)
-UnflaggedApi: android.net.wifi.sharedconnectivity.app.HotspotNetwork#hashCode():
- New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.HotspotNetwork.hashCode()
-UnflaggedApi: android.net.wifi.sharedconnectivity.app.HotspotNetwork#toString():
- New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.HotspotNetwork.toString()
-UnflaggedApi: android.net.wifi.sharedconnectivity.app.HotspotNetworkConnectionStatus#equals(Object):
- New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.HotspotNetworkConnectionStatus.equals(Object)
-UnflaggedApi: android.net.wifi.sharedconnectivity.app.HotspotNetworkConnectionStatus#hashCode():
- New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.HotspotNetworkConnectionStatus.hashCode()
-UnflaggedApi: android.net.wifi.sharedconnectivity.app.HotspotNetworkConnectionStatus#toString():
- New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.HotspotNetworkConnectionStatus.toString()
-UnflaggedApi: android.net.wifi.sharedconnectivity.app.KnownNetwork#equals(Object):
- New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.KnownNetwork.equals(Object)
-UnflaggedApi: android.net.wifi.sharedconnectivity.app.KnownNetwork#hashCode():
- New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.KnownNetwork.hashCode()
-UnflaggedApi: android.net.wifi.sharedconnectivity.app.KnownNetwork#toString():
- New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.KnownNetwork.toString()
-UnflaggedApi: android.net.wifi.sharedconnectivity.app.KnownNetworkConnectionStatus#equals(Object):
- New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.KnownNetworkConnectionStatus.equals(Object)
-UnflaggedApi: android.net.wifi.sharedconnectivity.app.KnownNetworkConnectionStatus#hashCode():
- New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.KnownNetworkConnectionStatus.hashCode()
-UnflaggedApi: android.net.wifi.sharedconnectivity.app.KnownNetworkConnectionStatus#toString():
- New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.KnownNetworkConnectionStatus.toString()
-UnflaggedApi: android.net.wifi.sharedconnectivity.app.NetworkProviderInfo#equals(Object):
- New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.equals(Object)
-UnflaggedApi: android.net.wifi.sharedconnectivity.app.NetworkProviderInfo#hashCode():
- New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.hashCode()
UnflaggedApi: android.net.wifi.sharedconnectivity.app.NetworkProviderInfo#isBatteryCharging():
New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.isBatteryCharging()
-UnflaggedApi: android.net.wifi.sharedconnectivity.app.NetworkProviderInfo#toString():
- New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.toString()
UnflaggedApi: android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.Builder#setBatteryCharging(boolean):
New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.Builder.setBatteryCharging(boolean)
-UnflaggedApi: android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState#equals(Object):
- New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState.equals(Object)
-UnflaggedApi: android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState#hashCode():
- New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState.hashCode()
-UnflaggedApi: android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState#toString():
- New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState.toString()
-UnflaggedApi: android.os.BatterySaverPolicyConfig#toString():
- New API must be flagged with @FlaggedApi: method android.os.BatterySaverPolicyConfig.toString()
UnflaggedApi: android.os.BugreportParams#BUGREPORT_MODE_ONBOARDING:
New API must be flagged with @FlaggedApi: field android.os.BugreportParams.BUGREPORT_MODE_ONBOARDING
-UnflaggedApi: android.os.Build.VERSION#KNOWN_CODENAMES:
- New API must be flagged with @FlaggedApi: field android.os.Build.VERSION.KNOWN_CODENAMES
-UnflaggedApi: android.os.Build.VERSION#PREVIEW_SDK_FINGERPRINT:
- New API must be flagged with @FlaggedApi: field android.os.Build.VERSION.PREVIEW_SDK_FINGERPRINT
-UnflaggedApi: android.os.IncidentManager.PendingReport#equals(Object):
- New API must be flagged with @FlaggedApi: method android.os.IncidentManager.PendingReport.equals(Object)
-UnflaggedApi: android.os.IncidentManager.PendingReport#toString():
- New API must be flagged with @FlaggedApi: method android.os.IncidentManager.PendingReport.toString()
-UnflaggedApi: android.os.IncidentReportArgs#toString():
- New API must be flagged with @FlaggedApi: method android.os.IncidentReportArgs.toString()
-UnflaggedApi: android.os.NewUserRequest#toString():
- New API must be flagged with @FlaggedApi: method android.os.NewUserRequest.toString()
-UnflaggedApi: android.os.NewUserResponse#toString():
- New API must be flagged with @FlaggedApi: method android.os.NewUserResponse.toString()
-UnflaggedApi: android.os.PowerManager.LowPowerStandbyPolicy#equals(Object):
- New API must be flagged with @FlaggedApi: method android.os.PowerManager.LowPowerStandbyPolicy.equals(Object)
-UnflaggedApi: android.os.PowerManager.LowPowerStandbyPolicy#hashCode():
- New API must be flagged with @FlaggedApi: method android.os.PowerManager.LowPowerStandbyPolicy.hashCode()
-UnflaggedApi: android.os.PowerManager.LowPowerStandbyPolicy#toString():
- New API must be flagged with @FlaggedApi: method android.os.PowerManager.LowPowerStandbyPolicy.toString()
-UnflaggedApi: android.os.PowerManager.LowPowerStandbyPortDescription#equals(Object):
- New API must be flagged with @FlaggedApi: method android.os.PowerManager.LowPowerStandbyPortDescription.equals(Object)
-UnflaggedApi: android.os.PowerManager.LowPowerStandbyPortDescription#hashCode():
- New API must be flagged with @FlaggedApi: method android.os.PowerManager.LowPowerStandbyPortDescription.hashCode()
-UnflaggedApi: android.os.PowerManager.LowPowerStandbyPortDescription#toString():
- New API must be flagged with @FlaggedApi: method android.os.PowerManager.LowPowerStandbyPortDescription.toString()
-UnflaggedApi: android.os.ServiceSpecificException#toString():
- New API must be flagged with @FlaggedApi: method android.os.ServiceSpecificException.toString()
-UnflaggedApi: android.os.WorkSource.WorkChain#equals(Object):
- New API must be flagged with @FlaggedApi: method android.os.WorkSource.WorkChain.equals(Object)
-UnflaggedApi: android.os.WorkSource.WorkChain#hashCode():
- New API must be flagged with @FlaggedApi: method android.os.WorkSource.WorkChain.hashCode()
-UnflaggedApi: android.os.WorkSource.WorkChain#toString():
- New API must be flagged with @FlaggedApi: method android.os.WorkSource.WorkChain.toString()
-UnflaggedApi: android.os.connectivity.CellularBatteryStats#equals(Object):
- New API must be flagged with @FlaggedApi: method android.os.connectivity.CellularBatteryStats.equals(Object)
-UnflaggedApi: android.os.connectivity.CellularBatteryStats#hashCode():
- New API must be flagged with @FlaggedApi: method android.os.connectivity.CellularBatteryStats.hashCode()
-UnflaggedApi: android.os.connectivity.WifiActivityEnergyInfo#toString():
- New API must be flagged with @FlaggedApi: method android.os.connectivity.WifiActivityEnergyInfo.toString()
-UnflaggedApi: android.os.connectivity.WifiBatteryStats#equals(Object):
- New API must be flagged with @FlaggedApi: method android.os.connectivity.WifiBatteryStats.equals(Object)
-UnflaggedApi: android.os.connectivity.WifiBatteryStats#hashCode():
- New API must be flagged with @FlaggedApi: method android.os.connectivity.WifiBatteryStats.hashCode()
-UnflaggedApi: android.permission.AdminPermissionControlParams#toString():
- New API must be flagged with @FlaggedApi: method android.permission.AdminPermissionControlParams.toString()
-UnflaggedApi: android.permission.PermissionGroupUsage#equals(Object):
- New API must be flagged with @FlaggedApi: method android.permission.PermissionGroupUsage.equals(Object)
-UnflaggedApi: android.permission.PermissionGroupUsage#hashCode():
- New API must be flagged with @FlaggedApi: method android.permission.PermissionGroupUsage.hashCode()
-UnflaggedApi: android.permission.PermissionGroupUsage#toString():
- New API must be flagged with @FlaggedApi: method android.permission.PermissionGroupUsage.toString()
-UnflaggedApi: android.permission.PermissionManager.SplitPermissionInfo#equals(Object):
- New API must be flagged with @FlaggedApi: method android.permission.PermissionManager.SplitPermissionInfo.equals(Object)
-UnflaggedApi: android.permission.PermissionManager.SplitPermissionInfo#hashCode():
- New API must be flagged with @FlaggedApi: method android.permission.PermissionManager.SplitPermissionInfo.hashCode()
-UnflaggedApi: android.printservice.PrintServiceInfo#equals(Object):
- New API must be flagged with @FlaggedApi: method android.printservice.PrintServiceInfo.equals(Object)
-UnflaggedApi: android.printservice.PrintServiceInfo#hashCode():
- New API must be flagged with @FlaggedApi: method android.printservice.PrintServiceInfo.hashCode()
-UnflaggedApi: android.printservice.PrintServiceInfo#toString():
- New API must be flagged with @FlaggedApi: method android.printservice.PrintServiceInfo.toString()
-UnflaggedApi: android.printservice.recommendation.RecommendationService#attachBaseContext(android.content.Context):
- New API must be flagged with @FlaggedApi: method android.printservice.recommendation.RecommendationService.attachBaseContext(android.content.Context)
-UnflaggedApi: android.provider.CallLog.CallComposerLoggingException#toString():
- New API must be flagged with @FlaggedApi: method android.provider.CallLog.CallComposerLoggingException.toString()
-UnflaggedApi: android.provider.ContactsContract.MetadataSync#CONTENT_ITEM_TYPE:
- New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSync.CONTENT_ITEM_TYPE
-UnflaggedApi: android.provider.ContactsContract.MetadataSync#CONTENT_TYPE:
- New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSync.CONTENT_TYPE
-UnflaggedApi: android.provider.ContactsContract.MetadataSync#CONTENT_URI:
- New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSync.CONTENT_URI
-UnflaggedApi: android.provider.ContactsContract.MetadataSync#METADATA_AUTHORITY:
- New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSync.METADATA_AUTHORITY
-UnflaggedApi: android.provider.ContactsContract.MetadataSync#METADATA_AUTHORITY_URI:
- New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSync.METADATA_AUTHORITY_URI
-UnflaggedApi: android.provider.ContactsContract.MetadataSync#_COUNT:
- New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSync._COUNT
-UnflaggedApi: android.provider.ContactsContract.MetadataSync#_ID:
- New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSync._ID
-UnflaggedApi: android.provider.ContactsContract.MetadataSyncColumns#ACCOUNT_NAME:
- New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSyncColumns.ACCOUNT_NAME
-UnflaggedApi: android.provider.ContactsContract.MetadataSyncColumns#ACCOUNT_TYPE:
- New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSyncColumns.ACCOUNT_TYPE
-UnflaggedApi: android.provider.ContactsContract.MetadataSyncColumns#DATA:
- New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSyncColumns.DATA
-UnflaggedApi: android.provider.ContactsContract.MetadataSyncColumns#DATA_SET:
- New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSyncColumns.DATA_SET
-UnflaggedApi: android.provider.ContactsContract.MetadataSyncColumns#DELETED:
- New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSyncColumns.DELETED
-UnflaggedApi: android.provider.ContactsContract.MetadataSyncColumns#RAW_CONTACT_BACKUP_ID:
- New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSyncColumns.RAW_CONTACT_BACKUP_ID
-UnflaggedApi: android.provider.ContactsContract.MetadataSyncState#CONTENT_ITEM_TYPE:
- New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSyncState.CONTENT_ITEM_TYPE
-UnflaggedApi: android.provider.ContactsContract.MetadataSyncState#CONTENT_TYPE:
- New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSyncState.CONTENT_TYPE
-UnflaggedApi: android.provider.ContactsContract.MetadataSyncState#CONTENT_URI:
- New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSyncState.CONTENT_URI
-UnflaggedApi: android.provider.ContactsContract.MetadataSyncState#_COUNT:
- New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSyncState._COUNT
-UnflaggedApi: android.provider.ContactsContract.MetadataSyncState#_ID:
- New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSyncState._ID
-UnflaggedApi: android.provider.ContactsContract.MetadataSyncStateColumns#ACCOUNT_NAME:
- New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSyncStateColumns.ACCOUNT_NAME
-UnflaggedApi: android.provider.ContactsContract.MetadataSyncStateColumns#ACCOUNT_TYPE:
- New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSyncStateColumns.ACCOUNT_TYPE
-UnflaggedApi: android.provider.ContactsContract.MetadataSyncStateColumns#DATA_SET:
- New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSyncStateColumns.DATA_SET
-UnflaggedApi: android.provider.ContactsContract.MetadataSyncStateColumns#STATE:
- New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSyncStateColumns.STATE
-UnflaggedApi: android.provider.ContactsContract.Settings#setDefaultAccount(android.content.ContentResolver, android.accounts.Account):
- New API must be flagged with @FlaggedApi: method android.provider.ContactsContract.Settings.setDefaultAccount(android.content.ContentResolver,android.accounts.Account)
-UnflaggedApi: android.provider.ContactsContract.SimContacts#addSimAccount(android.content.ContentResolver, String, String, int, int):
- New API must be flagged with @FlaggedApi: method android.provider.ContactsContract.SimContacts.addSimAccount(android.content.ContentResolver,String,String,int,int)
-UnflaggedApi: android.provider.ContactsContract.SimContacts#removeSimAccounts(android.content.ContentResolver, int):
- New API must be flagged with @FlaggedApi: method android.provider.ContactsContract.SimContacts.removeSimAccounts(android.content.ContentResolver,int)
-UnflaggedApi: android.provider.SearchIndexableData#toString():
- New API must be flagged with @FlaggedApi: method android.provider.SearchIndexableData.toString()
-UnflaggedApi: android.provider.SearchIndexableResource#toString():
- New API must be flagged with @FlaggedApi: method android.provider.SearchIndexableResource.toString()
-UnflaggedApi: android.provider.SearchIndexablesProvider#attachInfo(android.content.Context, android.content.pm.ProviderInfo):
- New API must be flagged with @FlaggedApi: method android.provider.SearchIndexablesProvider.attachInfo(android.content.Context,android.content.pm.ProviderInfo)
UnflaggedApi: android.provider.Settings#ACTION_APP_PERMISSIONS_SETTINGS:
New API must be flagged with @FlaggedApi: field android.provider.Settings.ACTION_APP_PERMISSIONS_SETTINGS
UnflaggedApi: android.provider.Settings.System#putString(android.content.ContentResolver, String, String, boolean, boolean):
New API must be flagged with @FlaggedApi: method android.provider.Settings.System.putString(android.content.ContentResolver,String,String,boolean,boolean)
UnflaggedApi: android.provider.Settings.System#resetToDefaults(android.content.ContentResolver, String):
New API must be flagged with @FlaggedApi: method android.provider.Settings.System.resetToDefaults(android.content.ContentResolver,String)
-UnflaggedApi: android.provider.SimPhonebookContract.SimRecords#QUERY_ARG_PIN2:
- New API must be flagged with @FlaggedApi: field android.provider.SimPhonebookContract.SimRecords.QUERY_ARG_PIN2
-UnflaggedApi: android.provider.Telephony.Carriers#APN_SET_ID:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.Carriers.APN_SET_ID
-UnflaggedApi: android.provider.Telephony.Carriers#CARRIER_EDITED:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.Carriers.CARRIER_EDITED
-UnflaggedApi: android.provider.Telephony.Carriers#EDITED_STATUS:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.Carriers.EDITED_STATUS
-UnflaggedApi: android.provider.Telephony.Carriers#MATCH_ALL_APN_SET_ID:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.Carriers.MATCH_ALL_APN_SET_ID
-UnflaggedApi: android.provider.Telephony.Carriers#MAX_CONNECTIONS:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.Carriers.MAX_CONNECTIONS
-UnflaggedApi: android.provider.Telephony.Carriers#MODEM_PERSIST:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.Carriers.MODEM_PERSIST
-UnflaggedApi: android.provider.Telephony.Carriers#MTU_V4:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.Carriers.MTU_V4
-UnflaggedApi: android.provider.Telephony.Carriers#MTU_V6:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.Carriers.MTU_V6
-UnflaggedApi: android.provider.Telephony.Carriers#NO_APN_SET_ID:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.Carriers.NO_APN_SET_ID
-UnflaggedApi: android.provider.Telephony.Carriers#TIME_LIMIT_FOR_MAX_CONNECTIONS:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.Carriers.TIME_LIMIT_FOR_MAX_CONNECTIONS
-UnflaggedApi: android.provider.Telephony.Carriers#UNEDITED:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.Carriers.UNEDITED
-UnflaggedApi: android.provider.Telephony.Carriers#USER_DELETED:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.Carriers.USER_DELETED
-UnflaggedApi: android.provider.Telephony.Carriers#USER_EDITABLE:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.Carriers.USER_EDITABLE
-UnflaggedApi: android.provider.Telephony.Carriers#USER_EDITED:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.Carriers.USER_EDITED
-UnflaggedApi: android.provider.Telephony.Carriers#USER_VISIBLE:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.Carriers.USER_VISIBLE
-UnflaggedApi: android.provider.Telephony.Carriers#WAIT_TIME_RETRY:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.Carriers.WAIT_TIME_RETRY
-UnflaggedApi: android.provider.Telephony.CellBroadcasts:
- New API must be flagged with @FlaggedApi: class android.provider.Telephony.CellBroadcasts
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#AUTHORITY_LEGACY:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.AUTHORITY_LEGACY
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#AUTHORITY_LEGACY_URI:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.AUTHORITY_LEGACY_URI
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#CALL_METHOD_GET_PREFERENCE:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.CALL_METHOD_GET_PREFERENCE
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#CID:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.CID
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#CMAS_CATEGORY:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.CMAS_CATEGORY
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#CMAS_CERTAINTY:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.CMAS_CERTAINTY
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#CMAS_MESSAGE_CLASS:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.CMAS_MESSAGE_CLASS
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#CMAS_RESPONSE_TYPE:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.CMAS_RESPONSE_TYPE
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#CMAS_SEVERITY:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.CMAS_SEVERITY
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#CMAS_URGENCY:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.CMAS_URGENCY
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#CONTENT_URI:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.CONTENT_URI
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#DATA_CODING_SCHEME:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.DATA_CODING_SCHEME
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#DEFAULT_SORT_ORDER:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.DEFAULT_SORT_ORDER
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#DELIVERY_TIME:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.DELIVERY_TIME
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#ETWS_IS_PRIMARY:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.ETWS_IS_PRIMARY
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#ETWS_WARNING_TYPE:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.ETWS_WARNING_TYPE
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#GEOGRAPHICAL_SCOPE:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.GEOGRAPHICAL_SCOPE
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#GEOMETRIES:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.GEOMETRIES
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#LAC:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.LAC
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#LANGUAGE_CODE:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.LANGUAGE_CODE
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#LOCATION_CHECK_TIME:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.LOCATION_CHECK_TIME
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#MAXIMUM_WAIT_TIME:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.MAXIMUM_WAIT_TIME
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#MESSAGE_BODY:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.MESSAGE_BODY
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#MESSAGE_BROADCASTED:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.MESSAGE_BROADCASTED
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#MESSAGE_DISPLAYED:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.MESSAGE_DISPLAYED
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#MESSAGE_FORMAT:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.MESSAGE_FORMAT
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#MESSAGE_HISTORY_URI:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.MESSAGE_HISTORY_URI
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#MESSAGE_PRIORITY:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.MESSAGE_PRIORITY
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#MESSAGE_READ:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.MESSAGE_READ
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#PLMN:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.PLMN
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#RECEIVED_TIME:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.RECEIVED_TIME
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#SERIAL_NUMBER:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.SERIAL_NUMBER
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#SERVICE_CATEGORY:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.SERVICE_CATEGORY
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#SLOT_INDEX:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.SLOT_INDEX
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#SUBSCRIPTION_ID:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.SUBSCRIPTION_ID
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#_COUNT:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts._COUNT
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#_ID:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts._ID
-UnflaggedApi: android.provider.Telephony.CellBroadcasts.Preference:
- New API must be flagged with @FlaggedApi: class android.provider.Telephony.CellBroadcasts.Preference
-UnflaggedApi: android.provider.Telephony.CellBroadcasts.Preference#ENABLE_ALERT_VIBRATION_PREF:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.Preference.ENABLE_ALERT_VIBRATION_PREF
-UnflaggedApi: android.provider.Telephony.CellBroadcasts.Preference#ENABLE_AREA_UPDATE_INFO_PREF:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.Preference.ENABLE_AREA_UPDATE_INFO_PREF
-UnflaggedApi: android.provider.Telephony.CellBroadcasts.Preference#ENABLE_CMAS_AMBER_PREF:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.Preference.ENABLE_CMAS_AMBER_PREF
-UnflaggedApi: android.provider.Telephony.CellBroadcasts.Preference#ENABLE_CMAS_EXTREME_THREAT_PREF:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.Preference.ENABLE_CMAS_EXTREME_THREAT_PREF
-UnflaggedApi: android.provider.Telephony.CellBroadcasts.Preference#ENABLE_CMAS_IN_SECOND_LANGUAGE_PREF:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.Preference.ENABLE_CMAS_IN_SECOND_LANGUAGE_PREF
-UnflaggedApi: android.provider.Telephony.CellBroadcasts.Preference#ENABLE_CMAS_PRESIDENTIAL_PREF:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.Preference.ENABLE_CMAS_PRESIDENTIAL_PREF
-UnflaggedApi: android.provider.Telephony.CellBroadcasts.Preference#ENABLE_CMAS_SEVERE_THREAT_PREF:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.Preference.ENABLE_CMAS_SEVERE_THREAT_PREF
-UnflaggedApi: android.provider.Telephony.CellBroadcasts.Preference#ENABLE_EMERGENCY_PERF:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.Preference.ENABLE_EMERGENCY_PERF
-UnflaggedApi: android.provider.Telephony.CellBroadcasts.Preference#ENABLE_PUBLIC_SAFETY_PREF:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.Preference.ENABLE_PUBLIC_SAFETY_PREF
-UnflaggedApi: android.provider.Telephony.CellBroadcasts.Preference#ENABLE_STATE_LOCAL_TEST_PREF:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.Preference.ENABLE_STATE_LOCAL_TEST_PREF
-UnflaggedApi: android.provider.Telephony.CellBroadcasts.Preference#ENABLE_TEST_ALERT_PREF:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.Preference.ENABLE_TEST_ALERT_PREF
-UnflaggedApi: android.provider.Telephony.Sms.Intents#ACTION_SMS_EMERGENCY_CB_RECEIVED:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.Sms.Intents.ACTION_SMS_EMERGENCY_CB_RECEIVED
-UnflaggedApi: android.service.ambientcontext.AmbientContextDetectionResult#toString():
- New API must be flagged with @FlaggedApi: method android.service.ambientcontext.AmbientContextDetectionResult.toString()
-UnflaggedApi: android.service.ambientcontext.AmbientContextDetectionServiceStatus#toString():
- New API must be flagged with @FlaggedApi: method android.service.ambientcontext.AmbientContextDetectionServiceStatus.toString()
-UnflaggedApi: android.service.appprediction.AppPredictionService#onCreate():
- New API must be flagged with @FlaggedApi: method android.service.appprediction.AppPredictionService.onCreate()
-UnflaggedApi: android.service.assist.classification.FieldClassificationRequest#toString():
- New API must be flagged with @FlaggedApi: method android.service.assist.classification.FieldClassificationRequest.toString()
-UnflaggedApi: android.service.assist.classification.FieldClassificationResponse#toString():
- New API must be flagged with @FlaggedApi: method android.service.assist.classification.FieldClassificationResponse.toString()
-UnflaggedApi: android.service.assist.classification.FieldClassificationService#onCreate():
- New API must be flagged with @FlaggedApi: method android.service.assist.classification.FieldClassificationService.onCreate()
-UnflaggedApi: android.service.autofill.AutofillFieldClassificationService#onCreate():
- New API must be flagged with @FlaggedApi: method android.service.autofill.AutofillFieldClassificationService.onCreate()
-UnflaggedApi: android.service.autofill.Dataset.Builder#setContent(android.view.autofill.AutofillId, android.content.ClipData):
- New API must be flagged with @FlaggedApi: method android.service.autofill.Dataset.Builder.setContent(android.view.autofill.AutofillId,android.content.ClipData)
-UnflaggedApi: android.service.autofill.augmented.AugmentedAutofillService#onCreate():
- New API must be flagged with @FlaggedApi: method android.service.autofill.augmented.AugmentedAutofillService.onCreate()
-UnflaggedApi: android.service.autofill.augmented.AugmentedAutofillService#onUnbind(android.content.Intent):
- New API must be flagged with @FlaggedApi: method android.service.autofill.augmented.AugmentedAutofillService.onUnbind(android.content.Intent)
-UnflaggedApi: android.service.autofill.augmented.FillRequest#toString():
- New API must be flagged with @FlaggedApi: method android.service.autofill.augmented.FillRequest.toString()
-UnflaggedApi: android.service.autofill.augmented.FillWindow#finalize():
- New API must be flagged with @FlaggedApi: method android.service.autofill.augmented.FillWindow.finalize()
-UnflaggedApi: android.service.autofill.augmented.PresentationParams.Area#toString():
- New API must be flagged with @FlaggedApi: method android.service.autofill.augmented.PresentationParams.Area.toString()
-UnflaggedApi: android.service.cloudsearch.CloudSearchService#onCreate():
- New API must be flagged with @FlaggedApi: method android.service.cloudsearch.CloudSearchService.onCreate()
-UnflaggedApi: android.service.contentcapture.ActivityEvent#toString():
- New API must be flagged with @FlaggedApi: method android.service.contentcapture.ActivityEvent.toString()
-UnflaggedApi: android.service.contentcapture.ContentCaptureService#dump(java.io.FileDescriptor, java.io.PrintWriter, String[]):
- New API must be flagged with @FlaggedApi: method android.service.contentcapture.ContentCaptureService.dump(java.io.FileDescriptor,java.io.PrintWriter,String[])
-UnflaggedApi: android.service.contentcapture.ContentCaptureService#onCreate():
- New API must be flagged with @FlaggedApi: method android.service.contentcapture.ContentCaptureService.onCreate()
-UnflaggedApi: android.service.contentsuggestions.ContentSuggestionsService#onCreate():
- New API must be flagged with @FlaggedApi: method android.service.contentsuggestions.ContentSuggestionsService.onCreate()
-UnflaggedApi: android.service.displayhash.DisplayHashParams#toString():
- New API must be flagged with @FlaggedApi: method android.service.displayhash.DisplayHashParams.toString()
-UnflaggedApi: android.service.displayhash.DisplayHashingService#onCreate():
- New API must be flagged with @FlaggedApi: method android.service.displayhash.DisplayHashingService.onCreate()
-UnflaggedApi: android.service.euicc.EuiccProfileInfo#equals(Object):
- New API must be flagged with @FlaggedApi: method android.service.euicc.EuiccProfileInfo.equals(Object)
-UnflaggedApi: android.service.euicc.EuiccProfileInfo#hashCode():
- New API must be flagged with @FlaggedApi: method android.service.euicc.EuiccProfileInfo.hashCode()
-UnflaggedApi: android.service.euicc.EuiccProfileInfo#toString():
- New API must be flagged with @FlaggedApi: method android.service.euicc.EuiccProfileInfo.toString()
-UnflaggedApi: android.service.euicc.EuiccService#onCreate():
- New API must be flagged with @FlaggedApi: method android.service.euicc.EuiccService.onCreate()
-UnflaggedApi: android.service.euicc.EuiccService#onDestroy():
- New API must be flagged with @FlaggedApi: method android.service.euicc.EuiccService.onDestroy()
-UnflaggedApi: android.service.games.CreateGameSessionRequest#equals(Object):
- New API must be flagged with @FlaggedApi: method android.service.games.CreateGameSessionRequest.equals(Object)
-UnflaggedApi: android.service.games.CreateGameSessionRequest#hashCode():
- New API must be flagged with @FlaggedApi: method android.service.games.CreateGameSessionRequest.hashCode()
-UnflaggedApi: android.service.games.CreateGameSessionRequest#toString():
- New API must be flagged with @FlaggedApi: method android.service.games.CreateGameSessionRequest.toString()
-UnflaggedApi: android.service.games.GameSessionService#onCreate():
- New API must be flagged with @FlaggedApi: method android.service.games.GameSessionService.onCreate()
-UnflaggedApi: android.service.games.GameStartedEvent#equals(Object):
- New API must be flagged with @FlaggedApi: method android.service.games.GameStartedEvent.equals(Object)
-UnflaggedApi: android.service.games.GameStartedEvent#hashCode():
- New API must be flagged with @FlaggedApi: method android.service.games.GameStartedEvent.hashCode()
-UnflaggedApi: android.service.games.GameStartedEvent#toString():
- New API must be flagged with @FlaggedApi: method android.service.games.GameStartedEvent.toString()
-UnflaggedApi: android.service.notification.Adjustment#toString():
- New API must be flagged with @FlaggedApi: method android.service.notification.Adjustment.toString()
-UnflaggedApi: android.service.notification.NotificationAssistantService#attachBaseContext(android.content.Context):
- New API must be flagged with @FlaggedApi: method android.service.notification.NotificationAssistantService.attachBaseContext(android.content.Context)
-UnflaggedApi: android.service.notification.NotificationStats#equals(Object):
- New API must be flagged with @FlaggedApi: method android.service.notification.NotificationStats.equals(Object)
-UnflaggedApi: android.service.notification.NotificationStats#hashCode():
- New API must be flagged with @FlaggedApi: method android.service.notification.NotificationStats.hashCode()
-UnflaggedApi: android.service.notification.NotificationStats#toString():
- New API must be flagged with @FlaggedApi: method android.service.notification.NotificationStats.toString()
-UnflaggedApi: android.service.notification.SnoozeCriterion#equals(Object):
- New API must be flagged with @FlaggedApi: method android.service.notification.SnoozeCriterion.equals(Object)
-UnflaggedApi: android.service.notification.SnoozeCriterion#hashCode():
- New API must be flagged with @FlaggedApi: method android.service.notification.SnoozeCriterion.hashCode()
-UnflaggedApi: android.service.resolver.ResolverRankerService#onDestroy():
- New API must be flagged with @FlaggedApi: method android.service.resolver.ResolverRankerService.onDestroy()
-UnflaggedApi: android.service.resolver.ResolverTarget#toString():
- New API must be flagged with @FlaggedApi: method android.service.resolver.ResolverTarget.toString()
-UnflaggedApi: android.service.rotationresolver.RotationResolutionRequest#toString():
- New API must be flagged with @FlaggedApi: method android.service.rotationresolver.RotationResolutionRequest.toString()
-UnflaggedApi: android.service.search.SearchUiService#onCreate():
- New API must be flagged with @FlaggedApi: method android.service.search.SearchUiService.onCreate()
-UnflaggedApi: android.service.smartspace.SmartspaceService#onCreate():
- New API must be flagged with @FlaggedApi: method android.service.smartspace.SmartspaceService.onCreate()
-UnflaggedApi: android.service.textclassifier.TextClassifierService#onUnbind(android.content.Intent):
- New API must be flagged with @FlaggedApi: method android.service.textclassifier.TextClassifierService.onUnbind(android.content.Intent)
-UnflaggedApi: android.service.timezone.TimeZoneProviderStatus#equals(Object):
- New API must be flagged with @FlaggedApi: method android.service.timezone.TimeZoneProviderStatus.equals(Object)
-UnflaggedApi: android.service.timezone.TimeZoneProviderStatus#hashCode():
- New API must be flagged with @FlaggedApi: method android.service.timezone.TimeZoneProviderStatus.hashCode()
-UnflaggedApi: android.service.timezone.TimeZoneProviderStatus#toString():
- New API must be flagged with @FlaggedApi: method android.service.timezone.TimeZoneProviderStatus.toString()
-UnflaggedApi: android.service.timezone.TimeZoneProviderSuggestion#equals(Object):
- New API must be flagged with @FlaggedApi: method android.service.timezone.TimeZoneProviderSuggestion.equals(Object)
-UnflaggedApi: android.service.timezone.TimeZoneProviderSuggestion#hashCode():
- New API must be flagged with @FlaggedApi: method android.service.timezone.TimeZoneProviderSuggestion.hashCode()
-UnflaggedApi: android.service.timezone.TimeZoneProviderSuggestion#toString():
- New API must be flagged with @FlaggedApi: method android.service.timezone.TimeZoneProviderSuggestion.toString()
-UnflaggedApi: android.service.translation.TranslationService#onCreate():
- New API must be flagged with @FlaggedApi: method android.service.translation.TranslationService.onCreate()
-UnflaggedApi: android.service.trust.TrustAgentService#onCreate():
- New API must be flagged with @FlaggedApi: method android.service.trust.TrustAgentService.onCreate()
-UnflaggedApi: android.service.voice.AlwaysOnHotwordDetector#equals(Object):
- New API must be flagged with @FlaggedApi: method android.service.voice.AlwaysOnHotwordDetector.equals(Object)
-UnflaggedApi: android.service.voice.AlwaysOnHotwordDetector#hashCode():
- New API must be flagged with @FlaggedApi: method android.service.voice.AlwaysOnHotwordDetector.hashCode()
-UnflaggedApi: android.service.voice.AlwaysOnHotwordDetector.ModelParamRange#equals(Object):
- New API must be flagged with @FlaggedApi: method android.service.voice.AlwaysOnHotwordDetector.ModelParamRange.equals(Object)
-UnflaggedApi: android.service.voice.AlwaysOnHotwordDetector.ModelParamRange#hashCode():
- New API must be flagged with @FlaggedApi: method android.service.voice.AlwaysOnHotwordDetector.ModelParamRange.hashCode()
-UnflaggedApi: android.service.voice.AlwaysOnHotwordDetector.ModelParamRange#toString():
- New API must be flagged with @FlaggedApi: method android.service.voice.AlwaysOnHotwordDetector.ModelParamRange.toString()
-UnflaggedApi: android.service.voice.HotwordAudioStream#equals(Object):
- New API must be flagged with @FlaggedApi: method android.service.voice.HotwordAudioStream.equals(Object)
-UnflaggedApi: android.service.voice.HotwordAudioStream#hashCode():
- New API must be flagged with @FlaggedApi: method android.service.voice.HotwordAudioStream.hashCode()
-UnflaggedApi: android.service.voice.HotwordAudioStream#toString():
- New API must be flagged with @FlaggedApi: method android.service.voice.HotwordAudioStream.toString()
-UnflaggedApi: android.service.voice.HotwordDetectedResult#equals(Object):
- New API must be flagged with @FlaggedApi: method android.service.voice.HotwordDetectedResult.equals(Object)
-UnflaggedApi: android.service.voice.HotwordDetectedResult#hashCode():
- New API must be flagged with @FlaggedApi: method android.service.voice.HotwordDetectedResult.hashCode()
-UnflaggedApi: android.service.voice.HotwordDetectedResult#toString():
- New API must be flagged with @FlaggedApi: method android.service.voice.HotwordDetectedResult.toString()
-UnflaggedApi: android.service.voice.HotwordDetectionService#getSystemService(String):
- New API must be flagged with @FlaggedApi: method android.service.voice.HotwordDetectionService.getSystemService(String)
-UnflaggedApi: android.service.voice.HotwordDetectionServiceFailure#toString():
- New API must be flagged with @FlaggedApi: method android.service.voice.HotwordDetectionServiceFailure.toString()
-UnflaggedApi: android.service.voice.HotwordRejectedResult#equals(Object):
- New API must be flagged with @FlaggedApi: method android.service.voice.HotwordRejectedResult.equals(Object)
-UnflaggedApi: android.service.voice.HotwordRejectedResult#hashCode():
- New API must be flagged with @FlaggedApi: method android.service.voice.HotwordRejectedResult.hashCode()
-UnflaggedApi: android.service.voice.HotwordRejectedResult#toString():
- New API must be flagged with @FlaggedApi: method android.service.voice.HotwordRejectedResult.toString()
UnflaggedApi: android.service.voice.HotwordTrainingAudio:
New API must be flagged with @FlaggedApi: class android.service.voice.HotwordTrainingAudio
UnflaggedApi: android.service.voice.HotwordTrainingAudio#CONTENTS_FILE_DESCRIPTOR:
@@ -2487,8 +271,6 @@
New API must be flagged with @FlaggedApi: field android.service.voice.HotwordTrainingAudio.PARCELABLE_WRITE_RETURN_VALUE
UnflaggedApi: android.service.voice.HotwordTrainingAudio#describeContents():
New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingAudio.describeContents()
-UnflaggedApi: android.service.voice.HotwordTrainingAudio#equals(Object):
- New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingAudio.equals(Object)
UnflaggedApi: android.service.voice.HotwordTrainingAudio#getAudioFormat():
New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingAudio.getAudioFormat()
UnflaggedApi: android.service.voice.HotwordTrainingAudio#getAudioType():
@@ -2497,10 +279,6 @@
New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingAudio.getHotwordAudio()
UnflaggedApi: android.service.voice.HotwordTrainingAudio#getHotwordOffsetMillis():
New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingAudio.getHotwordOffsetMillis()
-UnflaggedApi: android.service.voice.HotwordTrainingAudio#hashCode():
- New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingAudio.hashCode()
-UnflaggedApi: android.service.voice.HotwordTrainingAudio#toString():
- New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingAudio.toString()
UnflaggedApi: android.service.voice.HotwordTrainingAudio#writeToParcel(android.os.Parcel, int):
New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingAudio.writeToParcel(android.os.Parcel,int)
UnflaggedApi: android.service.voice.HotwordTrainingAudio.Builder:
@@ -2513,8 +291,6 @@
New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingAudio.Builder.setAudioFormat(android.media.AudioFormat)
UnflaggedApi: android.service.voice.HotwordTrainingAudio.Builder#setAudioType(int):
New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingAudio.Builder.setAudioType(int)
-UnflaggedApi: android.service.voice.HotwordTrainingAudio.Builder#setHotwordAudio(byte...):
- New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingAudio.Builder.setHotwordAudio(byte...)
UnflaggedApi: android.service.voice.HotwordTrainingAudio.Builder#setHotwordOffsetMillis(int):
New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingAudio.Builder.setHotwordOffsetMillis(int)
UnflaggedApi: android.service.voice.HotwordTrainingData:
@@ -2537,18 +313,12 @@
New API must be flagged with @FlaggedApi: field android.service.voice.HotwordTrainingData.TIMEOUT_STAGE_VERY_EARLY
UnflaggedApi: android.service.voice.HotwordTrainingData#describeContents():
New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingData.describeContents()
-UnflaggedApi: android.service.voice.HotwordTrainingData#equals(Object):
- New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingData.equals(Object)
UnflaggedApi: android.service.voice.HotwordTrainingData#getMaxTrainingDataSize():
New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingData.getMaxTrainingDataSize()
UnflaggedApi: android.service.voice.HotwordTrainingData#getTimeoutStage():
New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingData.getTimeoutStage()
UnflaggedApi: android.service.voice.HotwordTrainingData#getTrainingAudios():
New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingData.getTrainingAudios()
-UnflaggedApi: android.service.voice.HotwordTrainingData#hashCode():
- New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingData.hashCode()
-UnflaggedApi: android.service.voice.HotwordTrainingData#toString():
- New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingData.toString()
UnflaggedApi: android.service.voice.HotwordTrainingData#writeToParcel(android.os.Parcel, int):
New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingData.writeToParcel(android.os.Parcel,int)
UnflaggedApi: android.service.voice.HotwordTrainingData.Builder:
@@ -2563,330 +333,10 @@
New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingData.Builder.setTimeoutStage(int)
UnflaggedApi: android.service.voice.HotwordTrainingData.Builder#setTrainingAudios(java.util.List<android.service.voice.HotwordTrainingAudio>):
New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingData.Builder.setTrainingAudios(java.util.List<android.service.voice.HotwordTrainingAudio>)
-UnflaggedApi: android.service.voice.SoundTriggerFailure#toString():
- New API must be flagged with @FlaggedApi: method android.service.voice.SoundTriggerFailure.toString()
-UnflaggedApi: android.service.voice.VisualQueryDetectionService#getSystemService(String):
- New API must be flagged with @FlaggedApi: method android.service.voice.VisualQueryDetectionService.getSystemService(String)
-UnflaggedApi: android.service.voice.VisualQueryDetectionService#openFileInput(String):
- New API must be flagged with @FlaggedApi: method android.service.voice.VisualQueryDetectionService.openFileInput(String)
-UnflaggedApi: android.service.voice.VisualQueryDetectionServiceFailure#toString():
- New API must be flagged with @FlaggedApi: method android.service.voice.VisualQueryDetectionServiceFailure.toString()
-UnflaggedApi: android.service.wallpaper.WallpaperService.Engine#isInAmbientMode():
- New API must be flagged with @FlaggedApi: method android.service.wallpaper.WallpaperService.Engine.isInAmbientMode()
-UnflaggedApi: android.service.wallpaper.WallpaperService.Engine#onAmbientModeChanged(boolean, long):
- New API must be flagged with @FlaggedApi: method android.service.wallpaper.WallpaperService.Engine.onAmbientModeChanged(boolean,long)
-UnflaggedApi: android.service.wallpapereffectsgeneration.WallpaperEffectsGenerationService#onCreate():
- New API must be flagged with @FlaggedApi: method android.service.wallpapereffectsgeneration.WallpaperEffectsGenerationService.onCreate()
-UnflaggedApi: android.service.watchdog.ExplicitHealthCheckService.PackageConfig#equals(Object):
- New API must be flagged with @FlaggedApi: method android.service.watchdog.ExplicitHealthCheckService.PackageConfig.equals(Object)
-UnflaggedApi: android.service.watchdog.ExplicitHealthCheckService.PackageConfig#hashCode():
- New API must be flagged with @FlaggedApi: method android.service.watchdog.ExplicitHealthCheckService.PackageConfig.hashCode()
-UnflaggedApi: android.service.watchdog.ExplicitHealthCheckService.PackageConfig#toString():
- New API must be flagged with @FlaggedApi: method android.service.watchdog.ExplicitHealthCheckService.PackageConfig.toString()
-UnflaggedApi: android.telecom.AudioState#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telecom.AudioState.equals(Object)
-UnflaggedApi: android.telecom.AudioState#toString():
- New API must be flagged with @FlaggedApi: method android.telecom.AudioState.toString()
-UnflaggedApi: android.telecom.BluetoothCallQualityReport#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telecom.BluetoothCallQualityReport.equals(Object)
-UnflaggedApi: android.telecom.BluetoothCallQualityReport#hashCode():
- New API must be flagged with @FlaggedApi: method android.telecom.BluetoothCallQualityReport.hashCode()
-UnflaggedApi: android.telecom.CallScreeningService.CallResponse.Builder#setShouldScreenCallViaAudioProcessing(boolean):
- New API must be flagged with @FlaggedApi: method android.telecom.CallScreeningService.CallResponse.Builder.setShouldScreenCallViaAudioProcessing(boolean)
-UnflaggedApi: android.telecom.Connection.CallFilteringCompletionInfo#toString():
- New API must be flagged with @FlaggedApi: method android.telecom.Connection.CallFilteringCompletionInfo.toString()
UnflaggedApi: android.telecom.StreamingCall#EXTRA_CALL_ID:
New API must be flagged with @FlaggedApi: field android.telecom.StreamingCall.EXTRA_CALL_ID
-UnflaggedApi: android.telephony.CallAttributes#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.CallAttributes.equals(Object)
-UnflaggedApi: android.telephony.CallAttributes#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.CallAttributes.hashCode()
-UnflaggedApi: android.telephony.CallAttributes#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.CallAttributes.toString()
-UnflaggedApi: android.telephony.CallQuality#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.CallQuality.equals(Object)
-UnflaggedApi: android.telephony.CallQuality#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.CallQuality.hashCode()
-UnflaggedApi: android.telephony.CallQuality#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.CallQuality.toString()
-UnflaggedApi: android.telephony.CallState#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.CallState.equals(Object)
-UnflaggedApi: android.telephony.CallState#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.CallState.hashCode()
-UnflaggedApi: android.telephony.CallState#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.CallState.toString()
-UnflaggedApi: android.telephony.CarrierRestrictionRules#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.CarrierRestrictionRules.toString()
-UnflaggedApi: android.telephony.CbGeoUtils.Circle#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.CbGeoUtils.Circle.toString()
-UnflaggedApi: android.telephony.CbGeoUtils.LatLng#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.CbGeoUtils.LatLng.toString()
-UnflaggedApi: android.telephony.CbGeoUtils.Polygon#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.CbGeoUtils.Polygon.toString()
-UnflaggedApi: android.telephony.CellBroadcastIdRange#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.CellBroadcastIdRange.equals(Object)
-UnflaggedApi: android.telephony.CellBroadcastIdRange#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.CellBroadcastIdRange.hashCode()
-UnflaggedApi: android.telephony.CellBroadcastIdRange#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.CellBroadcastIdRange.toString()
-UnflaggedApi: android.telephony.DataSpecificRegistrationInfo#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.DataSpecificRegistrationInfo.equals(Object)
-UnflaggedApi: android.telephony.DataSpecificRegistrationInfo#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.DataSpecificRegistrationInfo.hashCode()
-UnflaggedApi: android.telephony.DataSpecificRegistrationInfo#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.DataSpecificRegistrationInfo.toString()
-UnflaggedApi: android.telephony.DataThrottlingRequest#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.DataThrottlingRequest.toString()
-UnflaggedApi: android.telephony.ImsiEncryptionInfo#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ImsiEncryptionInfo.toString()
-UnflaggedApi: android.telephony.LinkCapacityEstimate#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.LinkCapacityEstimate.equals(Object)
-UnflaggedApi: android.telephony.LinkCapacityEstimate#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.LinkCapacityEstimate.hashCode()
-UnflaggedApi: android.telephony.LinkCapacityEstimate#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.LinkCapacityEstimate.toString()
-UnflaggedApi: android.telephony.LteVopsSupportInfo#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.LteVopsSupportInfo.toString()
-UnflaggedApi: android.telephony.ModemActivityInfo#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.ModemActivityInfo.equals(Object)
-UnflaggedApi: android.telephony.ModemActivityInfo#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.ModemActivityInfo.hashCode()
-UnflaggedApi: android.telephony.ModemActivityInfo#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ModemActivityInfo.toString()
UnflaggedApi: android.telephony.NetworkRegistrationInfo.Builder#setIsNonTerrestrialNetwork(boolean):
New API must be flagged with @FlaggedApi: method android.telephony.NetworkRegistrationInfo.Builder.setIsNonTerrestrialNetwork(boolean)
-UnflaggedApi: android.telephony.NetworkService#onUnbind(android.content.Intent):
- New API must be flagged with @FlaggedApi: method android.telephony.NetworkService.onUnbind(android.content.Intent)
-UnflaggedApi: android.telephony.NrVopsSupportInfo#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.NrVopsSupportInfo.toString()
-UnflaggedApi: android.telephony.PhoneCapability#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.PhoneCapability.equals(Object)
-UnflaggedApi: android.telephony.PhoneCapability#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.PhoneCapability.hashCode()
-UnflaggedApi: android.telephony.PhoneCapability#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.PhoneCapability.toString()
-UnflaggedApi: android.telephony.PhoneNumberRange#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.PhoneNumberRange.equals(Object)
-UnflaggedApi: android.telephony.PhoneNumberRange#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.PhoneNumberRange.hashCode()
-UnflaggedApi: android.telephony.PhoneNumberRange#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.PhoneNumberRange.toString()
-UnflaggedApi: android.telephony.PinResult#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.PinResult.equals(Object)
-UnflaggedApi: android.telephony.PinResult#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.PinResult.hashCode()
-UnflaggedApi: android.telephony.PinResult#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.PinResult.toString()
-UnflaggedApi: android.telephony.PreciseCallState#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.PreciseCallState.equals(Object)
-UnflaggedApi: android.telephony.PreciseCallState#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.PreciseCallState.hashCode()
-UnflaggedApi: android.telephony.PreciseCallState#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.PreciseCallState.toString()
-UnflaggedApi: android.telephony.SmsCbCmasInfo#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.SmsCbCmasInfo.toString()
-UnflaggedApi: android.telephony.SmsCbEtwsInfo#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.SmsCbEtwsInfo.toString()
-UnflaggedApi: android.telephony.SmsCbLocation#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.SmsCbLocation.equals(Object)
-UnflaggedApi: android.telephony.SmsCbLocation#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.SmsCbLocation.hashCode()
-UnflaggedApi: android.telephony.SmsCbLocation#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.SmsCbLocation.toString()
-UnflaggedApi: android.telephony.SmsCbMessage#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.SmsCbMessage.toString()
-UnflaggedApi: android.telephony.TelephonyHistogram#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.TelephonyHistogram.toString()
-UnflaggedApi: android.telephony.TelephonyManager.ModemActivityInfoException#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.TelephonyManager.ModemActivityInfoException.toString()
-UnflaggedApi: android.telephony.ThermalMitigationRequest#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ThermalMitigationRequest.toString()
-UnflaggedApi: android.telephony.UiccAccessRule#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.UiccAccessRule.equals(Object)
-UnflaggedApi: android.telephony.UiccAccessRule#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.UiccAccessRule.hashCode()
-UnflaggedApi: android.telephony.UiccAccessRule#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.UiccAccessRule.toString()
-UnflaggedApi: android.telephony.UiccSlotInfo#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.UiccSlotInfo.equals(Object)
-UnflaggedApi: android.telephony.UiccSlotInfo#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.UiccSlotInfo.hashCode()
-UnflaggedApi: android.telephony.UiccSlotInfo#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.UiccSlotInfo.toString()
-UnflaggedApi: android.telephony.UiccSlotMapping#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.UiccSlotMapping.equals(Object)
-UnflaggedApi: android.telephony.UiccSlotMapping#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.UiccSlotMapping.hashCode()
-UnflaggedApi: android.telephony.UiccSlotMapping#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.UiccSlotMapping.toString()
-UnflaggedApi: android.telephony.VopsSupportInfo#writeToParcel(android.os.Parcel, int):
- New API must be flagged with @FlaggedApi: method android.telephony.VopsSupportInfo.writeToParcel(android.os.Parcel,int)
-UnflaggedApi: android.telephony.cdma.CdmaSmsCbProgramData#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.cdma.CdmaSmsCbProgramData.toString()
-UnflaggedApi: android.telephony.data.DataCallResponse#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.data.DataCallResponse.equals(Object)
-UnflaggedApi: android.telephony.data.DataCallResponse#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.data.DataCallResponse.hashCode()
-UnflaggedApi: android.telephony.data.DataCallResponse#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.data.DataCallResponse.toString()
-UnflaggedApi: android.telephony.data.DataProfile#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.data.DataProfile.equals(Object)
-UnflaggedApi: android.telephony.data.DataProfile#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.data.DataProfile.hashCode()
-UnflaggedApi: android.telephony.data.DataProfile#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.data.DataProfile.toString()
-UnflaggedApi: android.telephony.data.DataService#onDestroy():
- New API must be flagged with @FlaggedApi: method android.telephony.data.DataService.onDestroy()
-UnflaggedApi: android.telephony.data.DataService#onUnbind(android.content.Intent):
- New API must be flagged with @FlaggedApi: method android.telephony.data.DataService.onUnbind(android.content.Intent)
-UnflaggedApi: android.telephony.data.EpsBearerQosSessionAttributes#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.data.EpsBearerQosSessionAttributes.equals(Object)
-UnflaggedApi: android.telephony.data.EpsBearerQosSessionAttributes#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.data.EpsBearerQosSessionAttributes.hashCode()
-UnflaggedApi: android.telephony.data.NrQosSessionAttributes#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.data.NrQosSessionAttributes.equals(Object)
-UnflaggedApi: android.telephony.data.NrQosSessionAttributes#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.data.NrQosSessionAttributes.hashCode()
-UnflaggedApi: android.telephony.data.ThrottleStatus#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.data.ThrottleStatus.equals(Object)
-UnflaggedApi: android.telephony.data.ThrottleStatus#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.data.ThrottleStatus.hashCode()
-UnflaggedApi: android.telephony.data.ThrottleStatus#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.data.ThrottleStatus.toString()
-UnflaggedApi: android.telephony.euicc.EuiccNotification#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.euicc.EuiccNotification.equals(Object)
-UnflaggedApi: android.telephony.euicc.EuiccNotification#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.euicc.EuiccNotification.hashCode()
-UnflaggedApi: android.telephony.euicc.EuiccNotification#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.euicc.EuiccNotification.toString()
-UnflaggedApi: android.telephony.euicc.EuiccRulesAuthTable#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.euicc.EuiccRulesAuthTable.equals(Object)
-UnflaggedApi: android.telephony.gba.UaSecurityProtocolIdentifier#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.gba.UaSecurityProtocolIdentifier.equals(Object)
-UnflaggedApi: android.telephony.gba.UaSecurityProtocolIdentifier#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.gba.UaSecurityProtocolIdentifier.hashCode()
-UnflaggedApi: android.telephony.gba.UaSecurityProtocolIdentifier#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.gba.UaSecurityProtocolIdentifier.toString()
-UnflaggedApi: android.telephony.ims.AudioCodecAttributes#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.AudioCodecAttributes.toString()
-UnflaggedApi: android.telephony.ims.DelegateRegistrationState#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.ims.DelegateRegistrationState.equals(Object)
-UnflaggedApi: android.telephony.ims.DelegateRegistrationState#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.DelegateRegistrationState.hashCode()
-UnflaggedApi: android.telephony.ims.DelegateRegistrationState#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.DelegateRegistrationState.toString()
-UnflaggedApi: android.telephony.ims.DelegateRequest#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.ims.DelegateRequest.equals(Object)
-UnflaggedApi: android.telephony.ims.DelegateRequest#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.DelegateRequest.hashCode()
-UnflaggedApi: android.telephony.ims.DelegateRequest#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.DelegateRequest.toString()
-UnflaggedApi: android.telephony.ims.FeatureTagState#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.ims.FeatureTagState.equals(Object)
-UnflaggedApi: android.telephony.ims.FeatureTagState#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.FeatureTagState.hashCode()
-UnflaggedApi: android.telephony.ims.FeatureTagState#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.FeatureTagState.toString()
-UnflaggedApi: android.telephony.ims.ImsCallForwardInfo#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.ImsCallForwardInfo.toString()
-UnflaggedApi: android.telephony.ims.ImsCallProfile#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.ImsCallProfile.toString()
-UnflaggedApi: android.telephony.ims.ImsConferenceState#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.ImsConferenceState.toString()
-UnflaggedApi: android.telephony.ims.ImsExternalCallState#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.ImsExternalCallState.toString()
-UnflaggedApi: android.telephony.ims.ImsMmTelManager.RegistrationCallback#onTechnologyChangeFailed(int, android.telephony.ims.ImsReasonInfo):
- New API must be flagged with @FlaggedApi: method android.telephony.ims.ImsMmTelManager.RegistrationCallback.onTechnologyChangeFailed(int,android.telephony.ims.ImsReasonInfo)
-UnflaggedApi: android.telephony.ims.ImsMmTelManager.RegistrationCallback#onUnregistered(android.telephony.ims.ImsReasonInfo):
- New API must be flagged with @FlaggedApi: method android.telephony.ims.ImsMmTelManager.RegistrationCallback.onUnregistered(android.telephony.ims.ImsReasonInfo)
-UnflaggedApi: android.telephony.ims.ImsSsData#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.ImsSsData.toString()
-UnflaggedApi: android.telephony.ims.ImsSsInfo#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.ImsSsInfo.toString()
-UnflaggedApi: android.telephony.ims.ImsStreamMediaProfile#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.ImsStreamMediaProfile.toString()
-UnflaggedApi: android.telephony.ims.ImsSuppServiceNotification#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.ImsSuppServiceNotification.toString()
-UnflaggedApi: android.telephony.ims.MediaQualityStatus#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.ims.MediaQualityStatus.equals(Object)
-UnflaggedApi: android.telephony.ims.MediaQualityStatus#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.MediaQualityStatus.hashCode()
-UnflaggedApi: android.telephony.ims.MediaQualityStatus#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.MediaQualityStatus.toString()
-UnflaggedApi: android.telephony.ims.MediaThreshold#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.ims.MediaThreshold.equals(Object)
-UnflaggedApi: android.telephony.ims.MediaThreshold#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.MediaThreshold.hashCode()
-UnflaggedApi: android.telephony.ims.MediaThreshold#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.MediaThreshold.toString()
-UnflaggedApi: android.telephony.ims.PublishAttributes#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.ims.PublishAttributes.equals(Object)
-UnflaggedApi: android.telephony.ims.PublishAttributes#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.PublishAttributes.hashCode()
-UnflaggedApi: android.telephony.ims.PublishAttributes#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.PublishAttributes.toString()
-UnflaggedApi: android.telephony.ims.RcsClientConfiguration#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.ims.RcsClientConfiguration.equals(Object)
-UnflaggedApi: android.telephony.ims.RcsClientConfiguration#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.RcsClientConfiguration.hashCode()
-UnflaggedApi: android.telephony.ims.RcsContactPresenceTuple#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.RcsContactPresenceTuple.toString()
-UnflaggedApi: android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities.toString()
-UnflaggedApi: android.telephony.ims.RcsContactUceCapability#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.RcsContactUceCapability.toString()
-UnflaggedApi: android.telephony.ims.RtpHeaderExtension#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.ims.RtpHeaderExtension.equals(Object)
-UnflaggedApi: android.telephony.ims.RtpHeaderExtension#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.RtpHeaderExtension.hashCode()
-UnflaggedApi: android.telephony.ims.RtpHeaderExtension#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.RtpHeaderExtension.toString()
-UnflaggedApi: android.telephony.ims.RtpHeaderExtensionType#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.ims.RtpHeaderExtensionType.equals(Object)
-UnflaggedApi: android.telephony.ims.RtpHeaderExtensionType#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.RtpHeaderExtensionType.hashCode()
-UnflaggedApi: android.telephony.ims.RtpHeaderExtensionType#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.RtpHeaderExtensionType.toString()
-UnflaggedApi: android.telephony.ims.SipDelegateConfiguration#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.ims.SipDelegateConfiguration.equals(Object)
-UnflaggedApi: android.telephony.ims.SipDelegateConfiguration#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.SipDelegateConfiguration.hashCode()
-UnflaggedApi: android.telephony.ims.SipDelegateConfiguration#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.SipDelegateConfiguration.toString()
-UnflaggedApi: android.telephony.ims.SipDelegateConfiguration.IpSecConfiguration#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.ims.SipDelegateConfiguration.IpSecConfiguration.equals(Object)
-UnflaggedApi: android.telephony.ims.SipDelegateConfiguration.IpSecConfiguration#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.SipDelegateConfiguration.IpSecConfiguration.hashCode()
-UnflaggedApi: android.telephony.ims.SipDelegateConfiguration.IpSecConfiguration#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.SipDelegateConfiguration.IpSecConfiguration.toString()
-UnflaggedApi: android.telephony.ims.SipDialogState#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.ims.SipDialogState.equals(Object)
-UnflaggedApi: android.telephony.ims.SipDialogState#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.SipDialogState.hashCode()
-UnflaggedApi: android.telephony.ims.SipMessage#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.ims.SipMessage.equals(Object)
-UnflaggedApi: android.telephony.ims.SipMessage#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.SipMessage.hashCode()
-UnflaggedApi: android.telephony.ims.SipMessage#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.SipMessage.toString()
-UnflaggedApi: android.telephony.ims.SrvccCall#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.ims.SrvccCall.equals(Object)
-UnflaggedApi: android.telephony.ims.SrvccCall#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.SrvccCall.hashCode()
-UnflaggedApi: android.telephony.ims.SrvccCall#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.SrvccCall.toString()
-UnflaggedApi: android.telephony.ims.feature.CapabilityChangeRequest#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.feature.CapabilityChangeRequest.toString()
-UnflaggedApi: android.telephony.ims.feature.CapabilityChangeRequest.CapabilityPair#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.feature.CapabilityChangeRequest.CapabilityPair.toString()
-UnflaggedApi: android.telephony.ims.stub.ImsFeatureConfiguration.FeatureSlotPair#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.ims.stub.ImsFeatureConfiguration.FeatureSlotPair.equals(Object)
-UnflaggedApi: android.telephony.ims.stub.ImsFeatureConfiguration.FeatureSlotPair#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.stub.ImsFeatureConfiguration.FeatureSlotPair.hashCode()
-UnflaggedApi: android.telephony.ims.stub.ImsFeatureConfiguration.FeatureSlotPair#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.stub.ImsFeatureConfiguration.FeatureSlotPair.toString()
-UnflaggedApi: android.telephony.mbms.DownloadRequest.Builder#setServiceId(String):
- New API must be flagged with @FlaggedApi: method android.telephony.mbms.DownloadRequest.Builder.setServiceId(String)
UnflaggedApi: android.telephony.mbms.vendor.MbmsDownloadServiceBase#DESCRIPTOR:
New API must be flagged with @FlaggedApi: field android.telephony.mbms.vendor.MbmsDownloadServiceBase.DESCRIPTOR
UnflaggedApi: android.telephony.mbms.vendor.MbmsStreamingServiceBase#DESCRIPTOR:
@@ -2901,18 +351,12 @@
New API must be flagged with @FlaggedApi: field android.telephony.satellite.AntennaDirection.PARCELABLE_WRITE_RETURN_VALUE
UnflaggedApi: android.telephony.satellite.AntennaDirection#describeContents():
New API must be flagged with @FlaggedApi: method android.telephony.satellite.AntennaDirection.describeContents()
-UnflaggedApi: android.telephony.satellite.AntennaDirection#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.satellite.AntennaDirection.equals(Object)
UnflaggedApi: android.telephony.satellite.AntennaDirection#getX():
New API must be flagged with @FlaggedApi: method android.telephony.satellite.AntennaDirection.getX()
UnflaggedApi: android.telephony.satellite.AntennaDirection#getY():
New API must be flagged with @FlaggedApi: method android.telephony.satellite.AntennaDirection.getY()
UnflaggedApi: android.telephony.satellite.AntennaDirection#getZ():
New API must be flagged with @FlaggedApi: method android.telephony.satellite.AntennaDirection.getZ()
-UnflaggedApi: android.telephony.satellite.AntennaDirection#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.satellite.AntennaDirection.hashCode()
-UnflaggedApi: android.telephony.satellite.AntennaDirection#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.satellite.AntennaDirection.toString()
UnflaggedApi: android.telephony.satellite.AntennaDirection#writeToParcel(android.os.Parcel, int):
New API must be flagged with @FlaggedApi: method android.telephony.satellite.AntennaDirection.writeToParcel(android.os.Parcel,int)
UnflaggedApi: android.telephony.satellite.AntennaPosition:
@@ -2925,16 +369,10 @@
New API must be flagged with @FlaggedApi: field android.telephony.satellite.AntennaPosition.PARCELABLE_WRITE_RETURN_VALUE
UnflaggedApi: android.telephony.satellite.AntennaPosition#describeContents():
New API must be flagged with @FlaggedApi: method android.telephony.satellite.AntennaPosition.describeContents()
-UnflaggedApi: android.telephony.satellite.AntennaPosition#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.satellite.AntennaPosition.equals(Object)
UnflaggedApi: android.telephony.satellite.AntennaPosition#getAntennaDirection():
New API must be flagged with @FlaggedApi: method android.telephony.satellite.AntennaPosition.getAntennaDirection()
UnflaggedApi: android.telephony.satellite.AntennaPosition#getSuggestedHoldPosition():
New API must be flagged with @FlaggedApi: method android.telephony.satellite.AntennaPosition.getSuggestedHoldPosition()
-UnflaggedApi: android.telephony.satellite.AntennaPosition#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.satellite.AntennaPosition.hashCode()
-UnflaggedApi: android.telephony.satellite.AntennaPosition#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.satellite.AntennaPosition.toString()
UnflaggedApi: android.telephony.satellite.AntennaPosition#writeToParcel(android.os.Parcel, int):
New API must be flagged with @FlaggedApi: method android.telephony.satellite.AntennaPosition.writeToParcel(android.os.Parcel,int)
UnflaggedApi: android.telephony.satellite.PointingInfo:
@@ -2947,16 +385,10 @@
New API must be flagged with @FlaggedApi: field android.telephony.satellite.PointingInfo.PARCELABLE_WRITE_RETURN_VALUE
UnflaggedApi: android.telephony.satellite.PointingInfo#describeContents():
New API must be flagged with @FlaggedApi: method android.telephony.satellite.PointingInfo.describeContents()
-UnflaggedApi: android.telephony.satellite.PointingInfo#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.satellite.PointingInfo.equals(Object)
UnflaggedApi: android.telephony.satellite.PointingInfo#getSatelliteAzimuthDegrees():
New API must be flagged with @FlaggedApi: method android.telephony.satellite.PointingInfo.getSatelliteAzimuthDegrees()
UnflaggedApi: android.telephony.satellite.PointingInfo#getSatelliteElevationDegrees():
New API must be flagged with @FlaggedApi: method android.telephony.satellite.PointingInfo.getSatelliteElevationDegrees()
-UnflaggedApi: android.telephony.satellite.PointingInfo#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.satellite.PointingInfo.hashCode()
-UnflaggedApi: android.telephony.satellite.PointingInfo#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.satellite.PointingInfo.toString()
UnflaggedApi: android.telephony.satellite.PointingInfo#writeToParcel(android.os.Parcel, int):
New API must be flagged with @FlaggedApi: method android.telephony.satellite.PointingInfo.writeToParcel(android.os.Parcel,int)
UnflaggedApi: android.telephony.satellite.SatelliteCapabilities:
@@ -2969,20 +401,14 @@
New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteCapabilities.PARCELABLE_WRITE_RETURN_VALUE
UnflaggedApi: android.telephony.satellite.SatelliteCapabilities#describeContents():
New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteCapabilities.describeContents()
-UnflaggedApi: android.telephony.satellite.SatelliteCapabilities#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteCapabilities.equals(Object)
UnflaggedApi: android.telephony.satellite.SatelliteCapabilities#getAntennaPositionMap():
New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteCapabilities.getAntennaPositionMap()
UnflaggedApi: android.telephony.satellite.SatelliteCapabilities#getMaxBytesPerOutgoingDatagram():
New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteCapabilities.getMaxBytesPerOutgoingDatagram()
UnflaggedApi: android.telephony.satellite.SatelliteCapabilities#getSupportedRadioTechnologies():
New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteCapabilities.getSupportedRadioTechnologies()
-UnflaggedApi: android.telephony.satellite.SatelliteCapabilities#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteCapabilities.hashCode()
UnflaggedApi: android.telephony.satellite.SatelliteCapabilities#isPointingRequired():
New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteCapabilities.isPointingRequired()
-UnflaggedApi: android.telephony.satellite.SatelliteCapabilities#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteCapabilities.toString()
UnflaggedApi: android.telephony.satellite.SatelliteCapabilities#writeToParcel(android.os.Parcel, int):
New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteCapabilities.writeToParcel(android.os.Parcel,int)
UnflaggedApi: android.telephony.satellite.SatelliteDatagram:
@@ -3037,8 +463,6 @@
New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.NT_RADIO_TECHNOLOGY_PROPRIETARY
UnflaggedApi: android.telephony.satellite.SatelliteManager#NT_RADIO_TECHNOLOGY_UNKNOWN:
New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.NT_RADIO_TECHNOLOGY_UNKNOWN
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_ACCESS_BARRED:
- New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_ACCESS_BARRED
UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE:
New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE
UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED:
@@ -3057,20 +481,6 @@
New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS
UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_DATAGRAM_TRANSFER_STATE_UNKNOWN:
New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_UNKNOWN
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_ERROR:
- New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_ERROR
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_ERROR_NONE:
- New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_ERROR_NONE
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_INVALID_ARGUMENTS:
- New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_INVALID_ARGUMENTS
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_INVALID_MODEM_STATE:
- New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_INVALID_MODEM_STATE
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_INVALID_TELEPHONY_STATE:
- New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_MODEM_BUSY:
- New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_BUSY
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_MODEM_ERROR:
- New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_ERROR
UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_MODEM_STATE_DATAGRAM_RETRYING:
New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_RETRYING
UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING:
@@ -3085,28 +495,6 @@
New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_UNAVAILABLE
UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_MODEM_STATE_UNKNOWN:
New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_UNKNOWN
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_NETWORK_ERROR:
- New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_NETWORK_ERROR
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_NETWORK_TIMEOUT:
- New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_NETWORK_TIMEOUT
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_NOT_AUTHORIZED:
- New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_NOT_AUTHORIZED
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_NOT_REACHABLE:
- New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_NOT_REACHABLE
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_NOT_SUPPORTED:
- New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_NOT_SUPPORTED
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_NO_RESOURCES:
- New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_NO_RESOURCES
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_RADIO_NOT_AVAILABLE:
- New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_RADIO_NOT_AVAILABLE
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_REQUEST_ABORTED:
- New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_REQUEST_ABORTED
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_REQUEST_FAILED:
- New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_REQUEST_FAILED
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_REQUEST_IN_PROGRESS:
- New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_REQUEST_IN_PROGRESS
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_REQUEST_NOT_SUPPORTED:
- New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_REQUEST_NOT_SUPPORTED
UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_RESULT_ACCESS_BARRED:
New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_ACCESS_BARRED
UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_RESULT_ERROR:
@@ -3153,14 +541,6 @@
New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SERVICE_PROVISION_IN_PROGRESS
UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_RESULT_SUCCESS:
New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_SERVER_ERROR:
- New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_SERVER_ERROR
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_SERVICE_ERROR:
- New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_SERVICE_ERROR
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_SERVICE_NOT_PROVISIONED:
- New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_SERVICE_NOT_PROVISIONED
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_SERVICE_PROVISION_IN_PROGRESS:
- New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_SERVICE_PROVISION_IN_PROGRESS
UnflaggedApi: android.telephony.satellite.SatelliteManager#deprovisionSatelliteService(String, java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>):
New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteManager.deprovisionSatelliteService(String,java.util.concurrent.Executor,java.util.function.Consumer<java.lang.Integer>)
UnflaggedApi: android.telephony.satellite.SatelliteManager#pollPendingSatelliteDatagrams(java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>):
@@ -3225,141 +605,3 @@
New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteTransmissionUpdateCallback.onSatellitePositionChanged(android.telephony.satellite.PointingInfo)
UnflaggedApi: android.telephony.satellite.SatelliteTransmissionUpdateCallback#onSendDatagramStateChanged(int, int, int):
New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteTransmissionUpdateCallback.onSendDatagramStateChanged(int,int,int)
-UnflaggedApi: android.text.FontConfig#equals(Object):
- New API must be flagged with @FlaggedApi: method android.text.FontConfig.equals(Object)
-UnflaggedApi: android.text.FontConfig#hashCode():
- New API must be flagged with @FlaggedApi: method android.text.FontConfig.hashCode()
-UnflaggedApi: android.text.FontConfig#toString():
- New API must be flagged with @FlaggedApi: method android.text.FontConfig.toString()
-UnflaggedApi: android.text.FontConfig.Alias#equals(Object):
- New API must be flagged with @FlaggedApi: method android.text.FontConfig.Alias.equals(Object)
-UnflaggedApi: android.text.FontConfig.Alias#hashCode():
- New API must be flagged with @FlaggedApi: method android.text.FontConfig.Alias.hashCode()
-UnflaggedApi: android.text.FontConfig.Alias#toString():
- New API must be flagged with @FlaggedApi: method android.text.FontConfig.Alias.toString()
-UnflaggedApi: android.text.FontConfig.Font#equals(Object):
- New API must be flagged with @FlaggedApi: method android.text.FontConfig.Font.equals(Object)
-UnflaggedApi: android.text.FontConfig.Font#hashCode():
- New API must be flagged with @FlaggedApi: method android.text.FontConfig.Font.hashCode()
-UnflaggedApi: android.text.FontConfig.Font#toString():
- New API must be flagged with @FlaggedApi: method android.text.FontConfig.Font.toString()
-UnflaggedApi: android.text.FontConfig.FontFamily#equals(Object):
- New API must be flagged with @FlaggedApi: method android.text.FontConfig.FontFamily.equals(Object)
-UnflaggedApi: android.text.FontConfig.FontFamily#hashCode():
- New API must be flagged with @FlaggedApi: method android.text.FontConfig.FontFamily.hashCode()
-UnflaggedApi: android.text.FontConfig.FontFamily#toString():
- New API must be flagged with @FlaggedApi: method android.text.FontConfig.FontFamily.toString()
-UnflaggedApi: android.text.FontConfig.NamedFamilyList#equals(Object):
- New API must be flagged with @FlaggedApi: method android.text.FontConfig.NamedFamilyList.equals(Object)
-UnflaggedApi: android.text.FontConfig.NamedFamilyList#hashCode():
- New API must be flagged with @FlaggedApi: method android.text.FontConfig.NamedFamilyList.hashCode()
-UnflaggedApi: android.text.FontConfig.NamedFamilyList#toString():
- New API must be flagged with @FlaggedApi: method android.text.FontConfig.NamedFamilyList.toString()
-UnflaggedApi: android.view.contentcapture.ContentCaptureEvent#toString():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ContentCaptureEvent.toString()
-UnflaggedApi: android.view.contentcapture.ViewNode#getAutofillHints():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getAutofillHints()
-UnflaggedApi: android.view.contentcapture.ViewNode#getAutofillId():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getAutofillId()
-UnflaggedApi: android.view.contentcapture.ViewNode#getAutofillOptions():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getAutofillOptions()
-UnflaggedApi: android.view.contentcapture.ViewNode#getAutofillType():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getAutofillType()
-UnflaggedApi: android.view.contentcapture.ViewNode#getAutofillValue():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getAutofillValue()
-UnflaggedApi: android.view.contentcapture.ViewNode#getClassName():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getClassName()
-UnflaggedApi: android.view.contentcapture.ViewNode#getContentDescription():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getContentDescription()
-UnflaggedApi: android.view.contentcapture.ViewNode#getExtras():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getExtras()
-UnflaggedApi: android.view.contentcapture.ViewNode#getHeight():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getHeight()
-UnflaggedApi: android.view.contentcapture.ViewNode#getHint():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getHint()
-UnflaggedApi: android.view.contentcapture.ViewNode#getHintIdEntry():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getHintIdEntry()
-UnflaggedApi: android.view.contentcapture.ViewNode#getId():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getId()
-UnflaggedApi: android.view.contentcapture.ViewNode#getIdEntry():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getIdEntry()
-UnflaggedApi: android.view.contentcapture.ViewNode#getIdPackage():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getIdPackage()
-UnflaggedApi: android.view.contentcapture.ViewNode#getIdType():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getIdType()
-UnflaggedApi: android.view.contentcapture.ViewNode#getInputType():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getInputType()
-UnflaggedApi: android.view.contentcapture.ViewNode#getLeft():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getLeft()
-UnflaggedApi: android.view.contentcapture.ViewNode#getLocaleList():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getLocaleList()
-UnflaggedApi: android.view.contentcapture.ViewNode#getMaxTextEms():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getMaxTextEms()
-UnflaggedApi: android.view.contentcapture.ViewNode#getMaxTextLength():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getMaxTextLength()
-UnflaggedApi: android.view.contentcapture.ViewNode#getMinTextEms():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getMinTextEms()
-UnflaggedApi: android.view.contentcapture.ViewNode#getReceiveContentMimeTypes():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getReceiveContentMimeTypes()
-UnflaggedApi: android.view.contentcapture.ViewNode#getScrollX():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getScrollX()
-UnflaggedApi: android.view.contentcapture.ViewNode#getScrollY():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getScrollY()
-UnflaggedApi: android.view.contentcapture.ViewNode#getText():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getText()
-UnflaggedApi: android.view.contentcapture.ViewNode#getTextBackgroundColor():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getTextBackgroundColor()
-UnflaggedApi: android.view.contentcapture.ViewNode#getTextColor():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getTextColor()
-UnflaggedApi: android.view.contentcapture.ViewNode#getTextIdEntry():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getTextIdEntry()
-UnflaggedApi: android.view.contentcapture.ViewNode#getTextLineBaselines():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getTextLineBaselines()
-UnflaggedApi: android.view.contentcapture.ViewNode#getTextLineCharOffsets():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getTextLineCharOffsets()
-UnflaggedApi: android.view.contentcapture.ViewNode#getTextSelectionEnd():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getTextSelectionEnd()
-UnflaggedApi: android.view.contentcapture.ViewNode#getTextSelectionStart():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getTextSelectionStart()
-UnflaggedApi: android.view.contentcapture.ViewNode#getTextSize():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getTextSize()
-UnflaggedApi: android.view.contentcapture.ViewNode#getTextStyle():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getTextStyle()
-UnflaggedApi: android.view.contentcapture.ViewNode#getTop():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getTop()
-UnflaggedApi: android.view.contentcapture.ViewNode#getVisibility():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getVisibility()
-UnflaggedApi: android.view.contentcapture.ViewNode#getWidth():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getWidth()
-UnflaggedApi: android.view.contentcapture.ViewNode#isAccessibilityFocused():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.isAccessibilityFocused()
-UnflaggedApi: android.view.contentcapture.ViewNode#isActivated():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.isActivated()
-UnflaggedApi: android.view.contentcapture.ViewNode#isAssistBlocked():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.isAssistBlocked()
-UnflaggedApi: android.view.contentcapture.ViewNode#isCheckable():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.isCheckable()
-UnflaggedApi: android.view.contentcapture.ViewNode#isChecked():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.isChecked()
-UnflaggedApi: android.view.contentcapture.ViewNode#isClickable():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.isClickable()
-UnflaggedApi: android.view.contentcapture.ViewNode#isContextClickable():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.isContextClickable()
-UnflaggedApi: android.view.contentcapture.ViewNode#isEnabled():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.isEnabled()
-UnflaggedApi: android.view.contentcapture.ViewNode#isFocusable():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.isFocusable()
-UnflaggedApi: android.view.contentcapture.ViewNode#isFocused():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.isFocused()
-UnflaggedApi: android.view.contentcapture.ViewNode#isLongClickable():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.isLongClickable()
-UnflaggedApi: android.view.contentcapture.ViewNode#isOpaque():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.isOpaque()
-UnflaggedApi: android.view.contentcapture.ViewNode#isSelected():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.isSelected()
-UnflaggedApi: android.view.translation.UiTranslationSpec#equals(Object):
- New API must be flagged with @FlaggedApi: method android.view.translation.UiTranslationSpec.equals(Object)
-UnflaggedApi: android.view.translation.UiTranslationSpec#hashCode():
- New API must be flagged with @FlaggedApi: method android.view.translation.UiTranslationSpec.hashCode()
-UnflaggedApi: android.view.translation.UiTranslationSpec#toString():
- New API must be flagged with @FlaggedApi: method android.view.translation.UiTranslationSpec.toString()
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index a4cc446..40c6fa8 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -3506,10 +3506,6 @@
field public static final int FLAG_IS_ACCESSIBILITY_EVENT = 2048; // 0x800
}
- public static final class MotionEvent.PointerCoords {
- method public boolean isResampled();
- }
-
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD}) public @interface RemotableViewMethod {
method public abstract String asyncImpl() default "";
}
diff --git a/core/api/test-lint-baseline.txt b/core/api/test-lint-baseline.txt
index 1aaedab..107be8b 100644
--- a/core/api/test-lint-baseline.txt
+++ b/core/api/test-lint-baseline.txt
@@ -1,985 +1,341 @@
// Baseline format: 1.0
-AcronymName: android.app.NotificationChannel#isImportanceLockedByOEM():
- Acronyms should not be capitalized in method names: was `isImportanceLockedByOEM`, should this be `isImportanceLockedByOem`?
-AcronymName: android.app.NotificationChannel#setImportanceLockedByOEM(boolean):
- Acronyms should not be capitalized in method names: was `setImportanceLockedByOEM`, should this be `setImportanceLockedByOem`?
+KotlinKeyword: android.app.Notification#when:
+ Avoid field names that are Kotlin hard keywords ("when"); see https://android.github.io/kotlin-guides/interop.html#no-hard-keywords
-ArrayReturn: android.media.AudioRecordingConfiguration#AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String, int, boolean, int, android.media.audiofx.AudioEffect.Descriptor[], android.media.audiofx.AudioEffect.Descriptor[]) parameter #10:
- Method parameter should be Collection<Descriptor> (or subclass) instead of raw array; was `android.media.audiofx.AudioEffect.Descriptor[]`
-ArrayReturn: android.media.AudioRecordingConfiguration#AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String, int, boolean, int, android.media.audiofx.AudioEffect.Descriptor[], android.media.audiofx.AudioEffect.Descriptor[]) parameter #11:
- Method parameter should be Collection<Descriptor> (or subclass) instead of raw array; was `android.media.audiofx.AudioEffect.Descriptor[]`
-ArrayReturn: android.view.Display#getSupportedWideColorGamut():
- Method should return Collection<ColorSpace> (or subclass) instead of raw array; was `android.graphics.ColorSpace[]`
-ArrayReturn: android.view.FocusFinder#sort(android.view.View[], int, int, android.view.ViewGroup, boolean) parameter #0:
- Method parameter should be Collection<View> (or subclass) instead of raw array; was `android.view.View[]`
-ArrayReturn: android.view.contentcapture.ViewNode.ViewStructureImpl#setAutofillOptions(CharSequence[]) parameter #0:
- Method parameter should be Collection<CharSequence> (or subclass) instead of raw array; was `java.lang.CharSequence[]`
-
-
-AutoBoxing: android.os.VintfObject#getTargetFrameworkCompatibilityMatrixVersion():
- Must avoid boxed primitives (`java.lang.Long`)
-
-
-BuilderSetStyle: android.os.StrictMode.ThreadPolicy.Builder#detectExplicitGc():
- Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.os.StrictMode.ThreadPolicy.Builder.detectExplicitGc()
-BuilderSetStyle: android.os.StrictMode.VmPolicy.Builder#permitIncorrectContextUse():
- Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.os.StrictMode.VmPolicy.Builder.permitIncorrectContextUse()
-
-
-ConcreteCollection: android.content.AutofillOptions#disabledActivities:
- Field type is concrete collection (`android.util.ArrayMap`); must be higher-level interface
-ConcreteCollection: android.content.AutofillOptions#whitelistedActivitiesForAugmentedAutofill:
- Field type is concrete collection (`android.util.ArraySet`); must be higher-level interface
-ConcreteCollection: android.content.ContentCaptureOptions#ContentCaptureOptions(int, int, int, int, int, android.util.ArraySet<android.content.ComponentName>) parameter #5:
- Parameter type is concrete collection (`android.util.ArraySet`); must be higher-level interface
-ConcreteCollection: android.content.ContentCaptureOptions#whitelistedComponents:
- Field type is concrete collection (`android.util.ArraySet`); must be higher-level interface
-ConcreteCollection: android.database.sqlite.SQLiteDebug.PagerStats#dbStats:
- Field type is concrete collection (`java.util.ArrayList`); must be higher-level interface
-ConcreteCollection: android.service.autofill.CompositeUserData#getFieldClassificationAlgorithms():
- Return type is concrete collection (`android.util.ArrayMap`); must be higher-level interface
-ConcreteCollection: android.service.autofill.CompositeUserData#getFieldClassificationArgs():
- Return type is concrete collection (`android.util.ArrayMap`); must be higher-level interface
-ConcreteCollection: android.service.autofill.InternalTransformation#batchApply(android.service.autofill.ValueFinder, android.widget.RemoteViews, java.util.ArrayList<android.util.Pair<java.lang.Integer,android.service.autofill.InternalTransformation>>) parameter #2:
- Parameter type is concrete collection (`java.util.ArrayList`); must be higher-level interface
-ConcreteCollection: android.service.autofill.UserData#getFieldClassificationAlgorithms():
- Return type is concrete collection (`android.util.ArrayMap`); must be higher-level interface
-
-
-ContextFirst: android.os.VibrationEffect#get(android.net.Uri, android.content.Context) parameter #1:
- Context is distinct, so it must be the first argument (method `get`)
-
-
-EndsWithImpl: android.view.contentcapture.ViewNode.ViewStructureImpl:
- Don't expose your implementation details: `ViewStructureImpl` ends with `Impl`
-
-
-Enum: android.view.inspector.InspectableProperty.ValueType:
- Enums are discouraged in Android APIs
-
-
-EqualsAndHashCode: android.os.StrictMode.ViolationInfo#hashCode():
- Must override both equals and hashCode; missing one in android.os.StrictMode.ViolationInfo
-
-
-ExecutorRegistration: android.media.audiofx.AudioEffect#setParameterListener(android.media.audiofx.AudioEffect.OnParameterChangeListener):
- Registration methods should have overload that accepts delivery Executor: `setParameterListener`
-ExecutorRegistration: android.permission.PermissionControllerManager#countPermissionApps(java.util.List<java.lang.String>, int, android.permission.PermissionControllerManager.OnCountPermissionAppsResultCallback, android.os.Handler):
- Registration methods should have overload that accepts delivery Executor: `countPermissionApps`
-ExecutorRegistration: android.permission.PermissionControllerManager#getAppPermissions(String, android.permission.PermissionControllerManager.OnGetAppPermissionResultCallback, android.os.Handler):
- Registration methods should have overload that accepts delivery Executor: `getAppPermissions`
-ExecutorRegistration: android.service.watchdog.ExplicitHealthCheckService#setCallback(android.os.RemoteCallback):
- Registration methods should have overload that accepts delivery Executor: `setCallback`
-ExecutorRegistration: android.window.WindowOrganizer#applySyncTransaction(android.window.WindowContainerTransaction, android.window.WindowContainerTransactionCallback):
- Registration methods should have overload that accepts delivery Executor: `applySyncTransaction`
-
-
-ForbiddenSuperClass: android.app.AppDetailsActivity:
- AppDetailsActivity should not extend `Activity`. Activity subclasses are impossible to compose. Expose a composable API instead.
-
-
-GenericException: android.service.autofill.CharSequenceTransformation#apply(android.service.autofill.ValueFinder, android.widget.RemoteViews, int):
- Methods must not throw generic exceptions (`java.lang.Exception`)
-GenericException: android.service.autofill.DateTransformation#apply(android.service.autofill.ValueFinder, android.widget.RemoteViews, int):
- Methods must not throw generic exceptions (`java.lang.Exception`)
-GenericException: android.service.autofill.ImageTransformation#apply(android.service.autofill.ValueFinder, android.widget.RemoteViews, int):
- Methods must not throw generic exceptions (`java.lang.Exception`)
-
-
-GetterSetterNames: android.net.NetworkPolicyManager#getRestrictBackground():
- Symmetric method for `setRestrictBackground` must be named `isRestrictBackground`; was `getRestrictBackground`
-
-
-IntentBuilderName: android.hardware.soundtrigger.KeyphraseEnrollmentInfo#getManageKeyphraseIntent(int, String, java.util.Locale):
- Methods creating an Intent should be named `create<Foo>Intent()`, was `getManageKeyphraseIntent`
-
-
-IntentName: android.provider.Settings.Secure#VOICE_INTERACTION_SERVICE:
- Intent action constant name must be ACTION_FOO: VOICE_INTERACTION_SERVICE
-IntentName: android.provider.Telephony.Sms.Intents#SMS_CARRIER_PROVISION_ACTION:
- Intent action constant name must be ACTION_FOO: SMS_CARRIER_PROVISION_ACTION
-
-
-KotlinOperator: android.os.PackageTagsList#contains(android.os.PackageTagsList):
- Method can be invoked as a "in" operator from Kotlin: `contains` (this is usually desirable; just make sure it makes sense for this type of object)
-KotlinOperator: android.util.SparseArrayMap#get(int, K):
- Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
-
-
-ListenerLast: android.permission.PermissionControllerManager#countPermissionApps(java.util.List<java.lang.String>, int, android.permission.PermissionControllerManager.OnCountPermissionAppsResultCallback, android.os.Handler) parameter #3:
- Listeners should always be at end of argument list (method `countPermissionApps`)
-ListenerLast: android.permission.PermissionControllerManager#getAppPermissions(String, android.permission.PermissionControllerManager.OnGetAppPermissionResultCallback, android.os.Handler) parameter #2:
- Listeners should always be at end of argument list (method `getAppPermissions`)
-
-
-ManagerConstructor: android.content.pm.ShortcutManager#ShortcutManager(android.content.Context):
- Managers must always be obtained from Context; no direct constructors
-
-
-MinMaxConstant: android.os.UserHandle#MIN_SECONDARY_USER_ID:
- If min/max could change in future, make them dynamic methods: android.os.UserHandle#MIN_SECONDARY_USER_ID
-MinMaxConstant: android.view.autofill.AutofillManager#MAX_TEMP_AUGMENTED_SERVICE_DURATION_MS:
- If min/max could change in future, make them dynamic methods: android.view.autofill.AutofillManager#MAX_TEMP_AUGMENTED_SERVICE_DURATION_MS
-
-
-MissingGetterMatchingBuilder: android.media.VolumeShaper.Configuration.Builder#setOptionFlags(int):
- android.media.VolumeShaper.Configuration does not declare a `getOptionFlags()` method matching method android.media.VolumeShaper.Configuration.Builder.setOptionFlags(int)
-MissingGetterMatchingBuilder: android.media.audiopolicy.AudioPolicy.Builder#setIsTestFocusPolicy(boolean):
- android.media.audiopolicy.AudioPolicy does not declare a `isIsTestFocusPolicy()` method matching method android.media.audiopolicy.AudioPolicy.Builder.setIsTestFocusPolicy(boolean)
-MissingGetterMatchingBuilder: android.security.keystore.KeyGenParameterSpec.Builder#setUniqueIdIncluded(boolean):
- android.security.keystore.KeyGenParameterSpec does not declare a `isUniqueIdIncluded()` method matching method android.security.keystore.KeyGenParameterSpec.Builder.setUniqueIdIncluded(boolean)
-MissingGetterMatchingBuilder: android.telecom.ConnectionRequest.Builder#setIsAdhocConferenceCall(boolean):
- android.telecom.ConnectionRequest does not declare a `isIsAdhocConferenceCall()` method matching method android.telecom.ConnectionRequest.Builder.setIsAdhocConferenceCall(boolean)
-MissingGetterMatchingBuilder: android.telecom.ConnectionRequest.Builder#setRttPipeFromInCall(android.os.ParcelFileDescriptor):
- android.telecom.ConnectionRequest does not declare a `getRttPipeFromInCall()` method matching method android.telecom.ConnectionRequest.Builder.setRttPipeFromInCall(android.os.ParcelFileDescriptor)
-MissingGetterMatchingBuilder: android.telecom.ConnectionRequest.Builder#setRttPipeToInCall(android.os.ParcelFileDescriptor):
- android.telecom.ConnectionRequest does not declare a `getRttPipeToInCall()` method matching method android.telecom.ConnectionRequest.Builder.setRttPipeToInCall(android.os.ParcelFileDescriptor)
-MissingGetterMatchingBuilder: android.telecom.ConnectionRequest.Builder#setShouldShowIncomingCallUi(boolean):
- android.telecom.ConnectionRequest does not declare a `shouldShowIncomingCallUi()` method matching method android.telecom.ConnectionRequest.Builder.setShouldShowIncomingCallUi(boolean)
-MissingGetterMatchingBuilder: android.view.Display.Mode.Builder#setResolution(int, int):
- android.view.Display.Mode does not declare a `getResolution()` method matching method android.view.Display.Mode.Builder.setResolution(int,int)
-
-
-MissingNullability: android.app.Activity#onMovedToDisplay(int, android.content.res.Configuration) parameter #1:
- Missing nullability on parameter `config` in method `onMovedToDisplay`
-MissingNullability: android.app.ActivityManager#alwaysShowUnsupportedCompileSdkWarning(android.content.ComponentName) parameter #0:
- Missing nullability on parameter `activity` in method `alwaysShowUnsupportedCompileSdkWarning`
-MissingNullability: android.app.ActivityManager#holdLock(android.os.IBinder, int) parameter #0:
- Missing nullability on parameter `token` in method `holdLock`
-MissingNullability: android.app.ActivityManager#scheduleApplicationInfoChanged(java.util.List<java.lang.String>, int) parameter #0:
- Missing nullability on parameter `packages` in method `scheduleApplicationInfoChanged`
-MissingNullability: android.app.ActivityManager.TaskDescription#getIconFilename():
- Missing nullability on method `getIconFilename` return
-MissingNullability: android.app.ActivityTaskManager#clearLaunchParamsForPackages(java.util.List<java.lang.String>) parameter #0:
- Missing nullability on parameter `packageNames` in method `clearLaunchParamsForPackages`
-MissingNullability: android.app.ActivityTaskManager#resizeTask(int, android.graphics.Rect) parameter #1:
- Missing nullability on parameter `bounds` in method `resizeTask`
-MissingNullability: android.app.ActivityTaskManager#supportsMultiWindow(android.content.Context) parameter #0:
- Missing nullability on parameter `context` in method `supportsMultiWindow`
-MissingNullability: android.app.ActivityTaskManager#supportsSplitScreenMultiWindow(android.content.Context) parameter #0:
- Missing nullability on parameter `context` in method `supportsSplitScreenMultiWindow`
MissingNullability: android.app.AppDetailsActivity#onCreate(android.os.Bundle) parameter #0:
Missing nullability on parameter `savedInstanceState` in method `onCreate`
-MissingNullability: android.app.AppOpsManager#isOperationActive(int, int, String) parameter #2:
- Missing nullability on parameter `packageName` in method `isOperationActive`
-MissingNullability: android.app.AppOpsManager#opToPermission(int):
- Missing nullability on method `opToPermission` return
-MissingNullability: android.app.AppOpsManager#permissionToOpCode(String) parameter #0:
- Missing nullability on parameter `permission` in method `permissionToOpCode`
-MissingNullability: android.app.AppOpsManager#setMode(int, int, String, int) parameter #2:
- Missing nullability on parameter `packageName` in method `setMode`
-MissingNullability: android.app.NotificationManager#allowAssistantAdjustment(String) parameter #0:
- Missing nullability on parameter `capability` in method `allowAssistantAdjustment`
-MissingNullability: android.app.NotificationManager#disallowAssistantAdjustment(String) parameter #0:
- Missing nullability on parameter `capability` in method `disallowAssistantAdjustment`
-MissingNullability: android.app.NotificationManager#getEffectsSuppressor():
- Missing nullability on method `getEffectsSuppressor` return
-MissingNullability: android.app.TimePickerDialog#getTimePicker():
- Missing nullability on method `getTimePicker` return
-MissingNullability: android.app.WindowConfiguration#compareTo(android.app.WindowConfiguration) parameter #0:
- Missing nullability on parameter `that` in method `compareTo`
-MissingNullability: android.app.WindowConfiguration#getAppBounds():
- Missing nullability on method `getAppBounds` return
-MissingNullability: android.app.WindowConfiguration#getBounds():
- Missing nullability on method `getBounds` return
-MissingNullability: android.app.WindowConfiguration#setAppBounds(android.graphics.Rect) parameter #0:
- Missing nullability on parameter `rect` in method `setAppBounds`
-MissingNullability: android.app.WindowConfiguration#setBounds(android.graphics.Rect) parameter #0:
- Missing nullability on parameter `rect` in method `setBounds`
-MissingNullability: android.app.WindowConfiguration#setTo(android.app.WindowConfiguration) parameter #0:
- Missing nullability on parameter `other` in method `setTo`
-MissingNullability: android.app.WindowConfiguration#writeToParcel(android.os.Parcel, int) parameter #0:
- Missing nullability on parameter `dest` in method `writeToParcel`
-MissingNullability: android.app.admin.DevicePolicyManager#getOwnerInstalledCaCerts(android.os.UserHandle):
- Missing nullability on method `getOwnerInstalledCaCerts` return
-MissingNullability: android.app.admin.SecurityLog.SecurityEvent#SecurityEvent(long, byte[]) parameter #1:
- Missing nullability on parameter `data` in method `SecurityEvent`
-MissingNullability: android.app.prediction.AppPredictor#getSessionId():
- Missing nullability on method `getSessionId` return
-MissingNullability: android.content.AutofillOptions#forWhitelistingItself():
- Missing nullability on method `forWhitelistingItself` return
-MissingNullability: android.content.AutofillOptions#writeToParcel(android.os.Parcel, int) parameter #0:
- Missing nullability on parameter `parcel` in method `writeToParcel`
-MissingNullability: android.content.ContentCaptureOptions#forWhitelistingItself():
- Missing nullability on method `forWhitelistingItself` return
-MissingNullability: android.content.ContentCaptureOptions#writeToParcel(android.os.Parcel, int) parameter #0:
- Missing nullability on parameter `parcel` in method `writeToParcel`
-MissingNullability: android.content.ContentResolver#getSyncAdapterPackagesForAuthorityAsUser(String, int):
- Missing nullability on method `getSyncAdapterPackagesForAuthorityAsUser` return
-MissingNullability: android.content.ContentResolver#getSyncAdapterPackagesForAuthorityAsUser(String, int) parameter #0:
- Missing nullability on parameter `authority` in method `getSyncAdapterPackagesForAuthorityAsUser`
-MissingNullability: android.content.pm.ActivityInfo#isTranslucentOrFloating(android.content.res.TypedArray) parameter #0:
- Missing nullability on parameter `attributes` in method `isTranslucentOrFloating`
-MissingNullability: android.content.pm.LauncherApps#LauncherApps(android.content.Context) parameter #0:
- Missing nullability on parameter `context` in method `LauncherApps`
-MissingNullability: android.content.pm.PackageManager#getHoldLockToken():
- Missing nullability on method `getHoldLockToken` return
-MissingNullability: android.content.pm.PackageManager#getNamesForUids(int[]) parameter #0:
- Missing nullability on parameter `uids` in method `getNamesForUids`
-MissingNullability: android.content.pm.PackageManager#holdLock(android.os.IBinder, int) parameter #0:
- Missing nullability on parameter `token` in method `holdLock`
-MissingNullability: android.content.pm.ShortcutManager#ShortcutManager(android.content.Context) parameter #0:
- Missing nullability on parameter `context` in method `ShortcutManager`
-MissingNullability: android.content.pm.UserInfo#UserInfo(android.content.pm.UserInfo) parameter #0:
- Missing nullability on parameter `orig` in method `UserInfo`
-MissingNullability: android.content.pm.UserInfo#UserInfo(int, String, String, int) parameter #1:
- Missing nullability on parameter `name` in method `UserInfo`
-MissingNullability: android.content.pm.UserInfo#UserInfo(int, String, String, int) parameter #2:
- Missing nullability on parameter `iconPath` in method `UserInfo`
-MissingNullability: android.content.pm.UserInfo#UserInfo(int, String, String, int, String) parameter #1:
- Missing nullability on parameter `name` in method `UserInfo`
-MissingNullability: android.content.pm.UserInfo#UserInfo(int, String, String, int, String) parameter #2:
- Missing nullability on parameter `iconPath` in method `UserInfo`
-MissingNullability: android.content.pm.UserInfo#UserInfo(int, String, String, int, String) parameter #4:
- Missing nullability on parameter `userType` in method `UserInfo`
-MissingNullability: android.content.pm.UserInfo#UserInfo(int, String, int) parameter #1:
- Missing nullability on parameter `name` in method `UserInfo`
-MissingNullability: android.content.pm.UserInfo#getUserHandle():
- Missing nullability on method `getUserHandle` return
-MissingNullability: android.content.pm.UserInfo#iconPath:
- Missing nullability on field `iconPath` in class `class android.content.pm.UserInfo`
-MissingNullability: android.content.pm.UserInfo#lastLoggedInFingerprint:
- Missing nullability on field `lastLoggedInFingerprint` in class `class android.content.pm.UserInfo`
-MissingNullability: android.content.pm.UserInfo#name:
- Missing nullability on field `name` in class `class android.content.pm.UserInfo`
-MissingNullability: android.content.pm.UserInfo#userType:
- Missing nullability on field `userType` in class `class android.content.pm.UserInfo`
-MissingNullability: android.content.pm.UserInfo#writeToParcel(android.os.Parcel, int) parameter #0:
- Missing nullability on parameter `dest` in method `writeToParcel`
-MissingNullability: android.content.res.AssetManager#getOverlayablesToString(String) parameter #0:
- Missing nullability on parameter `packageName` in method `getOverlayablesToString`
-MissingNullability: android.content.res.Configuration#windowConfiguration:
- Missing nullability on field `windowConfiguration` in class `class android.content.res.Configuration`
-MissingNullability: android.database.sqlite.SQLiteDebug#dump(android.util.Printer, String[]) parameter #0:
- Missing nullability on parameter `printer` in method `dump`
-MissingNullability: android.database.sqlite.SQLiteDebug#dump(android.util.Printer, String[]) parameter #1:
- Missing nullability on parameter `args` in method `dump`
-MissingNullability: android.database.sqlite.SQLiteDebug#getDatabaseInfo():
- Missing nullability on method `getDatabaseInfo` return
-MissingNullability: android.database.sqlite.SQLiteDebug.DbStats#DbStats(String, long, long, int, int, int, int) parameter #0:
- Missing nullability on parameter `dbName` in method `DbStats`
-MissingNullability: android.database.sqlite.SQLiteDebug.DbStats#cache:
- Missing nullability on field `cache` in class `class android.database.sqlite.SQLiteDebug.DbStats`
-MissingNullability: android.database.sqlite.SQLiteDebug.DbStats#dbName:
- Missing nullability on field `dbName` in class `class android.database.sqlite.SQLiteDebug.DbStats`
-MissingNullability: android.database.sqlite.SQLiteDebug.PagerStats#dbStats:
- Missing nullability on field `dbStats` in class `class android.database.sqlite.SQLiteDebug.PagerStats`
-MissingNullability: android.database.sqlite.SQLiteDirectCursorDriver#SQLiteDirectCursorDriver(android.database.sqlite.SQLiteDatabase, String, String, android.os.CancellationSignal) parameter #0:
- Missing nullability on parameter `db` in method `SQLiteDirectCursorDriver`
-MissingNullability: android.database.sqlite.SQLiteDirectCursorDriver#SQLiteDirectCursorDriver(android.database.sqlite.SQLiteDatabase, String, String, android.os.CancellationSignal) parameter #1:
- Missing nullability on parameter `sql` in method `SQLiteDirectCursorDriver`
-MissingNullability: android.database.sqlite.SQLiteDirectCursorDriver#SQLiteDirectCursorDriver(android.database.sqlite.SQLiteDatabase, String, String, android.os.CancellationSignal) parameter #2:
- Missing nullability on parameter `editTable` in method `SQLiteDirectCursorDriver`
-MissingNullability: android.database.sqlite.SQLiteDirectCursorDriver#SQLiteDirectCursorDriver(android.database.sqlite.SQLiteDatabase, String, String, android.os.CancellationSignal) parameter #3:
- Missing nullability on parameter `cancellationSignal` in method `SQLiteDirectCursorDriver`
-MissingNullability: android.database.sqlite.SQLiteDirectCursorDriver#cursorRequeried(android.database.Cursor) parameter #0:
- Missing nullability on parameter `cursor` in method `cursorRequeried`
-MissingNullability: android.database.sqlite.SQLiteDirectCursorDriver#query(android.database.sqlite.SQLiteDatabase.CursorFactory, String[]):
- Missing nullability on method `query` return
-MissingNullability: android.database.sqlite.SQLiteDirectCursorDriver#query(android.database.sqlite.SQLiteDatabase.CursorFactory, String[]) parameter #0:
- Missing nullability on parameter `factory` in method `query`
-MissingNullability: android.database.sqlite.SQLiteDirectCursorDriver#query(android.database.sqlite.SQLiteDatabase.CursorFactory, String[]) parameter #1:
- Missing nullability on parameter `selectionArgs` in method `query`
-MissingNullability: android.database.sqlite.SQLiteDirectCursorDriver#setBindArguments(String[]) parameter #0:
- Missing nullability on parameter `bindArgs` in method `setBindArguments`
-MissingNullability: android.database.sqlite.SQLiteGlobal#getDefaultJournalMode():
- Missing nullability on method `getDefaultJournalMode` return
-MissingNullability: android.database.sqlite.SQLiteGlobal#getDefaultSyncMode():
- Missing nullability on method `getDefaultSyncMode` return
-MissingNullability: android.database.sqlite.SQLiteGlobal#getWALSyncMode():
- Missing nullability on method `getWALSyncMode` return
-MissingNullability: android.graphics.ImageDecoder#createSource(android.content.res.Resources, java.io.InputStream, int) parameter #0:
- Missing nullability on parameter `res` in method `createSource`
-MissingNullability: android.graphics.drawable.AdaptiveIconDrawable#getSafeZone():
- Missing nullability on method `getSafeZone` return
-MissingNullability: android.graphics.drawable.ColorDrawable#getXfermode():
- Missing nullability on method `getXfermode` return
-MissingNullability: android.hardware.camera2.CameraManager#getCameraIdListNoLazy():
- Missing nullability on method `getCameraIdListNoLazy` return
-MissingNullability: android.hardware.display.AmbientDisplayConfiguration#AmbientDisplayConfiguration(android.content.Context) parameter #0:
- Missing nullability on parameter `context` in method `AmbientDisplayConfiguration`
-MissingNullability: android.media.AudioAttributes#getSdkUsages():
- Missing nullability on method `getSdkUsages` return
-MissingNullability: android.media.AudioManager#getPublicStreamTypes():
- Missing nullability on method `getPublicStreamTypes` return
-MissingNullability: android.media.AudioRecordingConfiguration#AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String) parameter #3:
- Missing nullability on parameter `clientFormat` in method `AudioRecordingConfiguration`
-MissingNullability: android.media.AudioRecordingConfiguration#AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String) parameter #4:
- Missing nullability on parameter `devFormat` in method `AudioRecordingConfiguration`
-MissingNullability: android.media.AudioRecordingConfiguration#AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String) parameter #6:
- Missing nullability on parameter `packageName` in method `AudioRecordingConfiguration`
-MissingNullability: android.media.AudioRecordingConfiguration#AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String, int, boolean, int, android.media.audiofx.AudioEffect.Descriptor[], android.media.audiofx.AudioEffect.Descriptor[]) parameter #10:
- Missing nullability on parameter `clientEffects` in method `AudioRecordingConfiguration`
-MissingNullability: android.media.AudioRecordingConfiguration#AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String, int, boolean, int, android.media.audiofx.AudioEffect.Descriptor[], android.media.audiofx.AudioEffect.Descriptor[]) parameter #11:
- Missing nullability on parameter `deviceEffects` in method `AudioRecordingConfiguration`
-MissingNullability: android.media.AudioRecordingConfiguration#AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String, int, boolean, int, android.media.audiofx.AudioEffect.Descriptor[], android.media.audiofx.AudioEffect.Descriptor[]) parameter #3:
- Missing nullability on parameter `clientFormat` in method `AudioRecordingConfiguration`
-MissingNullability: android.media.AudioRecordingConfiguration#AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String, int, boolean, int, android.media.audiofx.AudioEffect.Descriptor[], android.media.audiofx.AudioEffect.Descriptor[]) parameter #4:
- Missing nullability on parameter `devFormat` in method `AudioRecordingConfiguration`
-MissingNullability: android.media.AudioRecordingConfiguration#AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String, int, boolean, int, android.media.audiofx.AudioEffect.Descriptor[], android.media.audiofx.AudioEffect.Descriptor[]) parameter #6:
- Missing nullability on parameter `packageName` in method `AudioRecordingConfiguration`
-MissingNullability: android.media.PlaybackParams#setAudioStretchMode(int):
- Missing nullability on method `setAudioStretchMode` return
-MissingNullability: android.media.audiofx.AudioEffect#EFFECT_TYPE_NULL:
- Missing nullability on field `EFFECT_TYPE_NULL` in class `class android.media.audiofx.AudioEffect`
-MissingNullability: android.media.audiofx.AudioEffect#byteArrayToInt(byte[]) parameter #0:
- Missing nullability on parameter `valueBuf` in method `byteArrayToInt`
-MissingNullability: android.media.audiofx.AudioEffect#byteArrayToShort(byte[]) parameter #0:
- Missing nullability on parameter `valueBuf` in method `byteArrayToShort`
-MissingNullability: android.media.audiofx.AudioEffect#getParameter(byte[], byte[]) parameter #0:
- Missing nullability on parameter `param` in method `getParameter`
-MissingNullability: android.media.audiofx.AudioEffect#getParameter(byte[], byte[]) parameter #1:
- Missing nullability on parameter `value` in method `getParameter`
-MissingNullability: android.media.audiofx.AudioEffect#getParameter(int, byte[]) parameter #1:
- Missing nullability on parameter `value` in method `getParameter`
-MissingNullability: android.media.audiofx.AudioEffect#getParameter(int, int[]) parameter #1:
- Missing nullability on parameter `value` in method `getParameter`
-MissingNullability: android.media.audiofx.AudioEffect#getParameter(int, short[]) parameter #1:
- Missing nullability on parameter `value` in method `getParameter`
-MissingNullability: android.media.audiofx.AudioEffect#getParameter(int[], short[]) parameter #0:
- Missing nullability on parameter `param` in method `getParameter`
-MissingNullability: android.media.audiofx.AudioEffect#getParameter(int[], short[]) parameter #1:
- Missing nullability on parameter `value` in method `getParameter`
-MissingNullability: android.media.audiofx.AudioEffect#intToByteArray(int):
- Missing nullability on method `intToByteArray` return
-MissingNullability: android.media.audiofx.AudioEffect#isEffectTypeAvailable(java.util.UUID) parameter #0:
- Missing nullability on parameter `type` in method `isEffectTypeAvailable`
-MissingNullability: android.media.audiofx.AudioEffect#setParameter(byte[], byte[]) parameter #0:
- Missing nullability on parameter `param` in method `setParameter`
-MissingNullability: android.media.audiofx.AudioEffect#setParameter(byte[], byte[]) parameter #1:
- Missing nullability on parameter `value` in method `setParameter`
-MissingNullability: android.media.audiofx.AudioEffect#setParameter(int, byte[]) parameter #1:
- Missing nullability on parameter `value` in method `setParameter`
-MissingNullability: android.media.audiofx.AudioEffect#setParameter(int[], byte[]) parameter #0:
- Missing nullability on parameter `param` in method `setParameter`
-MissingNullability: android.media.audiofx.AudioEffect#setParameter(int[], byte[]) parameter #1:
- Missing nullability on parameter `value` in method `setParameter`
-MissingNullability: android.media.audiofx.AudioEffect#setParameter(int[], int[]) parameter #0:
- Missing nullability on parameter `param` in method `setParameter`
-MissingNullability: android.media.audiofx.AudioEffect#setParameter(int[], int[]) parameter #1:
- Missing nullability on parameter `value` in method `setParameter`
-MissingNullability: android.media.audiofx.AudioEffect#setParameterListener(android.media.audiofx.AudioEffect.OnParameterChangeListener) parameter #0:
- Missing nullability on parameter `listener` in method `setParameterListener`
-MissingNullability: android.media.audiofx.AudioEffect#shortToByteArray(short):
- Missing nullability on method `shortToByteArray` return
-MissingNullability: android.media.audiofx.AudioEffect.Descriptor#Descriptor(android.os.Parcel) parameter #0:
- Missing nullability on parameter `in` in method `Descriptor`
-MissingNullability: android.media.audiofx.AudioEffect.Descriptor#writeToParcel(android.os.Parcel) parameter #0:
- Missing nullability on parameter `dest` in method `writeToParcel`
-MissingNullability: android.media.audiofx.AudioEffect.OnParameterChangeListener#onParameterChange(android.media.audiofx.AudioEffect, int, byte[], byte[]) parameter #0:
- Missing nullability on parameter `effect` in method `onParameterChange`
-MissingNullability: android.media.audiofx.AudioEffect.OnParameterChangeListener#onParameterChange(android.media.audiofx.AudioEffect, int, byte[], byte[]) parameter #2:
- Missing nullability on parameter `param` in method `onParameterChange`
-MissingNullability: android.media.audiofx.AudioEffect.OnParameterChangeListener#onParameterChange(android.media.audiofx.AudioEffect, int, byte[], byte[]) parameter #3:
- Missing nullability on parameter `value` in method `onParameterChange`
-MissingNullability: android.os.Build#is64BitAbi(String) parameter #0:
- Missing nullability on parameter `abi` in method `is64BitAbi`
-MissingNullability: android.os.Build.VERSION#ACTIVE_CODENAMES:
- Missing nullability on field `ACTIVE_CODENAMES` in class `class android.os.Build.VERSION`
-MissingNullability: android.os.Environment#buildPath(java.io.File, java.lang.String...):
- Missing nullability on method `buildPath` return
-MissingNullability: android.os.Environment#buildPath(java.io.File, java.lang.String...) parameter #0:
- Missing nullability on parameter `base` in method `buildPath`
-MissingNullability: android.os.Environment#buildPath(java.io.File, java.lang.String...) parameter #1:
- Missing nullability on parameter `segments` in method `buildPath`
-MissingNullability: android.os.FileUtils#contains(java.io.File, java.io.File) parameter #0:
- Missing nullability on parameter `dir` in method `contains`
-MissingNullability: android.os.FileUtils#contains(java.io.File, java.io.File) parameter #1:
- Missing nullability on parameter `file` in method `contains`
-MissingNullability: android.os.ParcelFileDescriptor#getFile(java.io.FileDescriptor):
- Missing nullability on method `getFile` return
-MissingNullability: android.os.ParcelFileDescriptor#getFile(java.io.FileDescriptor) parameter #0:
- Missing nullability on parameter `fd` in method `getFile`
-MissingNullability: android.os.StrictMode#setViolationLogger(android.os.StrictMode.ViolationLogger) parameter #0:
- Missing nullability on parameter `listener` in method `setViolationLogger`
-MissingNullability: android.os.StrictMode.ViolationInfo#ViolationInfo(android.os.Parcel) parameter #0:
- Missing nullability on parameter `in` in method `ViolationInfo`
-MissingNullability: android.os.StrictMode.ViolationInfo#ViolationInfo(android.os.Parcel, boolean) parameter #0:
- Missing nullability on parameter `in` in method `ViolationInfo`
-MissingNullability: android.os.StrictMode.ViolationInfo#broadcastIntentAction:
- Missing nullability on field `broadcastIntentAction` in class `class android.os.StrictMode.ViolationInfo`
-MissingNullability: android.os.StrictMode.ViolationInfo#dump(android.util.Printer, String) parameter #0:
- Missing nullability on parameter `pw` in method `dump`
-MissingNullability: android.os.StrictMode.ViolationInfo#dump(android.util.Printer, String) parameter #1:
- Missing nullability on parameter `prefix` in method `dump`
-MissingNullability: android.os.StrictMode.ViolationInfo#getStackTrace():
- Missing nullability on method `getStackTrace` return
-MissingNullability: android.os.StrictMode.ViolationInfo#getViolationClass():
- Missing nullability on method `getViolationClass` return
-MissingNullability: android.os.StrictMode.ViolationInfo#getViolationDetails():
- Missing nullability on method `getViolationDetails` return
-MissingNullability: android.os.StrictMode.ViolationInfo#tags:
- Missing nullability on field `tags` in class `class android.os.StrictMode.ViolationInfo`
-MissingNullability: android.os.StrictMode.ViolationInfo#writeToParcel(android.os.Parcel, int) parameter #0:
- Missing nullability on parameter `dest` in method `writeToParcel`
-MissingNullability: android.os.StrictMode.ViolationLogger#log(android.os.StrictMode.ViolationInfo) parameter #0:
- Missing nullability on parameter `info` in method `log`
-MissingNullability: android.os.VibrationEffect#RINGTONES:
- Missing nullability on field `RINGTONES` in class `class android.os.VibrationEffect`
-MissingNullability: android.os.VibrationEffect#get(android.net.Uri, android.content.Context) parameter #0:
- Missing nullability on parameter `uri` in method `get`
-MissingNullability: android.os.VibrationEffect#get(android.net.Uri, android.content.Context) parameter #1:
- Missing nullability on parameter `context` in method `get`
-MissingNullability: android.os.VibrationEffect#get(int):
- Missing nullability on method `get` return
-MissingNullability: android.os.VibrationEffect#get(int, boolean):
- Missing nullability on method `get` return
-MissingNullability: android.os.VintfObject#getHalNamesAndVersions():
- Missing nullability on method `getHalNamesAndVersions` return
-MissingNullability: android.os.VintfObject#getSepolicyVersion():
- Missing nullability on method `getSepolicyVersion` return
-MissingNullability: android.os.VintfObject#getTargetFrameworkCompatibilityMatrixVersion():
- Missing nullability on method `getTargetFrameworkCompatibilityMatrixVersion` return
-MissingNullability: android.os.VintfObject#getVndkSnapshots():
- Missing nullability on method `getVndkSnapshots` return
-MissingNullability: android.os.VintfObject#report():
- Missing nullability on method `report` return
-MissingNullability: android.os.VintfRuntimeInfo#getCpuInfo():
- Missing nullability on method `getCpuInfo` return
-MissingNullability: android.os.VintfRuntimeInfo#getHardwareId():
- Missing nullability on method `getHardwareId` return
-MissingNullability: android.os.VintfRuntimeInfo#getKernelVersion():
- Missing nullability on method `getKernelVersion` return
-MissingNullability: android.os.VintfRuntimeInfo#getNodeName():
- Missing nullability on method `getNodeName` return
-MissingNullability: android.os.VintfRuntimeInfo#getOsName():
- Missing nullability on method `getOsName` return
-MissingNullability: android.os.VintfRuntimeInfo#getOsRelease():
- Missing nullability on method `getOsRelease` return
-MissingNullability: android.os.VintfRuntimeInfo#getOsVersion():
- Missing nullability on method `getOsVersion` return
-MissingNullability: android.os.WorkSource#add(int, String) parameter #1:
- Missing nullability on parameter `name` in method `add`
-MissingNullability: android.os.health.HealthKeys.Constants#Constants(Class) parameter #0:
- Missing nullability on parameter `clazz` in method `Constants`
-MissingNullability: android.os.health.HealthKeys.Constants#getDataType():
- Missing nullability on method `getDataType` return
-MissingNullability: android.os.health.HealthKeys.Constants#getKeys(int):
- Missing nullability on method `getKeys` return
-MissingNullability: android.os.health.HealthStats#HealthStats(android.os.Parcel) parameter #0:
- Missing nullability on parameter `in` in method `HealthStats`
-MissingNullability: android.os.health.HealthStatsParceler#HealthStatsParceler(android.os.Parcel) parameter #0:
- Missing nullability on parameter `in` in method `HealthStatsParceler`
-MissingNullability: android.os.health.HealthStatsParceler#HealthStatsParceler(android.os.health.HealthStatsWriter) parameter #0:
- Missing nullability on parameter `writer` in method `HealthStatsParceler`
-MissingNullability: android.os.health.HealthStatsParceler#getHealthStats():
- Missing nullability on method `getHealthStats` return
-MissingNullability: android.os.health.HealthStatsParceler#writeToParcel(android.os.Parcel, int) parameter #0:
- Missing nullability on parameter `out` in method `writeToParcel`
-MissingNullability: android.os.health.HealthStatsWriter#HealthStatsWriter(android.os.health.HealthKeys.Constants) parameter #0:
- Missing nullability on parameter `constants` in method `HealthStatsWriter`
-MissingNullability: android.os.health.HealthStatsWriter#addMeasurements(int, String, long) parameter #1:
- Missing nullability on parameter `name` in method `addMeasurements`
-MissingNullability: android.os.health.HealthStatsWriter#addStats(int, String, android.os.health.HealthStatsWriter) parameter #1:
- Missing nullability on parameter `name` in method `addStats`
-MissingNullability: android.os.health.HealthStatsWriter#addStats(int, String, android.os.health.HealthStatsWriter) parameter #2:
- Missing nullability on parameter `value` in method `addStats`
-MissingNullability: android.os.health.HealthStatsWriter#addTimers(int, String, android.os.health.TimerStat) parameter #1:
- Missing nullability on parameter `name` in method `addTimers`
-MissingNullability: android.os.health.HealthStatsWriter#addTimers(int, String, android.os.health.TimerStat) parameter #2:
- Missing nullability on parameter `value` in method `addTimers`
-MissingNullability: android.os.health.HealthStatsWriter#flattenToParcel(android.os.Parcel) parameter #0:
- Missing nullability on parameter `out` in method `flattenToParcel`
-MissingNullability: android.os.storage.StorageVolume#getPath():
- Missing nullability on method `getPath` return
-MissingNullability: android.provider.CalendarContract.Calendars#SYNC_WRITABLE_COLUMNS:
- Missing nullability on field `SYNC_WRITABLE_COLUMNS` in class `class android.provider.CalendarContract.Calendars`
-MissingNullability: android.provider.CalendarContract.Events#SYNC_WRITABLE_COLUMNS:
- Missing nullability on field `SYNC_WRITABLE_COLUMNS` in class `class android.provider.CalendarContract.Events`
-MissingNullability: android.provider.ContactsContract.RawContactsEntity#CORP_CONTENT_URI:
- Missing nullability on field `CORP_CONTENT_URI` in class `class android.provider.ContactsContract.RawContactsEntity`
-MissingNullability: android.security.keystore.KeyProtection.Builder#setBoundToSpecificSecureUserId(long):
- Missing nullability on method `setBoundToSpecificSecureUserId` return
-MissingNullability: android.service.autofill.CompositeUserData#getCategoryIds():
- Missing nullability on method `getCategoryIds` return
-MissingNullability: android.service.autofill.CompositeUserData#getDefaultFieldClassificationArgs():
- Missing nullability on method `getDefaultFieldClassificationArgs` return
-MissingNullability: android.service.autofill.CompositeUserData#getFieldClassificationAlgorithms():
- Missing nullability on method `getFieldClassificationAlgorithms` return
-MissingNullability: android.service.autofill.CompositeUserData#getFieldClassificationArgs():
- Missing nullability on method `getFieldClassificationArgs` return
-MissingNullability: android.service.autofill.CompositeUserData#getValues():
- Missing nullability on method `getValues` return
-MissingNullability: android.service.autofill.CompositeUserData#writeToParcel(android.os.Parcel, int) parameter #0:
- Missing nullability on parameter `parcel` in method `writeToParcel`
-MissingNullability: android.service.autofill.UserData#getFieldClassificationAlgorithms():
- Missing nullability on method `getFieldClassificationAlgorithms` return
-MissingNullability: android.telecom.Call.Details#getTelecomCallId():
- Missing nullability on method `getTelecomCallId` return
-MissingNullability: android.telephony.ServiceState#addNetworkRegistrationInfo(android.telephony.NetworkRegistrationInfo) parameter #0:
- Missing nullability on parameter `nri` in method `addNetworkRegistrationInfo`
-MissingNullability: android.telephony.ServiceState#setCellBandwidths(int[]) parameter #0:
- Missing nullability on parameter `bandwidths` in method `setCellBandwidths`
-MissingNullability: android.telephony.SmsManager#checkSmsShortCodeDestination(String, String) parameter #0:
- Missing nullability on parameter `destAddress` in method `checkSmsShortCodeDestination`
-MissingNullability: android.telephony.SmsManager#checkSmsShortCodeDestination(String, String) parameter #1:
- Missing nullability on parameter `countryIso` in method `checkSmsShortCodeDestination`
-MissingNullability: android.telephony.TelephonyManager#HAL_VERSION_UNKNOWN:
- Missing nullability on field `HAL_VERSION_UNKNOWN` in class `class android.telephony.TelephonyManager`
-MissingNullability: android.telephony.TelephonyManager#HAL_VERSION_UNSUPPORTED:
- Missing nullability on field `HAL_VERSION_UNSUPPORTED` in class `class android.telephony.TelephonyManager`
-MissingNullability: android.telephony.TelephonyManager#getLine1AlphaTag():
- Missing nullability on method `getLine1AlphaTag` return
-MissingNullability: android.telephony.TelephonyManager#getRadioHalVersion():
- Missing nullability on method `getRadioHalVersion` return
-MissingNullability: android.telephony.TelephonyManager#setCarrierTestOverride(String, String, String, String, String, String, String, String, String) parameter #0:
- Missing nullability on parameter `mccmnc` in method `setCarrierTestOverride`
-MissingNullability: android.telephony.TelephonyManager#setCarrierTestOverride(String, String, String, String, String, String, String, String, String) parameter #1:
- Missing nullability on parameter `imsi` in method `setCarrierTestOverride`
-MissingNullability: android.telephony.TelephonyManager#setCarrierTestOverride(String, String, String, String, String, String, String, String, String) parameter #2:
- Missing nullability on parameter `iccid` in method `setCarrierTestOverride`
-MissingNullability: android.telephony.TelephonyManager#setCarrierTestOverride(String, String, String, String, String, String, String, String, String) parameter #3:
- Missing nullability on parameter `gid1` in method `setCarrierTestOverride`
-MissingNullability: android.telephony.TelephonyManager#setCarrierTestOverride(String, String, String, String, String, String, String, String, String) parameter #4:
- Missing nullability on parameter `gid2` in method `setCarrierTestOverride`
-MissingNullability: android.telephony.TelephonyManager#setCarrierTestOverride(String, String, String, String, String, String, String, String, String) parameter #5:
- Missing nullability on parameter `plmn` in method `setCarrierTestOverride`
-MissingNullability: android.telephony.TelephonyManager#setCarrierTestOverride(String, String, String, String, String, String, String, String, String) parameter #6:
- Missing nullability on parameter `spn` in method `setCarrierTestOverride`
-MissingNullability: android.telephony.TelephonyManager#setCarrierTestOverride(String, String, String, String, String, String, String, String, String) parameter #7:
- Missing nullability on parameter `carrierPriviledgeRules` in method `setCarrierTestOverride`
-MissingNullability: android.telephony.TelephonyManager#setCarrierTestOverride(String, String, String, String, String, String, String, String, String) parameter #8:
- Missing nullability on parameter `apn` in method `setCarrierTestOverride`
-MissingNullability: android.text.Selection.MemoryTextWatcher#afterTextChanged(android.text.Editable) parameter #0:
- Missing nullability on parameter `s` in method `afterTextChanged`
-MissingNullability: android.text.Selection.MemoryTextWatcher#beforeTextChanged(CharSequence, int, int, int) parameter #0:
- Missing nullability on parameter `s` in method `beforeTextChanged`
-MissingNullability: android.text.Selection.MemoryTextWatcher#onTextChanged(CharSequence, int, int, int) parameter #0:
- Missing nullability on parameter `s` in method `onTextChanged`
-MissingNullability: android.transition.TransitionManager#getTransition(android.transition.Scene):
- Missing nullability on method `getTransition` return
-MissingNullability: android.transition.TransitionManager#getTransition(android.transition.Scene) parameter #0:
- Missing nullability on parameter `scene` in method `getTransition`
-MissingNullability: android.util.FeatureFlagUtils#getAllFeatureFlags():
- Missing nullability on method `getAllFeatureFlags` return
-MissingNullability: android.util.FeatureFlagUtils#isEnabled(android.content.Context, String) parameter #0:
- Missing nullability on parameter `context` in method `isEnabled`
-MissingNullability: android.util.FeatureFlagUtils#isEnabled(android.content.Context, String) parameter #1:
- Missing nullability on parameter `feature` in method `isEnabled`
-MissingNullability: android.util.FeatureFlagUtils#setEnabled(android.content.Context, String, boolean) parameter #0:
- Missing nullability on parameter `context` in method `setEnabled`
-MissingNullability: android.util.FeatureFlagUtils#setEnabled(android.content.Context, String, boolean) parameter #1:
- Missing nullability on parameter `feature` in method `setEnabled`
-MissingNullability: android.util.TimeUtils#formatDuration(long):
- Missing nullability on method `formatDuration` return
-MissingNullability: android.util.proto.EncodedBuffer#dumpBuffers(String) parameter #0:
- Missing nullability on parameter `tag` in method `dumpBuffers`
-MissingNullability: android.util.proto.EncodedBuffer#dumpByteString(String, String, byte[]) parameter #0:
- Missing nullability on parameter `tag` in method `dumpByteString`
-MissingNullability: android.util.proto.EncodedBuffer#dumpByteString(String, String, byte[]) parameter #1:
- Missing nullability on parameter `prefix` in method `dumpByteString`
-MissingNullability: android.util.proto.EncodedBuffer#dumpByteString(String, String, byte[]) parameter #2:
- Missing nullability on parameter `buf` in method `dumpByteString`
-MissingNullability: android.util.proto.EncodedBuffer#getBytes(int):
- Missing nullability on method `getBytes` return
-MissingNullability: android.util.proto.EncodedBuffer#getDebugString():
- Missing nullability on method `getDebugString` return
-MissingNullability: android.util.proto.EncodedBuffer#writeRawBuffer(byte[]) parameter #0:
- Missing nullability on parameter `val` in method `writeRawBuffer`
-MissingNullability: android.util.proto.EncodedBuffer#writeRawBuffer(byte[], int, int) parameter #0:
- Missing nullability on parameter `val` in method `writeRawBuffer`
-MissingNullability: android.util.proto.ProtoParseException#ProtoParseException(String) parameter #0:
- Missing nullability on parameter `msg` in method `ProtoParseException`
-MissingNullability: android.util.proto.WireTypeMismatchException#WireTypeMismatchException(String) parameter #0:
- Missing nullability on parameter `msg` in method `WireTypeMismatchException`
-MissingNullability: android.view.Choreographer#postCallback(int, Runnable, Object) parameter #1:
- Missing nullability on parameter `action` in method `postCallback`
-MissingNullability: android.view.Choreographer#postCallback(int, Runnable, Object) parameter #2:
- Missing nullability on parameter `token` in method `postCallback`
-MissingNullability: android.view.Choreographer#postCallbackDelayed(int, Runnable, Object, long) parameter #1:
- Missing nullability on parameter `action` in method `postCallbackDelayed`
-MissingNullability: android.view.Choreographer#postCallbackDelayed(int, Runnable, Object, long) parameter #2:
- Missing nullability on parameter `token` in method `postCallbackDelayed`
-MissingNullability: android.view.Choreographer#removeCallbacks(int, Runnable, Object) parameter #1:
- Missing nullability on parameter `action` in method `removeCallbacks`
-MissingNullability: android.view.Choreographer#removeCallbacks(int, Runnable, Object) parameter #2:
- Missing nullability on parameter `token` in method `removeCallbacks`
-MissingNullability: android.view.FocusFinder#sort(android.view.View[], int, int, android.view.ViewGroup, boolean) parameter #0:
- Missing nullability on parameter `views` in method `sort`
-MissingNullability: android.view.FocusFinder#sort(android.view.View[], int, int, android.view.ViewGroup, boolean) parameter #3:
- Missing nullability on parameter `root` in method `sort`
-MissingNullability: android.view.KeyEvent#actionToString(int):
- Missing nullability on method `actionToString` return
-MissingNullability: android.view.SurfaceControlViewHost#relayout(android.view.WindowManager.LayoutParams) parameter #0:
- Missing nullability on parameter `attrs` in method `relayout`
-MissingNullability: android.view.View#getTooltipView():
- Missing nullability on method `getTooltipView` return
-MissingNullability: android.view.View#isDefaultFocusHighlightNeeded(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable) parameter #0:
- Missing nullability on parameter `background` in method `isDefaultFocusHighlightNeeded`
-MissingNullability: android.view.View#isDefaultFocusHighlightNeeded(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable) parameter #1:
- Missing nullability on parameter `foreground` in method `isDefaultFocusHighlightNeeded`
-MissingNullability: android.view.ViewDebug#startRenderingCommandsCapture(android.view.View, java.util.concurrent.Executor, java.util.concurrent.Callable<java.io.OutputStream>) parameter #0:
- Missing nullability on parameter `tree` in method `startRenderingCommandsCapture`
-MissingNullability: android.view.ViewDebug#startRenderingCommandsCapture(android.view.View, java.util.concurrent.Executor, java.util.concurrent.Callable<java.io.OutputStream>) parameter #1:
- Missing nullability on parameter `executor` in method `startRenderingCommandsCapture`
-MissingNullability: android.view.ViewDebug#startRenderingCommandsCapture(android.view.View, java.util.concurrent.Executor, java.util.concurrent.Callable<java.io.OutputStream>) parameter #2:
- Missing nullability on parameter `callback` in method `startRenderingCommandsCapture`
-MissingNullability: android.view.WindowManager#holdLock(android.os.IBinder, int) parameter #0:
- Missing nullability on parameter `token` in method `holdLock`
-MissingNullability: android.view.WindowManager.LayoutParams#accessibilityTitle:
- Missing nullability on field `accessibilityTitle` in class `class android.view.WindowManager.LayoutParams`
-MissingNullability: android.view.accessibility.AccessibilityNodeInfo#writeToParcelNoRecycle(android.os.Parcel, int) parameter #0:
- Missing nullability on parameter `parcel` in method `writeToParcelNoRecycle`
-MissingNullability: android.view.accessibility.AccessibilityWindowInfo#setNumInstancesInUseCounter(java.util.concurrent.atomic.AtomicInteger) parameter #0:
- Missing nullability on parameter `counter` in method `setNumInstancesInUseCounter`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#asyncNewChild(int):
- Missing nullability on method `asyncNewChild` return
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#getAutofillId():
- Missing nullability on method `getAutofillId` return
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#getExtras():
- Missing nullability on method `getExtras` return
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#getHint():
- Missing nullability on method `getHint` return
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#getNode():
- Missing nullability on method `getNode` return
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#getTempRect():
- Missing nullability on method `getTempRect` return
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#getText():
- Missing nullability on method `getText` return
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#newChild(int):
- Missing nullability on method `newChild` return
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#newHtmlInfoBuilder(String):
- Missing nullability on method `newHtmlInfoBuilder` return
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#newHtmlInfoBuilder(String) parameter #0:
- Missing nullability on parameter `tagName` in method `newHtmlInfoBuilder`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setAutofillHints(String[]) parameter #0:
- Missing nullability on parameter `hints` in method `setAutofillHints`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setAutofillId(android.view.autofill.AutofillId) parameter #0:
- Missing nullability on parameter `id` in method `setAutofillId`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setAutofillId(android.view.autofill.AutofillId, int) parameter #0:
- Missing nullability on parameter `parentId` in method `setAutofillId`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setAutofillOptions(CharSequence[]) parameter #0:
- Missing nullability on parameter `options` in method `setAutofillOptions`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setAutofillValue(android.view.autofill.AutofillValue) parameter #0:
- Missing nullability on parameter `value` in method `setAutofillValue`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setClassName(String) parameter #0:
- Missing nullability on parameter `className` in method `setClassName`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setContentDescription(CharSequence) parameter #0:
- Missing nullability on parameter `contentDescription` in method `setContentDescription`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setHint(CharSequence) parameter #0:
- Missing nullability on parameter `hint` in method `setHint`
MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setHintIdEntry(String) parameter #0:
Missing nullability on parameter `entryName` in method `setHintIdEntry`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setHtmlInfo(android.view.ViewStructure.HtmlInfo) parameter #0:
- Missing nullability on parameter `htmlInfo` in method `setHtmlInfo`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setId(int, String, String, String) parameter #1:
- Missing nullability on parameter `packageName` in method `setId`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setId(int, String, String, String) parameter #2:
- Missing nullability on parameter `typeName` in method `setId`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setId(int, String, String, String) parameter #3:
- Missing nullability on parameter `entryName` in method `setId`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setLocaleList(android.os.LocaleList) parameter #0:
- Missing nullability on parameter `localeList` in method `setLocaleList`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setText(CharSequence) parameter #0:
- Missing nullability on parameter `text` in method `setText`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setText(CharSequence, int, int) parameter #0:
- Missing nullability on parameter `text` in method `setText`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setTextLines(int[], int[]) parameter #0:
- Missing nullability on parameter `charOffsets` in method `setTextLines`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setTextLines(int[], int[]) parameter #1:
- Missing nullability on parameter `baselines` in method `setTextLines`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setTransformation(android.graphics.Matrix) parameter #0:
- Missing nullability on parameter `matrix` in method `setTransformation`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setWebDomain(String) parameter #0:
- Missing nullability on parameter `domain` in method `setWebDomain`
-MissingNullability: android.widget.CalendarView#getBoundsForDate(long, android.graphics.Rect) parameter #1:
- Missing nullability on parameter `outBounds` in method `getBoundsForDate`
MissingNullability: android.widget.ImageView#isDefaultFocusHighlightNeeded(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable) parameter #0:
Missing nullability on parameter `background` in method `isDefaultFocusHighlightNeeded`
MissingNullability: android.widget.ImageView#isDefaultFocusHighlightNeeded(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable) parameter #1:
Missing nullability on parameter `foreground` in method `isDefaultFocusHighlightNeeded`
-MissingNullability: android.widget.Magnifier#getMagnifierDefaultSize():
- Missing nullability on method `getMagnifierDefaultSize` return
-MissingNullability: android.widget.Magnifier#setOnOperationCompleteCallback(android.widget.Magnifier.Callback) parameter #0:
- Missing nullability on parameter `callback` in method `setOnOperationCompleteCallback`
-MissingNullability: android.widget.NumberPicker#getDisplayedValueForCurrentSelection():
- Missing nullability on method `getDisplayedValueForCurrentSelection` return
-MissingNullability: android.widget.PopupMenu#getMenuListView():
- Missing nullability on method `getMenuListView` return
-MissingNullability: android.widget.TimePicker#getAmView():
- Missing nullability on method `getAmView` return
-MissingNullability: android.widget.TimePicker#getHourView():
- Missing nullability on method `getHourView` return
-MissingNullability: android.widget.TimePicker#getMinuteView():
- Missing nullability on method `getMinuteView` return
-MissingNullability: android.widget.TimePicker#getPmView():
- Missing nullability on method `getPmView` return
-
-
-MutableBareField: android.content.AutofillOptions#appDisabledExpiration:
- Bare field appDisabledExpiration must be marked final, or moved behind accessors if mutable
-MutableBareField: android.content.AutofillOptions#augmentedAutofillEnabled:
- Bare field augmentedAutofillEnabled must be marked final, or moved behind accessors if mutable
-MutableBareField: android.content.AutofillOptions#disabledActivities:
- Bare field disabledActivities must be marked final, or moved behind accessors if mutable
-MutableBareField: android.content.AutofillOptions#whitelistedActivitiesForAugmentedAutofill:
- Bare field whitelistedActivitiesForAugmentedAutofill must be marked final, or moved behind accessors if mutable
-MutableBareField: android.content.pm.UserInfo#convertedFromPreCreated:
- Bare field convertedFromPreCreated must be marked final, or moved behind accessors if mutable
-MutableBareField: android.content.pm.UserInfo#creationTime:
- Bare field creationTime must be marked final, or moved behind accessors if mutable
-MutableBareField: android.content.pm.UserInfo#flags:
- Bare field flags must be marked final, or moved behind accessors if mutable
-MutableBareField: android.content.pm.UserInfo#guestToRemove:
- Bare field guestToRemove must be marked final, or moved behind accessors if mutable
-MutableBareField: android.content.pm.UserInfo#iconPath:
- Bare field iconPath must be marked final, or moved behind accessors if mutable
-MutableBareField: android.content.pm.UserInfo#id:
- Bare field id must be marked final, or moved behind accessors if mutable
-MutableBareField: android.content.pm.UserInfo#lastLoggedInFingerprint:
- Bare field lastLoggedInFingerprint must be marked final, or moved behind accessors if mutable
-MutableBareField: android.content.pm.UserInfo#lastLoggedInTime:
- Bare field lastLoggedInTime must be marked final, or moved behind accessors if mutable
-MutableBareField: android.content.pm.UserInfo#name:
- Bare field name must be marked final, or moved behind accessors if mutable
-MutableBareField: android.content.pm.UserInfo#partial:
- Bare field partial must be marked final, or moved behind accessors if mutable
-MutableBareField: android.content.pm.UserInfo#preCreated:
- Bare field preCreated must be marked final, or moved behind accessors if mutable
-MutableBareField: android.content.pm.UserInfo#profileBadge:
- Bare field profileBadge must be marked final, or moved behind accessors if mutable
-MutableBareField: android.content.pm.UserInfo#profileGroupId:
- Bare field profileGroupId must be marked final, or moved behind accessors if mutable
-MutableBareField: android.content.pm.UserInfo#restrictedProfileParentId:
- Bare field restrictedProfileParentId must be marked final, or moved behind accessors if mutable
-MutableBareField: android.content.pm.UserInfo#serialNumber:
- Bare field serialNumber must be marked final, or moved behind accessors if mutable
-MutableBareField: android.content.pm.UserInfo#userType:
- Bare field userType must be marked final, or moved behind accessors if mutable
-MutableBareField: android.database.sqlite.SQLiteDebug.DbStats#cache:
- Bare field cache must be marked final, or moved behind accessors if mutable
-MutableBareField: android.database.sqlite.SQLiteDebug.DbStats#dbName:
- Bare field dbName must be marked final, or moved behind accessors if mutable
-MutableBareField: android.database.sqlite.SQLiteDebug.DbStats#dbSize:
- Bare field dbSize must be marked final, or moved behind accessors if mutable
-MutableBareField: android.database.sqlite.SQLiteDebug.DbStats#lookaside:
- Bare field lookaside must be marked final, or moved behind accessors if mutable
-MutableBareField: android.database.sqlite.SQLiteDebug.DbStats#pageSize:
- Bare field pageSize must be marked final, or moved behind accessors if mutable
-MutableBareField: android.database.sqlite.SQLiteDebug.PagerStats#dbStats:
- Bare field dbStats must be marked final, or moved behind accessors if mutable
-MutableBareField: android.database.sqlite.SQLiteDebug.PagerStats#largestMemAlloc:
- Bare field largestMemAlloc must be marked final, or moved behind accessors if mutable
-MutableBareField: android.database.sqlite.SQLiteDebug.PagerStats#memoryUsed:
- Bare field memoryUsed must be marked final, or moved behind accessors if mutable
-MutableBareField: android.database.sqlite.SQLiteDebug.PagerStats#pageCacheOverflow:
- Bare field pageCacheOverflow must be marked final, or moved behind accessors if mutable
-MutableBareField: android.os.StrictMode.ViolationInfo#broadcastIntentAction:
- Bare field broadcastIntentAction must be marked final, or moved behind accessors if mutable
-MutableBareField: android.os.StrictMode.ViolationInfo#durationMillis:
- Bare field durationMillis must be marked final, or moved behind accessors if mutable
-MutableBareField: android.os.StrictMode.ViolationInfo#numAnimationsRunning:
- Bare field numAnimationsRunning must be marked final, or moved behind accessors if mutable
-MutableBareField: android.os.StrictMode.ViolationInfo#numInstances:
- Bare field numInstances must be marked final, or moved behind accessors if mutable
-MutableBareField: android.os.StrictMode.ViolationInfo#tags:
- Bare field tags must be marked final, or moved behind accessors if mutable
-MutableBareField: android.os.StrictMode.ViolationInfo#violationNumThisLoop:
- Bare field violationNumThisLoop must be marked final, or moved behind accessors if mutable
-MutableBareField: android.os.StrictMode.ViolationInfo#violationUptimeMillis:
- Bare field violationUptimeMillis must be marked final, or moved behind accessors if mutable
-
-
-NoByteOrShort: android.media.audiofx.AudioEffect#byteArrayToShort(byte[]):
- Should avoid odd sized primitives; use `int` instead of `short` in method android.media.audiofx.AudioEffect.byteArrayToShort(byte[])
-NoByteOrShort: android.media.audiofx.AudioEffect#setParameter(int, short) parameter #1:
- Should avoid odd sized primitives; use `int` instead of `short` in parameter value in android.media.audiofx.AudioEffect.setParameter(int param, short value)
-NoByteOrShort: android.media.audiofx.AudioEffect#shortToByteArray(short) parameter #0:
- Should avoid odd sized primitives; use `int` instead of `short` in parameter value in android.media.audiofx.AudioEffect.shortToByteArray(short value)
-NoByteOrShort: android.util.proto.EncodedBuffer#readRawByte():
- Should avoid odd sized primitives; use `int` instead of `byte` in method android.util.proto.EncodedBuffer.readRawByte()
-NoByteOrShort: android.util.proto.EncodedBuffer#writeRawByte(byte) parameter #0:
- Should avoid odd sized primitives; use `int` instead of `byte` in parameter val in android.util.proto.EncodedBuffer.writeRawByte(byte val)
-
-
-NoSettingsProvider: android.provider.Settings.Global#APP_OPS_CONSTANTS:
- New setting keys are not allowed (Field: APP_OPS_CONSTANTS); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Global#AUTOMATIC_POWER_SAVE_MODE:
- New setting keys are not allowed (Field: AUTOMATIC_POWER_SAVE_MODE); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Global#BATTERY_SAVER_CONSTANTS:
- New setting keys are not allowed (Field: BATTERY_SAVER_CONSTANTS); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Global#DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD:
- New setting keys are not allowed (Field: DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Global#DYNAMIC_POWER_SAVINGS_ENABLED:
- New setting keys are not allowed (Field: DYNAMIC_POWER_SAVINGS_ENABLED); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Global#HDR_CONVERSION_MODE:
- New setting keys are not allowed (Field: HDR_CONVERSION_MODE); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Global#HDR_FORCE_CONVERSION_TYPE:
- New setting keys are not allowed (Field: HDR_FORCE_CONVERSION_TYPE); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Global#HIDDEN_API_BLACKLIST_EXEMPTIONS:
- New setting keys are not allowed (Field: HIDDEN_API_BLACKLIST_EXEMPTIONS); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Global#HIDDEN_API_POLICY:
- New setting keys are not allowed (Field: HIDDEN_API_POLICY); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Global#HIDE_ERROR_DIALOGS:
- New setting keys are not allowed (Field: HIDE_ERROR_DIALOGS); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Global#LOW_POWER_MODE:
- New setting keys are not allowed (Field: LOW_POWER_MODE); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Global#LOW_POWER_MODE_STICKY:
- New setting keys are not allowed (Field: LOW_POWER_MODE_STICKY); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Global#OVERLAY_DISPLAY_DEVICES:
- New setting keys are not allowed (Field: OVERLAY_DISPLAY_DEVICES); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED:
- New setting keys are not allowed (Field: ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_MAGNIFICATION_CAPABILITY:
- New setting keys are not allowed (Field: ACCESSIBILITY_MAGNIFICATION_CAPABILITY); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE:
- New setting keys are not allowed (Field: ACCESSIBILITY_MAGNIFICATION_MODE); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_SHORTCUT_TARGET_SERVICE:
- New setting keys are not allowed (Field: ACCESSIBILITY_SHORTCUT_TARGET_SERVICE); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Secure#AUTOFILL_SERVICE:
- New setting keys are not allowed (Field: AUTOFILL_SERVICE); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Secure#BIOMETRIC_VIRTUAL_ENABLED:
- New setting keys are not allowed (Field: BIOMETRIC_VIRTUAL_ENABLED); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Secure#CONTENT_CAPTURE_ENABLED:
- New setting keys are not allowed (Field: CONTENT_CAPTURE_ENABLED); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Secure#DISABLED_PRINT_SERVICES:
- New setting keys are not allowed (Field: DISABLED_PRINT_SERVICES); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Secure#ENABLED_VR_LISTENERS:
- New setting keys are not allowed (Field: ENABLED_VR_LISTENERS); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Secure#IMMERSIVE_MODE_CONFIRMATIONS:
- New setting keys are not allowed (Field: IMMERSIVE_MODE_CONFIRMATIONS); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Secure#NOTIFICATION_BADGING:
- New setting keys are not allowed (Field: NOTIFICATION_BADGING); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Secure#POWER_MENU_LOCKED_SHOW_CONTENT:
- New setting keys are not allowed (Field: POWER_MENU_LOCKED_SHOW_CONTENT); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Secure#SYNC_PARENT_SOUNDS:
- New setting keys are not allowed (Field: SYNC_PARENT_SOUNDS); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Secure#VOICE_INTERACTION_SERVICE:
- New setting keys are not allowed (Field: VOICE_INTERACTION_SERVICE); use getters/setters in relevant manager class
-
-
-OnNameExpected: android.service.notification.ConditionProviderService#isBound():
- If implemented by developer, should follow the on<Something> style; otherwise consider marking final
-OnNameExpected: android.service.watchdog.ExplicitHealthCheckService#setCallback(android.os.RemoteCallback):
- If implemented by developer, should follow the on<Something> style; otherwise consider marking final
-
-
-PackageLayering: android.util.FeatureFlagUtils:
- Method parameter type `android.content.Context` violates package layering: nothing in `package android.util` should depend on `package android.content`
-
-
-ParcelConstructor: android.credentials.ui.ProviderData#ProviderData(android.os.Parcel):
- Parcelable inflation is exposed through CREATOR, not raw constructors, in android.credentials.ui.ProviderData
-ParcelConstructor: android.os.StrictMode.ViolationInfo#ViolationInfo(android.os.Parcel):
- Parcelable inflation is exposed through CREATOR, not raw constructors, in android.os.StrictMode.ViolationInfo
-ParcelConstructor: android.os.health.HealthStatsParceler#HealthStatsParceler(android.os.Parcel):
- Parcelable inflation is exposed through CREATOR, not raw constructors, in android.os.health.HealthStatsParceler
-
-
-ParcelCreator: android.app.WindowConfiguration:
- Parcelable requires a `CREATOR` field; missing in android.app.WindowConfiguration
-ParcelCreator: android.service.autofill.InternalOnClickAction:
- Parcelable requires a `CREATOR` field; missing in android.service.autofill.InternalOnClickAction
-ParcelCreator: android.service.autofill.InternalSanitizer:
- Parcelable requires a `CREATOR` field; missing in android.service.autofill.InternalSanitizer
-ParcelCreator: android.service.autofill.InternalTransformation:
- Parcelable requires a `CREATOR` field; missing in android.service.autofill.InternalTransformation
-ParcelCreator: android.service.autofill.InternalValidator:
- Parcelable requires a `CREATOR` field; missing in android.service.autofill.InternalValidator
-
-
-ParcelNotFinal: android.app.WindowConfiguration:
- Parcelable classes must be final: android.app.WindowConfiguration is not final
-ParcelNotFinal: android.content.pm.UserInfo:
- Parcelable classes must be final: android.content.pm.UserInfo is not final
-ParcelNotFinal: android.os.health.HealthStatsParceler:
- Parcelable classes must be final: android.os.health.HealthStatsParceler is not final
-ParcelNotFinal: android.service.autofill.InternalOnClickAction:
- Parcelable classes must be final: android.service.autofill.InternalOnClickAction is not final
-ParcelNotFinal: android.service.autofill.InternalSanitizer:
- Parcelable classes must be final: android.service.autofill.InternalSanitizer is not final
-ParcelNotFinal: android.service.autofill.InternalTransformation:
- Parcelable classes must be final: android.service.autofill.InternalTransformation is not final
-ParcelNotFinal: android.service.autofill.InternalValidator:
- Parcelable classes must be final: android.service.autofill.InternalValidator is not final
ProtectedMember: android.app.AppDetailsActivity#onCreate(android.os.Bundle):
Protected methods not allowed; must be public: method android.app.AppDetailsActivity.onCreate(android.os.Bundle)}
-ProtectedMember: android.view.View#resetResolvedDrawables():
- Protected methods not allowed; must be public: method android.view.View.resetResolvedDrawables()}
ProtectedMember: android.view.ViewGroup#resetResolvedDrawables():
Protected methods not allowed; must be public: method android.view.ViewGroup.resetResolvedDrawables()}
-RethrowRemoteException: android.app.ActivityManager#resumeAppSwitches():
- Methods calling system APIs should rethrow `RemoteException` as `RuntimeException` (but do not list it in the throws clause)
+SamShouldBeLast: android.animation.ValueAnimator#ofObject(android.animation.TypeEvaluator, java.lang.Object...):
+ SAM-compatible parameters (such as parameter 1, "evaluator", in android.animation.ValueAnimator.ofObject) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.app.Activity#convertToTranslucent(android.app.Activity.TranslucentConversionListener, android.app.ActivityOptions):
+ SAM-compatible parameters (such as parameter 1, "callback", in android.app.Activity.convertToTranslucent) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.app.ActivityManager#addOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener, int):
+ SAM-compatible parameters (such as parameter 1, "listener", in android.app.ActivityManager.addOnUidImportanceListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.app.AlarmManager#set(int, long, String, android.app.AlarmManager.OnAlarmListener, android.os.Handler):
+ SAM-compatible parameters (such as parameter 4, "listener", in android.app.AlarmManager.set) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.app.AlarmManager#set(int, long, long, long, android.app.AlarmManager.OnAlarmListener, android.os.Handler, android.os.WorkSource):
+ SAM-compatible parameters (such as parameter 5, "listener", in android.app.AlarmManager.set) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.app.AlarmManager#setExact(int, long, String, android.app.AlarmManager.OnAlarmListener, android.os.Handler):
+ SAM-compatible parameters (such as parameter 4, "listener", in android.app.AlarmManager.setExact) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.app.AlarmManager#setWindow(int, long, long, String, android.app.AlarmManager.OnAlarmListener, android.os.Handler):
+ SAM-compatible parameters (such as parameter 5, "listener", in android.app.AlarmManager.setWindow) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.app.PendingIntent#send(android.content.Context, int, android.content.Intent, android.app.PendingIntent.OnFinished, android.os.Handler):
+ SAM-compatible parameters (such as parameter 4, "onFinished", in android.app.PendingIntent.send) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.app.PendingIntent#send(android.content.Context, int, android.content.Intent, android.app.PendingIntent.OnFinished, android.os.Handler, String):
+ SAM-compatible parameters (such as parameter 4, "onFinished", in android.app.PendingIntent.send) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.app.PendingIntent#send(android.content.Context, int, android.content.Intent, android.app.PendingIntent.OnFinished, android.os.Handler, String, android.os.Bundle):
+ SAM-compatible parameters (such as parameter 4, "onFinished", in android.app.PendingIntent.send) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.app.PendingIntent#send(int, android.app.PendingIntent.OnFinished, android.os.Handler):
+ SAM-compatible parameters (such as parameter 2, "onFinished", in android.app.PendingIntent.send) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.app.UiAutomation#executeAndWaitForEvent(Runnable, android.app.UiAutomation.AccessibilityEventFilter, long):
+ SAM-compatible parameters (such as parameter 2, "filter", in android.app.UiAutomation.executeAndWaitForEvent) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.app.WallpaperManager#addOnColorsChangedListener(android.app.WallpaperManager.OnColorsChangedListener, android.os.Handler):
+ SAM-compatible parameters (such as parameter 1, "listener", in android.app.WallpaperManager.addOnColorsChangedListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.content.pm.ActivityInfo#dump(android.util.Printer, String):
+ SAM-compatible parameters (such as parameter 1, "pw", in android.content.pm.ActivityInfo.dump) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.content.pm.ApplicationInfo#dump(android.util.Printer, String):
+ SAM-compatible parameters (such as parameter 1, "pw", in android.content.pm.ApplicationInfo.dump) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.content.pm.ComponentInfo#dumpBack(android.util.Printer, String):
+ SAM-compatible parameters (such as parameter 1, "pw", in android.content.pm.ComponentInfo.dumpBack) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.content.pm.ComponentInfo#dumpFront(android.util.Printer, String):
+ SAM-compatible parameters (such as parameter 1, "pw", in android.content.pm.ComponentInfo.dumpFront) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.content.pm.PackageItemInfo#dumpBack(android.util.Printer, String):
+ SAM-compatible parameters (such as parameter 1, "pw", in android.content.pm.PackageItemInfo.dumpBack) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.content.pm.PackageItemInfo#dumpFront(android.util.Printer, String):
+ SAM-compatible parameters (such as parameter 1, "pw", in android.content.pm.PackageItemInfo.dumpFront) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.database.sqlite.SQLiteCursorDriver#query(android.database.sqlite.SQLiteDatabase.CursorFactory, String[]):
+ SAM-compatible parameters (such as parameter 1, "factory", in android.database.sqlite.SQLiteCursorDriver.query) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.graphics.drawable.AdaptiveIconDrawable#scheduleDrawable(android.graphics.drawable.Drawable, Runnable, long):
+ SAM-compatible parameters (such as parameter 2, "what", in android.graphics.drawable.AdaptiveIconDrawable.scheduleDrawable) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.graphics.drawable.Drawable#scheduleSelf(Runnable, long):
+ SAM-compatible parameters (such as parameter 1, "what", in android.graphics.drawable.Drawable.scheduleSelf) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.graphics.drawable.Drawable.Callback#scheduleDrawable(android.graphics.drawable.Drawable, Runnable, long):
+ SAM-compatible parameters (such as parameter 2, "what", in android.graphics.drawable.Drawable.Callback.scheduleDrawable) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.graphics.drawable.LayerDrawable#scheduleDrawable(android.graphics.drawable.Drawable, Runnable, long):
+ SAM-compatible parameters (such as parameter 2, "what", in android.graphics.drawable.LayerDrawable.scheduleDrawable) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.media.AudioManager#abandonAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes):
+ SAM-compatible parameters (such as parameter 1, "l", in android.media.AudioManager.abandonAudioFocus) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.media.AudioManager#requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes, int, int):
+ SAM-compatible parameters (such as parameter 1, "l", in android.media.AudioManager.requestAudioFocus) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.media.AudioManager#requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes, int, int, android.media.audiopolicy.AudioPolicy):
+ SAM-compatible parameters (such as parameter 1, "l", in android.media.AudioManager.requestAudioFocus) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.media.AudioManager#requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, int, int):
+ SAM-compatible parameters (such as parameter 1, "l", in android.media.AudioManager.requestAudioFocus) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.media.AudioRecord#addOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener, android.os.Handler):
+ SAM-compatible parameters (such as parameter 1, "listener", in android.media.AudioRecord.addOnRoutingChangedListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.media.AudioRecord#addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler):
+ SAM-compatible parameters (such as parameter 1, "listener", in android.media.AudioRecord.addOnRoutingChangedListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.media.AudioTrack#addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler):
+ SAM-compatible parameters (such as parameter 1, "listener", in android.media.AudioTrack.addOnRoutingChangedListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.media.AudioTrack#addOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener, android.os.Handler):
+ SAM-compatible parameters (such as parameter 1, "listener", in android.media.AudioTrack.addOnRoutingChangedListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.media.MediaCas#setEventListener(android.media.MediaCas.EventListener, android.os.Handler):
+ SAM-compatible parameters (such as parameter 1, "listener", in android.media.MediaCas.setEventListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.os.Parcel#createFixedArray(Class<T>, java.util.function.Function<android.os.IBinder,S>, int...):
+ SAM-compatible parameters (such as parameter 2, "asInterface", in android.os.Parcel.createFixedArray) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.security.KeyChain#choosePrivateKeyAlias(android.app.Activity, android.security.KeyChainAliasCallback, String[], java.security.Principal[], String, int, String):
+ SAM-compatible parameters (such as parameter 2, "response", in android.security.KeyChain.choosePrivateKeyAlias) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.security.KeyChain#choosePrivateKeyAlias(android.app.Activity, android.security.KeyChainAliasCallback, String[], java.security.Principal[], android.net.Uri, String):
+ SAM-compatible parameters (such as parameter 2, "response", in android.security.KeyChain.choosePrivateKeyAlias) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.view.Choreographer#postFrameCallbackDelayed(android.view.Choreographer.FrameCallback, long):
+ SAM-compatible parameters (such as parameter 1, "callback", in android.view.Choreographer.postFrameCallbackDelayed) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.view.View#postDelayed(Runnable, long):
+ SAM-compatible parameters (such as parameter 1, "action", in android.view.View.postDelayed) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.view.View#postOnAnimationDelayed(Runnable, long):
+ SAM-compatible parameters (such as parameter 1, "action", in android.view.View.postOnAnimationDelayed) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.view.View#scheduleDrawable(android.graphics.drawable.Drawable, Runnable, long):
+ SAM-compatible parameters (such as parameter 2, "what", in android.view.View.scheduleDrawable) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.view.Window#addOnFrameMetricsAvailableListener(android.view.Window.OnFrameMetricsAvailableListener, android.os.Handler):
+ SAM-compatible parameters (such as parameter 1, "listener", in android.view.Window.addOnFrameMetricsAvailableListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.view.accessibility.AccessibilityManager#addAccessibilityStateChangeListener(android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener, android.os.Handler):
+ SAM-compatible parameters (such as parameter 1, "listener", in android.view.accessibility.AccessibilityManager.addAccessibilityStateChangeListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.view.accessibility.AccessibilityManager#addTouchExplorationStateChangeListener(android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener, android.os.Handler):
+ SAM-compatible parameters (such as parameter 1, "listener", in android.view.accessibility.AccessibilityManager.addTouchExplorationStateChangeListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.view.inputmethod.InputMethodInfo#dump(android.util.Printer, String):
+ SAM-compatible parameters (such as parameter 1, "pw", in android.view.inputmethod.InputMethodInfo.dump) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.database.sqlite.SQLiteDebug#dump(android.util.Printer, String[]):
- SAM-compatible parameters (such as parameter 1, "printer", in android.database.sqlite.SQLiteDebug.dump) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.database.sqlite.SQLiteDirectCursorDriver#query(android.database.sqlite.SQLiteDatabase.CursorFactory, String[]):
- SAM-compatible parameters (such as parameter 1, "factory", in android.database.sqlite.SQLiteDirectCursorDriver.query) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.os.StrictMode.ViolationInfo#dump(android.util.Printer, String):
- SAM-compatible parameters (such as parameter 1, "pw", in android.os.StrictMode.ViolationInfo.dump) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.permission.PermissionControllerManager#countPermissionApps(java.util.List<java.lang.String>, int, android.permission.PermissionControllerManager.OnCountPermissionAppsResultCallback, android.os.Handler):
- SAM-compatible parameters (such as parameter 3, "callback", in android.permission.PermissionControllerManager.countPermissionApps) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.permission.PermissionControllerManager#getAppPermissions(String, android.permission.PermissionControllerManager.OnGetAppPermissionResultCallback, android.os.Handler):
- SAM-compatible parameters (such as parameter 2, "callback", in android.permission.PermissionControllerManager.getAppPermissions) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.service.autofill.CharSequenceTransformation#apply(android.service.autofill.ValueFinder, android.widget.RemoteViews, int):
- SAM-compatible parameters (such as parameter 1, "finder", in android.service.autofill.CharSequenceTransformation.apply) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.service.autofill.DateTransformation#apply(android.service.autofill.ValueFinder, android.widget.RemoteViews, int):
- SAM-compatible parameters (such as parameter 1, "finder", in android.service.autofill.DateTransformation.apply) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.service.autofill.ImageTransformation#apply(android.service.autofill.ValueFinder, android.widget.RemoteViews, int):
- SAM-compatible parameters (such as parameter 1, "finder", in android.service.autofill.ImageTransformation.apply) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.service.autofill.InternalTransformation#batchApply(android.service.autofill.ValueFinder, android.widget.RemoteViews, java.util.ArrayList<android.util.Pair<java.lang.Integer,android.service.autofill.InternalTransformation>>):
- SAM-compatible parameters (such as parameter 1, "finder", in android.service.autofill.InternalTransformation.batchApply) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.view.Choreographer#postCallback(int, Runnable, Object):
- SAM-compatible parameters (such as parameter 2, "action", in android.view.Choreographer.postCallback) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.view.Choreographer#postCallbackDelayed(int, Runnable, Object, long):
- SAM-compatible parameters (such as parameter 2, "action", in android.view.Choreographer.postCallbackDelayed) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.view.Choreographer#removeCallbacks(int, Runnable, Object):
- SAM-compatible parameters (such as parameter 2, "action", in android.view.Choreographer.removeCallbacks) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-
-
-StaticUtils: android.os.health.HealthKeys:
- Fully-static utility classes must not have constructor
-StaticUtils: android.service.autofill.InternalTransformation:
- Fully-static utility classes must not have constructor
-StaticUtils: android.util.FeatureFlagUtils:
- Fully-static utility classes must not have constructor
-
-
-StreamFiles: android.os.Environment#buildPath(java.io.File, java.lang.String...):
- Methods accepting `File` should also accept `FileDescriptor` or streams: method android.os.Environment.buildPath(java.io.File,java.lang.String...)
-StreamFiles: android.os.FileUtils#contains(java.io.File, java.io.File):
- Methods accepting `File` should also accept `FileDescriptor` or streams: method android.os.FileUtils.contains(java.io.File,java.io.File)
-
-
-UseIcu: android.hardware.soundtrigger.KeyphraseEnrollmentInfo#getKeyphraseMetadata(String, java.util.Locale) parameter #1:
- Type `java.util.Locale` should be replaced with richer ICU type `android.icu.util.ULocale`
-UseIcu: android.hardware.soundtrigger.KeyphraseEnrollmentInfo#getManageKeyphraseIntent(int, String, java.util.Locale) parameter #2:
- Type `java.util.Locale` should be replaced with richer ICU type `android.icu.util.ULocale`
-UseIcu: android.hardware.soundtrigger.KeyphraseMetadata#supportsLocale(java.util.Locale) parameter #0:
- Type `java.util.Locale` should be replaced with richer ICU type `android.icu.util.ULocale`
-
-
-UserHandle: android.app.admin.DevicePolicyManager#getOwnerInstalledCaCerts(android.os.UserHandle):
- When a method overload is needed to target a specific UserHandle, callers should be directed to use Context.createPackageContextAsUser() and re-obtain the relevant Manager, and no new API should be added
-UserHandle: android.app.usage.StorageStatsManager#queryCratesForPackage(java.util.UUID, String, android.os.UserHandle):
- When a method overload is needed to target a specific UserHandle, callers should be directed to use Context.createPackageContextAsUser() and re-obtain the relevant Manager, and no new API should be added
-UserHandle: android.app.usage.StorageStatsManager#queryCratesForUser(java.util.UUID, android.os.UserHandle):
- When a method overload is needed to target a specific UserHandle, callers should be directed to use Context.createPackageContextAsUser() and re-obtain the relevant Manager, and no new API should be added
-UserHandle: android.content.pm.PackageManager#getInstallReason(String, android.os.UserHandle):
- When a method overload is needed to target a specific UserHandle, callers should be directed to use Context.createPackageContextAsUser() and re-obtain the relevant Manager, and no new API should be added
-
-
-UserHandleName: android.content.AutofillOptions:
- Classes holding a set of parameters should be called `FooParams`, was `AutofillOptions`
-UserHandleName: android.content.ContentCaptureOptions:
- Classes holding a set of parameters should be called `FooParams`, was `ContentCaptureOptions`
-
-
-VisiblySynchronized: PsiThisExpression:
- Internal locks must not be exposed (synchronizing on this or class is still externally observable): method android.content.res.AssetManager.getApkPaths()
-VisiblySynchronized: android.content.res.AssetManager#getApkPaths():
- Internal locks must not be exposed (synchronizing on this or class is still externally observable): method android.content.res.AssetManager.getApkPaths()
-VisiblySynchronized: android.content.res.AssetManager#getLastResourceResolution():
- Internal locks must not be exposed (synchronizing on this or class is still externally observable): method android.content.res.AssetManager.getLastResourceResolution()
-VisiblySynchronized: android.content.res.AssetManager#getOverlayablesToString(String):
- Internal locks must not be exposed (synchronizing on this or class is still externally observable): method android.content.res.AssetManager.getOverlayablesToString(String)
-VisiblySynchronized: android.content.res.AssetManager#setResourceResolutionLoggingEnabled(boolean):
- Internal locks must not be exposed (synchronizing on this or class is still externally observable): method android.content.res.AssetManager.setResourceResolutionLoggingEnabled(boolean)
-VisiblySynchronized: android.os.MessageQueue#removeSyncBarrier(int):
- Internal locks must not be exposed (synchronizing on this or class is still externally observable): method android.os.MessageQueue.removeSyncBarrier(int)
+UnflaggedApi: android.Manifest.permission#MANAGE_REMOTE_AUTH:
+ New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_REMOTE_AUTH
+UnflaggedApi: android.Manifest.permission#START_ACTIVITIES_FROM_SDK_SANDBOX:
+ New API must be flagged with @FlaggedApi: field android.Manifest.permission.START_ACTIVITIES_FROM_SDK_SANDBOX
+UnflaggedApi: android.Manifest.permission#USE_REMOTE_AUTH:
+ New API must be flagged with @FlaggedApi: field android.Manifest.permission.USE_REMOTE_AUTH
+UnflaggedApi: android.app.admin.DevicePolicyIdentifiers#PERMITTED_INPUT_METHODS_POLICY:
+ New API must be flagged with @FlaggedApi: field android.app.admin.DevicePolicyIdentifiers.PERMITTED_INPUT_METHODS_POLICY
+UnflaggedApi: android.app.admin.DevicePolicyIdentifiers#PERSONAL_APPS_SUSPENDED_POLICY:
+ New API must be flagged with @FlaggedApi: field android.app.admin.DevicePolicyIdentifiers.PERSONAL_APPS_SUSPENDED_POLICY
+UnflaggedApi: android.app.admin.DevicePolicyIdentifiers#SCREEN_CAPTURE_DISABLED_POLICY:
+ New API must be flagged with @FlaggedApi: field android.app.admin.DevicePolicyIdentifiers.SCREEN_CAPTURE_DISABLED_POLICY
+UnflaggedApi: android.app.admin.DevicePolicyIdentifiers#USB_DATA_SIGNALING_POLICY:
+ New API must be flagged with @FlaggedApi: field android.app.admin.DevicePolicyIdentifiers.USB_DATA_SIGNALING_POLICY
+UnflaggedApi: android.companion.AssociationInfo.Builder:
+ New API must be flagged with @FlaggedApi: class android.companion.AssociationInfo.Builder
+UnflaggedApi: android.companion.AssociationInfo.Builder#Builder(android.companion.AssociationInfo):
+ New API must be flagged with @FlaggedApi: constructor android.companion.AssociationInfo.Builder(android.companion.AssociationInfo)
+UnflaggedApi: android.companion.AssociationInfo.Builder#Builder(int, int, String):
+ New API must be flagged with @FlaggedApi: constructor android.companion.AssociationInfo.Builder(int,int,String)
+UnflaggedApi: android.companion.AssociationInfo.Builder#build():
+ New API must be flagged with @FlaggedApi: method android.companion.AssociationInfo.Builder.build()
+UnflaggedApi: android.companion.AssociationInfo.Builder#setAssociatedDevice(android.companion.AssociatedDevice):
+ New API must be flagged with @FlaggedApi: method android.companion.AssociationInfo.Builder.setAssociatedDevice(android.companion.AssociatedDevice)
+UnflaggedApi: android.companion.AssociationInfo.Builder#setDeviceMacAddress(android.net.MacAddress):
+ New API must be flagged with @FlaggedApi: method android.companion.AssociationInfo.Builder.setDeviceMacAddress(android.net.MacAddress)
+UnflaggedApi: android.companion.AssociationInfo.Builder#setDeviceProfile(String):
+ New API must be flagged with @FlaggedApi: method android.companion.AssociationInfo.Builder.setDeviceProfile(String)
+UnflaggedApi: android.companion.AssociationInfo.Builder#setDisplayName(CharSequence):
+ New API must be flagged with @FlaggedApi: method android.companion.AssociationInfo.Builder.setDisplayName(CharSequence)
+UnflaggedApi: android.companion.AssociationInfo.Builder#setLastTimeConnected(long):
+ New API must be flagged with @FlaggedApi: method android.companion.AssociationInfo.Builder.setLastTimeConnected(long)
+UnflaggedApi: android.companion.AssociationInfo.Builder#setNotifyOnDeviceNearby(boolean):
+ New API must be flagged with @FlaggedApi: method android.companion.AssociationInfo.Builder.setNotifyOnDeviceNearby(boolean)
+UnflaggedApi: android.companion.AssociationInfo.Builder#setRevoked(boolean):
+ New API must be flagged with @FlaggedApi: method android.companion.AssociationInfo.Builder.setRevoked(boolean)
+UnflaggedApi: android.companion.AssociationInfo.Builder#setSelfManaged(boolean):
+ New API must be flagged with @FlaggedApi: method android.companion.AssociationInfo.Builder.setSelfManaged(boolean)
+UnflaggedApi: android.companion.AssociationInfo.Builder#setSystemDataSyncFlags(int):
+ New API must be flagged with @FlaggedApi: method android.companion.AssociationInfo.Builder.setSystemDataSyncFlags(int)
+UnflaggedApi: android.companion.AssociationInfo.Builder#setTag(String):
+ New API must be flagged with @FlaggedApi: method android.companion.AssociationInfo.Builder.setTag(String)
+UnflaggedApi: android.companion.AssociationInfo.Builder#setTimeApproved(long):
+ New API must be flagged with @FlaggedApi: method android.companion.AssociationInfo.Builder.setTimeApproved(long)
+UnflaggedApi: android.companion.CompanionDeviceManager#MESSAGE_REQUEST_PING:
+ New API must be flagged with @FlaggedApi: field android.companion.CompanionDeviceManager.MESSAGE_REQUEST_PING
+UnflaggedApi: android.companion.CompanionDeviceManager#enableSecureTransport(boolean):
+ New API must be flagged with @FlaggedApi: method android.companion.CompanionDeviceManager.enableSecureTransport(boolean)
+UnflaggedApi: android.content.AttributionSource#AttributionSource(int, int, String, String, android.os.IBinder, String[], android.content.AttributionSource):
+ New API must be flagged with @FlaggedApi: constructor android.content.AttributionSource(int,int,String,String,android.os.IBinder,String[],android.content.AttributionSource)
+UnflaggedApi: android.content.AttributionSource#AttributionSource(int, int, String, String, android.os.IBinder, String[], int, android.content.AttributionSource):
+ New API must be flagged with @FlaggedApi: constructor android.content.AttributionSource(int,int,String,String,android.os.IBinder,String[],int,android.content.AttributionSource)
+UnflaggedApi: android.content.pm.UserInfo#isCommunalProfile():
+ New API must be flagged with @FlaggedApi: method android.content.pm.UserInfo.isCommunalProfile()
+UnflaggedApi: android.content.pm.UserInfo#isPrivateProfile():
+ New API must be flagged with @FlaggedApi: method android.content.pm.UserInfo.isPrivateProfile()
+UnflaggedApi: android.credentials.CredentialProviderInfo#isPrimary():
+ New API must be flagged with @FlaggedApi: method android.credentials.CredentialProviderInfo.isPrimary()
+UnflaggedApi: android.media.AudioManager#enterAudioFocusFreezeForTest(java.util.List<java.lang.Integer>):
+ New API must be flagged with @FlaggedApi: method android.media.AudioManager.enterAudioFocusFreezeForTest(java.util.List<java.lang.Integer>)
+UnflaggedApi: android.media.AudioManager#exitAudioFocusFreezeForTest():
+ New API must be flagged with @FlaggedApi: method android.media.AudioManager.exitAudioFocusFreezeForTest()
+UnflaggedApi: android.media.AudioManager#getFocusDuckedUidsForTest():
+ New API must be flagged with @FlaggedApi: method android.media.AudioManager.getFocusDuckedUidsForTest()
+UnflaggedApi: android.media.AudioManager#getFocusFadeOutDurationForTest():
+ New API must be flagged with @FlaggedApi: method android.media.AudioManager.getFocusFadeOutDurationForTest()
+UnflaggedApi: android.media.AudioManager#getFocusUnmuteDelayAfterFadeOutForTest():
+ New API must be flagged with @FlaggedApi: method android.media.AudioManager.getFocusUnmuteDelayAfterFadeOutForTest()
+UnflaggedApi: android.media.RingtoneSelection:
+ New API must be flagged with @FlaggedApi: class android.media.RingtoneSelection
+UnflaggedApi: android.media.RingtoneSelection#DEFAULT_SELECTION_URI_STRING:
+ New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.DEFAULT_SELECTION_URI_STRING
+UnflaggedApi: android.media.RingtoneSelection#FROM_URI_RINGTONE_SELECTION_ONLY:
+ New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.FROM_URI_RINGTONE_SELECTION_ONLY
+UnflaggedApi: android.media.RingtoneSelection#FROM_URI_RINGTONE_SELECTION_OR_SOUND:
+ New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.FROM_URI_RINGTONE_SELECTION_OR_SOUND
+UnflaggedApi: android.media.RingtoneSelection#FROM_URI_RINGTONE_SELECTION_OR_VIBRATION:
+ New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.FROM_URI_RINGTONE_SELECTION_OR_VIBRATION
+UnflaggedApi: android.media.RingtoneSelection#SOUND_SOURCE_DEFAULT:
+ New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.SOUND_SOURCE_DEFAULT
+UnflaggedApi: android.media.RingtoneSelection#SOUND_SOURCE_OFF:
+ New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.SOUND_SOURCE_OFF
+UnflaggedApi: android.media.RingtoneSelection#SOUND_SOURCE_URI:
+ New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.SOUND_SOURCE_URI
+UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_APPLICATION_PROVIDED:
+ New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_APPLICATION_PROVIDED
+UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_AUDIO_CHANNEL:
+ New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_AUDIO_CHANNEL
+UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_DEFAULT:
+ New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_DEFAULT
+UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_HAPTIC_GENERATOR:
+ New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_HAPTIC_GENERATOR
+UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_OFF:
+ New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_OFF
+UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_URI:
+ New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_URI
+UnflaggedApi: android.media.RingtoneSelection#fromUri(android.net.Uri, int):
+ New API must be flagged with @FlaggedApi: method android.media.RingtoneSelection.fromUri(android.net.Uri,int)
+UnflaggedApi: android.media.RingtoneSelection#getSoundSource():
+ New API must be flagged with @FlaggedApi: method android.media.RingtoneSelection.getSoundSource()
+UnflaggedApi: android.media.RingtoneSelection#getSoundUri():
+ New API must be flagged with @FlaggedApi: method android.media.RingtoneSelection.getSoundUri()
+UnflaggedApi: android.media.RingtoneSelection#getVibrationSource():
+ New API must be flagged with @FlaggedApi: method android.media.RingtoneSelection.getVibrationSource()
+UnflaggedApi: android.media.RingtoneSelection#getVibrationUri():
+ New API must be flagged with @FlaggedApi: method android.media.RingtoneSelection.getVibrationUri()
+UnflaggedApi: android.media.RingtoneSelection#isRingtoneSelectionUri(android.net.Uri):
+ New API must be flagged with @FlaggedApi: method android.media.RingtoneSelection.isRingtoneSelectionUri(android.net.Uri)
+UnflaggedApi: android.media.RingtoneSelection#toUri():
+ New API must be flagged with @FlaggedApi: method android.media.RingtoneSelection.toUri()
+UnflaggedApi: android.media.RingtoneSelection.Builder:
+ New API must be flagged with @FlaggedApi: class android.media.RingtoneSelection.Builder
+UnflaggedApi: android.media.RingtoneSelection.Builder#Builder():
+ New API must be flagged with @FlaggedApi: constructor android.media.RingtoneSelection.Builder()
+UnflaggedApi: android.media.RingtoneSelection.Builder#Builder(android.media.RingtoneSelection):
+ New API must be flagged with @FlaggedApi: constructor android.media.RingtoneSelection.Builder(android.media.RingtoneSelection)
+UnflaggedApi: android.media.RingtoneSelection.Builder#build():
+ New API must be flagged with @FlaggedApi: method android.media.RingtoneSelection.Builder.build()
+UnflaggedApi: android.media.RingtoneSelection.Builder#setSoundSource(android.net.Uri):
+ New API must be flagged with @FlaggedApi: method android.media.RingtoneSelection.Builder.setSoundSource(android.net.Uri)
+UnflaggedApi: android.media.RingtoneSelection.Builder#setSoundSource(int):
+ New API must be flagged with @FlaggedApi: method android.media.RingtoneSelection.Builder.setSoundSource(int)
+UnflaggedApi: android.media.RingtoneSelection.Builder#setVibrationSource(android.net.Uri):
+ New API must be flagged with @FlaggedApi: method android.media.RingtoneSelection.Builder.setVibrationSource(android.net.Uri)
+UnflaggedApi: android.media.RingtoneSelection.Builder#setVibrationSource(int):
+ New API must be flagged with @FlaggedApi: method android.media.RingtoneSelection.Builder.setVibrationSource(int)
+UnflaggedApi: android.media.soundtrigger.SoundTriggerInstrumentation#setInPhoneCallState(boolean):
+ New API must be flagged with @FlaggedApi: method android.media.soundtrigger.SoundTriggerInstrumentation.setInPhoneCallState(boolean)
+UnflaggedApi: android.media.soundtrigger.SoundTriggerManager#createManagerForModule(android.hardware.soundtrigger.SoundTrigger.ModuleProperties):
+ New API must be flagged with @FlaggedApi: method android.media.soundtrigger.SoundTriggerManager.createManagerForModule(android.hardware.soundtrigger.SoundTrigger.ModuleProperties)
+UnflaggedApi: android.media.soundtrigger.SoundTriggerManager#createManagerForTestModule():
+ New API must be flagged with @FlaggedApi: method android.media.soundtrigger.SoundTriggerManager.createManagerForTestModule()
+UnflaggedApi: android.media.soundtrigger.SoundTriggerManager#listModuleProperties():
+ New API must be flagged with @FlaggedApi: method android.media.soundtrigger.SoundTriggerManager.listModuleProperties()
+UnflaggedApi: android.media.soundtrigger.SoundTriggerManager#loadSoundModel(android.hardware.soundtrigger.SoundTrigger.SoundModel):
+ New API must be flagged with @FlaggedApi: method android.media.soundtrigger.SoundTriggerManager.loadSoundModel(android.hardware.soundtrigger.SoundTrigger.SoundModel)
+UnflaggedApi: android.media.soundtrigger.SoundTriggerManager.Model#getSoundModel():
+ New API must be flagged with @FlaggedApi: method android.media.soundtrigger.SoundTriggerManager.Model.getSoundModel()
+UnflaggedApi: android.net.wifi.sharedconnectivity.app.SharedConnectivityManager#getBroadcastReceiver():
+ New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.SharedConnectivityManager.getBroadcastReceiver()
+UnflaggedApi: android.os.BatteryManager#BATTERY_PLUGGED_ANY:
+ New API must be flagged with @FlaggedApi: field android.os.BatteryManager.BATTERY_PLUGGED_ANY
+UnflaggedApi: android.os.BugreportParams#BUGREPORT_MODE_MAX_VALUE:
+ New API must be flagged with @FlaggedApi: field android.os.BugreportParams.BUGREPORT_MODE_MAX_VALUE
+UnflaggedApi: android.os.PowerManager#isBatterySaverSupported():
+ New API must be flagged with @FlaggedApi: method android.os.PowerManager.isBatterySaverSupported()
+UnflaggedApi: android.os.UserHandle#USER_CURRENT:
+ New API must be flagged with @FlaggedApi: field android.os.UserHandle.USER_CURRENT
+UnflaggedApi: android.os.UserManager#getAliveUsers():
+ New API must be flagged with @FlaggedApi: method android.os.UserManager.getAliveUsers()
+UnflaggedApi: android.os.UserManager#getUsers():
+ New API must be flagged with @FlaggedApi: method android.os.UserManager.getUsers()
+UnflaggedApi: android.os.vibrator.persistence.ParsedVibration:
+ New API must be flagged with @FlaggedApi: class android.os.vibrator.persistence.ParsedVibration
+UnflaggedApi: android.os.vibrator.persistence.ParsedVibration#getVibrationEffects():
+ New API must be flagged with @FlaggedApi: method android.os.vibrator.persistence.ParsedVibration.getVibrationEffects()
+UnflaggedApi: android.os.vibrator.persistence.ParsedVibration#resolve(android.os.Vibrator):
+ New API must be flagged with @FlaggedApi: method android.os.vibrator.persistence.ParsedVibration.resolve(android.os.Vibrator)
+UnflaggedApi: android.os.vibrator.persistence.VibrationXmlParser:
+ New API must be flagged with @FlaggedApi: class android.os.vibrator.persistence.VibrationXmlParser
+UnflaggedApi: android.os.vibrator.persistence.VibrationXmlParser#parseDocument(java.io.Reader):
+ New API must be flagged with @FlaggedApi: method android.os.vibrator.persistence.VibrationXmlParser.parseDocument(java.io.Reader)
+UnflaggedApi: android.os.vibrator.persistence.VibrationXmlParser#parseVibrationEffect(java.io.Reader):
+ New API must be flagged with @FlaggedApi: method android.os.vibrator.persistence.VibrationXmlParser.parseVibrationEffect(java.io.Reader)
+UnflaggedApi: android.os.vibrator.persistence.VibrationXmlSerializer:
+ New API must be flagged with @FlaggedApi: class android.os.vibrator.persistence.VibrationXmlSerializer
+UnflaggedApi: android.os.vibrator.persistence.VibrationXmlSerializer#serialize(android.os.VibrationEffect, java.io.Writer):
+ New API must be flagged with @FlaggedApi: method android.os.vibrator.persistence.VibrationXmlSerializer.serialize(android.os.VibrationEffect,java.io.Writer)
+UnflaggedApi: android.os.vibrator.persistence.VibrationXmlSerializer.SerializationFailedException:
+ New API must be flagged with @FlaggedApi: class android.os.vibrator.persistence.VibrationXmlSerializer.SerializationFailedException
+UnflaggedApi: android.service.notification.NotificationRankingUpdate:
+ New API must be flagged with @FlaggedApi: class android.service.notification.NotificationRankingUpdate
+UnflaggedApi: android.service.notification.NotificationRankingUpdate#CONTENTS_FILE_DESCRIPTOR:
+ New API must be flagged with @FlaggedApi: field android.service.notification.NotificationRankingUpdate.CONTENTS_FILE_DESCRIPTOR
+UnflaggedApi: android.service.notification.NotificationRankingUpdate#PARCELABLE_WRITE_RETURN_VALUE:
+ New API must be flagged with @FlaggedApi: field android.service.notification.NotificationRankingUpdate.PARCELABLE_WRITE_RETURN_VALUE
+UnflaggedApi: android.service.notification.NotificationRankingUpdate#isFdNotNullAndClosed():
+ New API must be flagged with @FlaggedApi: method android.service.notification.NotificationRankingUpdate.isFdNotNullAndClosed()
+UnflaggedApi: android.telephony.TelephonyManager#HAL_SERVICE_SATELLITE:
+ New API must be flagged with @FlaggedApi: field android.telephony.TelephonyManager.HAL_SERVICE_SATELLITE
+UnflaggedApi: android.telephony.ims.feature.MmTelFeature.MmTelCapabilities:
+ New API must be flagged with @FlaggedApi: class android.telephony.ims.feature.MmTelFeature.MmTelCapabilities
+UnflaggedApi: android.text.MeasuredParagraph:
+ New API must be flagged with @FlaggedApi: class android.text.MeasuredParagraph
+UnflaggedApi: android.text.MeasuredParagraph#buildForStaticLayoutTest(android.text.TextPaint, android.graphics.text.LineBreakConfig, CharSequence, int, int, android.text.TextDirectionHeuristic, int, boolean, android.text.MeasuredParagraph.StyleRunCallback):
+ New API must be flagged with @FlaggedApi: method android.text.MeasuredParagraph.buildForStaticLayoutTest(android.text.TextPaint,android.graphics.text.LineBreakConfig,CharSequence,int,int,android.text.TextDirectionHeuristic,int,boolean,android.text.MeasuredParagraph.StyleRunCallback)
+UnflaggedApi: android.text.MeasuredParagraph.StyleRunCallback:
+ New API must be flagged with @FlaggedApi: class android.text.MeasuredParagraph.StyleRunCallback
+UnflaggedApi: android.text.MeasuredParagraph.StyleRunCallback#onAppendReplacementRun(android.graphics.Paint, int, float):
+ New API must be flagged with @FlaggedApi: method android.text.MeasuredParagraph.StyleRunCallback.onAppendReplacementRun(android.graphics.Paint,int,float)
+UnflaggedApi: android.text.MeasuredParagraph.StyleRunCallback#onAppendStyleRun(android.graphics.Paint, android.graphics.text.LineBreakConfig, int, boolean):
+ New API must be flagged with @FlaggedApi: method android.text.MeasuredParagraph.StyleRunCallback.onAppendStyleRun(android.graphics.Paint,android.graphics.text.LineBreakConfig,int,boolean)
+UnflaggedApi: android.view.Choreographer#getFrameTimeNanos():
+ New API must be flagged with @FlaggedApi: method android.view.Choreographer.getFrameTimeNanos()
+UnflaggedApi: android.view.InputDevice#getAssociatedDisplayId():
+ New API must be flagged with @FlaggedApi: method android.view.InputDevice.getAssociatedDisplayId()
+UnflaggedApi: android.view.InputDevice#getKeyboardLanguageTag():
+ New API must be flagged with @FlaggedApi: method android.view.InputDevice.getKeyboardLanguageTag()
+UnflaggedApi: android.view.InputDevice#getKeyboardLayoutType():
+ New API must be flagged with @FlaggedApi: method android.view.InputDevice.getKeyboardLayoutType()
+UnflaggedApi: android.view.MotionEvent.PointerCoords#isResampled():
+ New API must be flagged with @FlaggedApi: method android.view.MotionEvent.PointerCoords.isResampled()
+UnflaggedApi: android.view.WindowManager#replaceContentOnDisplayWithMirror(int, android.view.Window):
+ New API must be flagged with @FlaggedApi: method android.view.WindowManager.replaceContentOnDisplayWithMirror(int,android.view.Window)
+UnflaggedApi: android.view.WindowManager#replaceContentOnDisplayWithSc(int, android.view.SurfaceControl):
+ New API must be flagged with @FlaggedApi: method android.view.WindowManager.replaceContentOnDisplayWithSc(int,android.view.SurfaceControl)
+UnflaggedApi: android.view.WindowManager.LayoutParams#preferredMaxDisplayRefreshRate:
+ New API must be flagged with @FlaggedApi: field android.view.WindowManager.LayoutParams.preferredMaxDisplayRefreshRate
+UnflaggedApi: android.view.WindowManager.LayoutParams#preferredMinDisplayRefreshRate:
+ New API must be flagged with @FlaggedApi: field android.view.WindowManager.LayoutParams.preferredMinDisplayRefreshRate
+UnflaggedApi: android.view.accessibility.AccessibilityWindowInfo#UNDEFINED_WINDOW_ID:
+ New API must be flagged with @FlaggedApi: field android.view.accessibility.AccessibilityWindowInfo.UNDEFINED_WINDOW_ID
+UnflaggedApi: android.view.animation.AnimationUtils#lockAnimationClock(long, long):
+ New API must be flagged with @FlaggedApi: method android.view.animation.AnimationUtils.lockAnimationClock(long,long)
+UnflaggedApi: android.view.inputmethod.InputMethodManager#getEnabledInputMethodListAsUser(android.os.UserHandle):
+ New API must be flagged with @FlaggedApi: method android.view.inputmethod.InputMethodManager.getEnabledInputMethodListAsUser(android.os.UserHandle)
+UnflaggedApi: android.view.inputmethod.InputMethodManager#getEnabledInputMethodSubtypeListAsUser(String, boolean, android.os.UserHandle):
+ New API must be flagged with @FlaggedApi: method android.view.inputmethod.InputMethodManager.getEnabledInputMethodSubtypeListAsUser(String,boolean,android.os.UserHandle)
+UnflaggedApi: android.view.inputmethod.InputMethodManager#isCurrentRootView(android.view.View):
+ New API must be flagged with @FlaggedApi: method android.view.inputmethod.InputMethodManager.isCurrentRootView(android.view.View)
+UnflaggedApi: android.view.inputmethod.InputMethodManager#isStylusHandwritingAvailableAsUser(android.os.UserHandle):
+ New API must be flagged with @FlaggedApi: method android.view.inputmethod.InputMethodManager.isStylusHandwritingAvailableAsUser(android.os.UserHandle)
+UnflaggedApi: android.view.inputmethod.InsertModeGesture:
+ New API must be flagged with @FlaggedApi: class android.view.inputmethod.InsertModeGesture
+UnflaggedApi: android.window.WindowInfosListenerForTest.WindowInfo#displayId:
+ New API must be flagged with @FlaggedApi: field android.window.WindowInfosListenerForTest.WindowInfo.displayId
+UnflaggedApi: android.window.WindowInfosListenerForTest.WindowInfo#isVisible:
+ New API must be flagged with @FlaggedApi: field android.window.WindowInfosListenerForTest.WindowInfo.isVisible
+UnflaggedApi: android.window.WindowInfosListenerForTest.WindowInfo#transform:
+ New API must be flagged with @FlaggedApi: field android.window.WindowInfosListenerForTest.WindowInfo.transform
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 0255860..04d04b9 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -3355,7 +3355,11 @@
}
Drawable dr = null;
if (itemInfo.packageName != null) {
- dr = getDrawable(itemInfo.packageName, itemInfo.icon, appInfo);
+ if (itemInfo.isArchived) {
+ dr = getArchivedAppIcon(itemInfo.packageName);
+ } else {
+ dr = getDrawable(itemInfo.packageName, itemInfo.icon, appInfo);
+ }
}
if (dr == null && itemInfo != appInfo && appInfo != null) {
dr = loadUnbadgedItemIcon(appInfo, appInfo);
@@ -3964,4 +3968,14 @@
throw e.rethrowFromSystemServer();
}
}
+
+ @Nullable
+ private Drawable getArchivedAppIcon(String packageName) {
+ try {
+ return new BitmapDrawable(null,
+ mPM.getArchivedAppIcon(packageName, new UserHandle(getUserId())));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/app/HomeVisibilityListener.java b/core/java/android/app/HomeVisibilityListener.java
index 0b5a5ed1..1f5f2e4 100644
--- a/core/java/android/app/HomeVisibilityListener.java
+++ b/core/java/android/app/HomeVisibilityListener.java
@@ -17,7 +17,6 @@
package android.app;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
import static android.view.Display.DEFAULT_DISPLAY;
import android.annotation.SuppressLint;
@@ -108,13 +107,12 @@
if (DBG) {
Log.d(TAG, "Task#" + i + ": activity=" + task.topActivity
+ ", visible=" + task.isVisible()
- + ", flg=" + Integer.toHexString(task.baseIntent.getFlags()));
+ + ", flg=" + Integer.toHexString(task.baseIntent.getFlags())
+ + ", type=" + task.getActivityType());
}
- if (!task.isVisible()
- || (task.baseIntent.getFlags() & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0) {
- continue;
+ if (task.isVisible() && (task.getActivityType() == ACTIVITY_TYPE_HOME)) {
+ return true;
}
- return task.getActivityType() == ACTIVITY_TYPE_HOME;
}
return false;
}
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index e447dc5..2d55403 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -49,7 +49,6 @@
import android.util.Log;
import android.view.IOnKeyguardExitResult;
import android.view.IWindowManager;
-import android.view.WindowManager.LayoutParams;
import android.view.WindowManagerGlobal;
import com.android.internal.annotations.VisibleForTesting;
@@ -71,9 +70,7 @@
import java.util.concurrent.Executor;
/**
- * Class that can be used to lock and unlock the keyguard. The
- * actual class to control the keyguard locking is
- * {@link android.app.KeyguardManager.KeyguardLock}.
+ * Class to manage and query the state of the lock screen (also known as Keyguard).
*/
@SystemService(Context.KEYGUARD_SERVICE)
public class KeyguardManager {
@@ -259,7 +256,9 @@
* {@link android.app.Activity#RESULT_OK} if the user successfully completes the challenge.
*
* @return the intent for launching the activity or null if no password is required.
- * @deprecated see BiometricPrompt.Builder#setDeviceCredentialAllowed(boolean)
+ *
+ * @deprecated see {@link
+ * android.hardware.biometrics.BiometricPrompt.Builder#setAllowedAuthenticators(int)}
*/
@Deprecated
@RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
@@ -477,13 +476,12 @@
/**
* Handle returned by {@link KeyguardManager#newKeyguardLock} that allows
- * you to disable / reenable the keyguard.
+ * you to temporarily disable / reenable the keyguard (lock screen).
*
- * @deprecated Use {@link LayoutParams#FLAG_DISMISS_KEYGUARD}
- * and/or {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED}
- * instead; this allows you to seamlessly hide the keyguard as your application
- * moves in and out of the foreground and does not require that any special
- * permissions be requested.
+ * @deprecated Use {@link android.R.attr#showWhenLocked} or {@link
+ * android.app.Activity#setShowWhenLocked(boolean)} instead. This allows you to seamlessly
+ * occlude and unocclude the keyguard as your application moves in and out of the foreground
+ * and does not require that any special permissions be requested.
*/
@Deprecated
public class KeyguardLock {
@@ -498,12 +496,12 @@
* Disable the keyguard from showing. If the keyguard is currently
* showing, hide it. The keyguard will be prevented from showing again
* until {@link #reenableKeyguard()} is called.
- *
+ * <p>
+ * This only works if the keyguard is not secure.
+ * <p>
* A good place to call this is from {@link android.app.Activity#onResume()}
*
- * Note: This call has no effect while any {@link android.app.admin.DevicePolicyManager}
- * is enabled that requires a password.
- *
+ * @see KeyguardManager#isKeyguardSecure()
* @see #reenableKeyguard()
*/
@RequiresPermission(Manifest.permission.DISABLE_KEYGUARD)
@@ -520,9 +518,6 @@
*
* A good place to call this is from {@link android.app.Activity#onPause()}
*
- * Note: This call has no effect while any {@link android.app.admin.DevicePolicyManager}
- * is enabled that requires a password.
- *
* @see #disableKeyguard()
*/
@RequiresPermission(Manifest.permission.DISABLE_KEYGUARD)
@@ -621,20 +616,18 @@
}
/**
- * Enables you to lock or unlock the keyguard. Get an instance of this class by
- * calling {@link android.content.Context#getSystemService(java.lang.String) Context.getSystemService()}.
- * This class is wrapped by {@link android.app.KeyguardManager KeyguardManager}.
+ * Enables you to temporarily disable / reenable the keyguard (lock screen).
+ *
* @param tag A tag that informally identifies who you are (for debugging who
* is disabling the keyguard).
*
* @return A {@link KeyguardLock} handle to use to disable and reenable the
* keyguard.
*
- * @deprecated Use {@link LayoutParams#FLAG_DISMISS_KEYGUARD}
- * and/or {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED}
- * instead; this allows you to seamlessly hide the keyguard as your application
- * moves in and out of the foreground and does not require that any special
- * permissions be requested.
+ * @deprecated Use {@link android.R.attr#showWhenLocked} or {@link
+ * android.app.Activity#setShowWhenLocked(boolean)} instead. This allows you to seamlessly
+ * occlude and unocclude the keyguard as your application moves in and out of the foreground
+ * and does not require that any special permissions be requested.
*/
@Deprecated
public KeyguardLock newKeyguardLock(String tag) {
@@ -642,9 +635,36 @@
}
/**
- * Return whether the keyguard is currently locked.
+ * Returns whether the lock screen (also known as Keyguard) is showing.
+ * <p>
+ * Specifically, this returns {@code true} in the following cases:
+ * <ul>
+ * <li>The lock screen is showing in the foreground.</li>
+ * <li>The lock screen is showing, but it is occluded by an activity that is showing on top of
+ * it. A common example is the phone app receiving a call or making an emergency call.</li>
+ * <li>The lock screen was showing but is temporarily disabled as a result of <a
+ * href="https://developer.android.com/work/dpc/dedicated-devices/lock-task-mode">lock task
+ * mode</a> or an app using the deprecated {@link KeyguardLock} API.</li>
+ * </ul>
+ * <p>
+ * "Showing" refers to a logical state of the UI, regardless of whether the screen happens to be
+ * on. When the power button is pressed on an unlocked device, the lock screen starts "showing"
+ * immediately when the screen turns off.
+ * <p>
+ * This method does not distinguish a lock screen that is requiring authentication (e.g. with
+ * PIN, pattern, password, or biometric) from a lock screen that is trivially dismissible (e.g.
+ * with swipe). It also does not distinguish a lock screen requesting a SIM card PIN from a
+ * normal device lock screen. Finally, it always returns the global lock screen state and does
+ * not consider the {@link Context}'s user specifically.
+ * <p>
+ * Note that {@code isKeyguardLocked()} is confusingly named and probably should be called
+ * {@code isKeyguardShowing()}. On many devices, the lock screen displays an <i>unlocked</i>
+ * padlock icon when it is trivially dismissible. As mentioned above, {@code isKeyguardLocked()}
+ * actually returns {@code true} in this case, not {@code false} as might be expected. {@link
+ * #isDeviceLocked()} is an alternative API that has slightly different semantics.
*
- * @return {@code true} if the keyguard is locked.
+ * @return {@code true} if the lock screen is showing
+ * @see #isDeviceLocked()
*/
public boolean isKeyguardLocked() {
try {
@@ -655,12 +675,23 @@
}
/**
- * Return whether the keyguard is secured by a PIN, pattern or password or a SIM card
- * is currently locked.
+ * Returns whether the user has a secure lock screen or there is a locked SIM card.
+ * <p>
+ * Specifically, this returns {@code true} if at least one of the following is true:
+ * <ul>
+ * <li>The {@link Context}'s user has a secure lock screen. A full user has a secure lock
+ * screen if its lock screen is set to PIN, pattern, or password, as opposed to swipe or none.
+ * A profile that uses a unified challenge is considered to have a secure lock screen if and
+ * only if its parent user has a secure lock screen.</li>
+ * <li>At least one SIM card is currently locked and requires a PIN.</li>
+ * </ul>
+ * <p>
+ * This method does not consider whether the lock screen is currently showing or not.
+ * <p>
+ * See also {@link #isDeviceSecure()} which excludes locked SIM cards.
*
- * <p>See also {@link #isDeviceSecure()} which ignores SIM locked states.
- *
- * @return {@code true} if a PIN, pattern or password is set or a SIM card is locked.
+ * @return {@code true} if the user has a secure lock screen or there is a locked SIM card
+ * @see #isDeviceSecure()
*/
public boolean isKeyguardSecure() {
try {
@@ -671,11 +702,11 @@
}
/**
- * If keyguard screen is showing or in restricted key input mode (i.e. in
- * keyguard password emergency screen). When in such mode, certain keys,
- * such as the Home key and the right soft keys, don't work.
+ * Returns whether the lock screen is showing.
+ * <p>
+ * This is exactly the same as {@link #isKeyguardLocked()}.
*
- * @return {@code true} if in keyguard restricted input mode.
+ * @return the value of {@link #isKeyguardLocked()}
* @deprecated Use {@link #isKeyguardLocked()} instead.
*/
public boolean inKeyguardRestrictedInputMode() {
@@ -683,11 +714,26 @@
}
/**
- * Returns whether the device is currently locked and requires a PIN, pattern or
- * password to unlock.
+ * Returns whether the device is currently locked for the user.
+ * <p>
+ * This returns the device locked state for the {@link Context}'s user. If this user is the
+ * current user, then the device is considered "locked" when the lock screen is showing (i.e.
+ * {@link #isKeyguardLocked()} returns {@code true}) and is not trivially dismissible (e.g. with
+ * swipe), and the user has a PIN, pattern, or password.
+ * <p>
+ * Note: the above definition implies that a user with no PIN, pattern, or password is never
+ * considered locked, even if the lock screen is showing and requesting a SIM card PIN. The
+ * device PIN and SIM PIN are separate. Also, the user is not considered locked if face
+ * authentication has just completed or a trust agent is keeping the device unlocked, since in
+ * these cases the lock screen is dismissible with swipe.
+ * <p>
+ * For a user that is not the current user but can be switched to (usually this means "another
+ * full user"), and that has a PIN, pattern, or password, the device is always considered
+ * locked. For a profile with a unified challenge, the device is considered locked if and only
+ * if the device is locked for the parent user.
*
- * @return {@code true} if unlocking the device currently requires a PIN, pattern or
- * password.
+ * @return {@code true} if the device is currently locked for the user
+ * @see #isKeyguardLocked()
*/
public boolean isDeviceLocked() {
return isDeviceLocked(mContext.getUserId());
@@ -708,12 +754,19 @@
}
/**
- * Returns whether the device is secured with a PIN, pattern or
- * password.
+ * Returns whether the user has a secure lock screen.
+ * <p>
+ * This returns {@code true} if the {@link Context}'s user has a secure lock screen. A full user
+ * has a secure lock screen if its lock screen is set to PIN, pattern, or password, as opposed
+ * to swipe or none. A profile that uses a unified challenge is considered to have a secure lock
+ * screen if and only if its parent user has a secure lock screen.
+ * <p>
+ * This method does not consider whether the lock screen is currently showing or not.
+ * <p>
+ * See also {@link #isKeyguardSecure()} which includes locked SIM cards.
*
- * <p>See also {@link #isKeyguardSecure} which treats SIM locked states as secure.
- *
- * @return {@code true} if a PIN, pattern or password was set.
+ * @return {@code true} if the user has a secure lock screen
+ * @see #isKeyguardSecure()
*/
public boolean isDeviceSecure() {
return isDeviceSecure(mContext.getUserId());
@@ -734,8 +787,7 @@
}
/**
- * If the device is currently locked (see {@link #isKeyguardLocked()}, requests the Keyguard to
- * be dismissed.
+ * Requests that the Keyguard (lock screen) be dismissed if it is currently showing.
* <p>
* If the Keyguard is not secure or the device is currently in a trusted state, calling this
* method will immediately dismiss the Keyguard without any user interaction.
@@ -746,8 +798,9 @@
* If the value set for the {@link Activity} attr {@link android.R.attr#turnScreenOn} is true,
* the screen will turn on when the keyguard is dismissed.
*
- * @param activity The activity requesting the dismissal. The activity must be either visible
- * by using {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED} or must be in a state in
+ * @param activity The activity requesting the dismissal. The activity must either be visible
+ * by using {@link android.R.attr#showWhenLocked} or {@link
+ * android.app.Activity#setShowWhenLocked(boolean)}, or must be in a state in
* which it would be visible if Keyguard would not be hiding it. If that's not
* the case, the request will fail immediately and
* {@link KeyguardDismissCallback#onDismissError} will be invoked.
@@ -762,8 +815,7 @@
}
/**
- * If the device is currently locked (see {@link #isKeyguardLocked()}, requests the Keyguard to
- * be dismissed.
+ * Requests that the Keyguard (lock screen) be dismissed if it is currently showing.
* <p>
* If the Keyguard is not secure or the device is currently in a trusted state, calling this
* method will immediately dismiss the Keyguard without any user interaction.
@@ -774,8 +826,9 @@
* If the value set for the {@link Activity} attr {@link android.R.attr#turnScreenOn} is true,
* the screen will turn on when the keyguard is dismissed.
*
- * @param activity The activity requesting the dismissal. The activity must be either visible
- * by using {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED} or must be in a state in
+ * @param activity The activity requesting the dismissal. The activity must either be visible
+ * by using {@link android.R.attr#showWhenLocked} or {@link
+ * android.app.Activity#setShowWhenLocked(boolean)}, or must be in a state in
* which it would be visible if Keyguard would not be hiding it. If that's not
* the case, the request will fail immediately and
* {@link KeyguardDismissCallback#onDismissError} will be invoked.
@@ -829,12 +882,12 @@
* @param callback Lets you know whether the operation was successful and
* it is safe to launch anything that would normally be considered safe
* once the user has gotten past the keyguard.
-
- * @deprecated Use {@link LayoutParams#FLAG_DISMISS_KEYGUARD}
- * and/or {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED}
- * instead; this allows you to seamlessly hide the keyguard as your application
- * moves in and out of the foreground and does not require that any special
- * permissions be requested.
+ *
+ * @deprecated Use {@link android.R.attr#showWhenLocked} or {@link
+ * android.app.Activity#setShowWhenLocked(boolean)} to seamlessly occlude and unocclude the
+ * keyguard as your application moves in and out of the foreground, without requiring any
+ * special permissions. Use {@link #requestDismissKeyguard(android.app.Activity,
+ * KeyguardDismissCallback)} to request dismissal of the keyguard.
*/
@Deprecated
@RequiresPermission(Manifest.permission.DISABLE_KEYGUARD)
diff --git a/core/java/android/app/TEST_MAPPING b/core/java/android/app/TEST_MAPPING
index 06e1b5b..93107ce 100644
--- a/core/java/android/app/TEST_MAPPING
+++ b/core/java/android/app/TEST_MAPPING
@@ -350,21 +350,6 @@
"file_patterns": ["(/|^)ContextImpl.java"]
},
{
- "name": "CtsWindowManagerDeviceScvh",
- "options": [
- {
- "exclude-annotation": "androidx.test.filters.FlakyTest"
- },
- {
- "exclude-annotation": "org.junit.Ignore"
- },
- {
- "include-filter": "android.content.wm.cts"
- }
- ],
- "file_patterns": ["(/|^)ContextImpl.java"]
- },
- {
"name": "CtsWindowManagerDeviceWindow",
"options": [
{
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 715edc5..213e5cb 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -11806,6 +11806,34 @@
}
/**
+ * Returns the list of {@link EnforcingAdmin}s who have set this restriction.
+ *
+ * <p>Note that for {@link #POLICY_SUSPEND_PACKAGES} it returns the PO or DO to keep the
+ * behavior the same as before the bug fix for b/192245204.
+ *
+ * <p>This API is only callable by the system UID
+ *
+ * @param userId The user for whom to retrieve the information.
+ * @param restriction The restriction enforced by admins. It could be any user restriction or
+ * policy like {@link DevicePolicyManager#POLICY_DISABLE_CAMERA} and
+ * {@link DevicePolicyManager#POLICY_DISABLE_SCREEN_CAPTURE}.
+ *
+ * @hide
+ */
+ public @NonNull Set<EnforcingAdmin> getEnforcingAdminsForRestriction(int userId,
+ @NonNull String restriction) {
+ if (mService != null) {
+ try {
+ return new HashSet<>(mService.getEnforcingAdminsForRestriction(
+ userId, restriction));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return null;
+ }
+
+ /**
* Hide or unhide packages. When a package is hidden it is unavailable for use, but the data and
* actual package file remain. This function can be called by a device owner, profile owner, or
* by a delegate given the {@link #DELEGATION_PACKAGE_ACCESS} scope via
diff --git a/core/java/android/app/admin/EnforcingAdmin.aidl b/core/java/android/app/admin/EnforcingAdmin.aidl
new file mode 100644
index 0000000..bfbfdbe
--- /dev/null
+++ b/core/java/android/app/admin/EnforcingAdmin.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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 android.app.admin;
+
+parcelable EnforcingAdmin;
\ No newline at end of file
diff --git a/core/java/android/app/admin/EnforcingAdmin.java b/core/java/android/app/admin/EnforcingAdmin.java
index 771794d..7c718f6 100644
--- a/core/java/android/app/admin/EnforcingAdmin.java
+++ b/core/java/android/app/admin/EnforcingAdmin.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.content.ComponentName;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.UserHandle;
@@ -38,6 +39,11 @@
private final UserHandle mUserHandle;
/**
+ * @hide
+ */
+ private final ComponentName mComponentName;
+
+ /**
* Creates an enforcing admin with the given params.
*/
public EnforcingAdmin(
@@ -46,6 +52,21 @@
mPackageName = Objects.requireNonNull(packageName);
mAuthority = Objects.requireNonNull(authority);
mUserHandle = Objects.requireNonNull(userHandle);
+ mComponentName = null;
+ }
+
+ /**
+ * Creates an enforcing admin with the given params.
+ *
+ * @hide
+ */
+ public EnforcingAdmin(
+ @NonNull String packageName, @NonNull Authority authority,
+ @NonNull UserHandle userHandle, @Nullable ComponentName componentName) {
+ mPackageName = Objects.requireNonNull(packageName);
+ mAuthority = Objects.requireNonNull(authority);
+ mUserHandle = Objects.requireNonNull(userHandle);
+ mComponentName = componentName;
}
private EnforcingAdmin(Parcel source) {
@@ -53,6 +74,7 @@
mUserHandle = new UserHandle(source.readInt());
mAuthority = Objects.requireNonNull(
source.readParcelable(Authority.class.getClassLoader()));
+ mComponentName = source.readParcelable(ComponentName.class.getClassLoader());
}
/**
@@ -86,7 +108,8 @@
EnforcingAdmin other = (EnforcingAdmin) o;
return Objects.equals(mPackageName, other.mPackageName)
&& Objects.equals(mAuthority, other.mAuthority)
- && Objects.equals(mUserHandle, other.mUserHandle);
+ && Objects.equals(mUserHandle, other.mUserHandle)
+ && Objects.equals(mComponentName, other.mComponentName);
}
@Override
@@ -97,7 +120,7 @@
@Override
public String toString() {
return "EnforcingAdmin { mPackageName= " + mPackageName + ", mAuthority= " + mAuthority
- + ", mUserHandle= " + mUserHandle + " }";
+ + ", mUserHandle= " + mUserHandle + ", mComponentName= " + mComponentName + " }";
}
@Override
@@ -110,6 +133,7 @@
dest.writeString(mPackageName);
dest.writeInt(mUserHandle.getIdentifier());
dest.writeParcelable(mAuthority, flags);
+ dest.writeParcelable(mComponentName, flags);
}
@NonNull
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 95ec89e..c49b820 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -54,6 +54,7 @@
import android.telephony.data.ApnSetting;
import com.android.internal.infra.AndroidFuture;
import android.app.admin.DevicePolicyState;
+import android.app.admin.EnforcingAdmin;
import java.util.List;
@@ -274,6 +275,7 @@
Intent createAdminSupportIntent(in String restriction);
Bundle getEnforcingAdminAndUserDetails(int userId,String restriction);
+ List<EnforcingAdmin> getEnforcingAdminsForRestriction(int userId,String restriction);
boolean setApplicationHidden(in ComponentName admin, in String callerPackage, in String packageName, boolean hidden, boolean parent);
boolean isApplicationHidden(in ComponentName admin, in String callerPackage, in String packageName, boolean parent);
diff --git a/core/java/android/app/admin/Provisioning_OWNERS b/core/java/android/app/admin/Provisioning_OWNERS
index fa0a1f0..8f71fc0 100644
--- a/core/java/android/app/admin/Provisioning_OWNERS
+++ b/core/java/android/app/admin/Provisioning_OWNERS
@@ -1,4 +1,4 @@
# Assign bugs to android-enterprise-triage@google.com
-mdb.ae-provisioning-reviews@google.com
+ae-provisioning-reviews@google.com
+petuska@google.com #{LAST_RESORT_SUGGESTION}
file:EnterprisePlatform_OWNERS
-petuska@google.com #{LAST_RESORT_SUGGESTION}
\ No newline at end of file
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 94c3b52..aca88d6 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -20,6 +20,7 @@
import android.content.ComponentName;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.IntentSender;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ArchivedPackageParcel;
@@ -59,7 +60,7 @@
import android.os.IRemoteCallback;
import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
-import android.content.IntentSender;
+import android.os.UserHandle;
import java.util.Map;
@@ -833,4 +834,6 @@
void unregisterPackageMonitorCallback(IRemoteCallback callback);
ArchivedPackageParcel getArchivedPackage(in String packageName, int userId);
+
+ Bitmap getArchivedAppIcon(String packageName, in UserHandle user);
}
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index 1fe1923..63c11b7 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -18,9 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
-import android.content.IntentSender;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
@@ -490,18 +488,6 @@
*/
public boolean isActiveApex;
- /**
- * Whether the package is currently in an archived state.
- *
- * <p>Packages can be archived through
- * {@link PackageInstaller#requestArchive(String, IntentSender)} and do not have any APKs stored
- * on the device, but do keep the data directory.
- * @hide
- */
- // TODO(b/278553670) Unhide and update @links before launch.
- @SystemApi
- public boolean isArchived;
-
public PackageInfo() {
}
@@ -589,7 +575,6 @@
}
dest.writeBoolean(isApex);
dest.writeBoolean(isActiveApex);
- dest.writeBoolean(isArchived);
dest.restoreAllowSquashing(prevAllowSquashing);
}
@@ -655,6 +640,5 @@
}
isApex = source.readBoolean();
isActiveApex = source.readBoolean();
- isArchived = source.readBoolean();
}
}
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 673a8a5..3a9e9bf 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -371,6 +371,13 @@
"android.content.pm.extra.UNARCHIVE_ALL_USERS";
/**
+ * A list of warnings that occurred during installation.
+ *
+ * @hide
+ */
+ public static final String EXTRA_WARNINGS = "android.content.pm.extra.WARNINGS";
+
+ /**
* Streaming installation pending.
* Caller should make sure DataLoader is able to prepare image and reinitiate the operation.
*
diff --git a/core/java/android/content/pm/PackageItemInfo.java b/core/java/android/content/pm/PackageItemInfo.java
index bb978e0..c7091ad 100644
--- a/core/java/android/content/pm/PackageItemInfo.java
+++ b/core/java/android/content/pm/PackageItemInfo.java
@@ -21,6 +21,7 @@
import static android.text.TextUtils.SAFE_STRING_FLAG_TRIM;
import static android.text.TextUtils.makeSafeForPresentation;
+import android.annotation.FlaggedApi;
import android.annotation.FloatRange;
import android.annotation.NonNull;
import android.annotation.SystemApi;
@@ -173,6 +174,18 @@
*/
public int showUserIcon;
+ /**
+ * Whether the package is currently in an archived state.
+ *
+ * <p>Packages can be archived through {@link PackageArchiver} and do not have any APKs stored
+ * on the device, but do keep the data directory.
+ * @hide
+ */
+ // TODO(b/278553670) Unhide and update @links before launch.
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_ARCHIVING)
+ public boolean isArchived;
+
public PackageItemInfo() {
showUserIcon = UserHandle.USER_NULL;
}
@@ -189,6 +202,7 @@
logo = orig.logo;
metaData = orig.metaData;
showUserIcon = orig.showUserIcon;
+ isArchived = orig.isArchived;
}
/**
@@ -442,6 +456,7 @@
dest.writeBundle(metaData);
dest.writeInt(banner);
dest.writeInt(showUserIcon);
+ dest.writeBoolean(isArchived);
}
/**
@@ -459,6 +474,7 @@
}
proto.write(PackageItemInfoProto.ICON, icon);
proto.write(PackageItemInfoProto.BANNER, banner);
+ proto.write(PackageItemInfoProto.IS_ARCHIVED, isArchived);
proto.end(token);
}
@@ -473,6 +489,7 @@
metaData = source.readBundle();
banner = source.readInt();
showUserIcon = source.readInt();
+ isArchived = source.readBoolean();
}
/**
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index cdab431..3bd6a09 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2549,6 +2549,15 @@
public static final int DELETE_DONT_KILL_APP = 0x00000008;
/**
+ * Flag parameter for {@link #deletePackage} to indicate that the deletion is an archival. This
+ * flag is only for internal usage as part of
+ * {@link PackageInstaller#requestArchive(String, IntentSender)}.
+ *
+ * @hide
+ */
+ public static final int DELETE_ARCHIVE = 0x00000010;
+
+ /**
* Flag parameter for {@link #deletePackage} to indicate that package deletion
* should be chatty.
*
diff --git a/core/java/android/content/pm/ServiceInfo.java b/core/java/android/content/pm/ServiceInfo.java
index be8b2a2..65f56f6 100644
--- a/core/java/android/content/pm/ServiceInfo.java
+++ b/core/java/android/content/pm/ServiceInfo.java
@@ -486,7 +486,7 @@
* Here is an example:
* <pre>
* <uses-permission
- * android:name="android.permissions.FOREGROUND_SERVICE_SPECIAL_USE"
+ * android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE"
* />
* <service
* android:name=".MySpecialForegroundService"
@@ -506,7 +506,7 @@
* in both platforms.
* <pre>
* <uses-permission
- * android:name="android.permissions.FOREGROUND_SERVICE_SPECIAL_USE"
+ * android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE"
* android:maxSdkVersion="last_sdk_version_without_type_foo"
* />
* <service
diff --git a/core/java/android/credentials/CredentialManager.java b/core/java/android/credentials/CredentialManager.java
index eedb25b..20771af 100644
--- a/core/java/android/credentials/CredentialManager.java
+++ b/core/java/android/credentials/CredentialManager.java
@@ -19,6 +19,7 @@
import static java.util.Objects.requireNonNull;
import android.annotation.CallbackExecutor;
+import android.annotation.Hide;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -117,6 +118,52 @@
}
/**
+ * Returns a list of candidate credentials returned from credential manager providers
+ *
+ * @param request the request specifying type(s) of credentials to get from the
+ * credential providers
+ * @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
+ *
+ * @hide
+ */
+ @Hide
+ public void getCandidateCredentials(
+ @NonNull GetCredentialRequest request,
+ @NonNull String callingPackage,
+ @Nullable CancellationSignal cancellationSignal,
+ @CallbackExecutor @NonNull Executor executor,
+ @NonNull OutcomeReceiver<GetCandidateCredentialsResponse,
+ GetCandidateCredentialsException> callback
+ ) {
+ requireNonNull(request, "request must not be null");
+ requireNonNull(callingPackage, "callingPackage must not be null");
+ requireNonNull(executor, "executor must not be null");
+ requireNonNull(callback, "callback must not be null");
+
+ if (cancellationSignal != null && cancellationSignal.isCanceled()) {
+ Log.w(TAG, "getCandidateCredentials already canceled");
+ return;
+ }
+
+ ICancellationSignal cancelRemote = null;
+ try {
+ cancelRemote =
+ mService.getCandidateCredentials(
+ request,
+ new GetCandidateCredentialsTransport(executor, callback),
+ mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+
+ if (cancellationSignal != null && cancelRemote != null) {
+ cancellationSignal.setRemote(cancelRemote);
+ }
+ }
+
+ /**
* Launches the necessary flows to retrieve an app credential from the user.
*
* <p>The execution can potentially launch UI flows to collect user consent to using a
@@ -641,6 +688,44 @@
}
}
+ private static class GetCandidateCredentialsTransport
+ extends IGetCandidateCredentialsCallback.Stub {
+
+ private final Executor mExecutor;
+ private final OutcomeReceiver<GetCandidateCredentialsResponse,
+ GetCandidateCredentialsException> mCallback;
+
+ private GetCandidateCredentialsTransport(
+ Executor executor,
+ OutcomeReceiver<GetCandidateCredentialsResponse,
+ GetCandidateCredentialsException> callback) {
+ mExecutor = executor;
+ mCallback = callback;
+ }
+
+ @Override
+ public void onResponse(GetCandidateCredentialsResponse response) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onResult(response));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void onError(String errorType, String message) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(
+ () -> mCallback.onError(new GetCandidateCredentialsException(
+ errorType, message)));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ }
+
private static class GetCredentialTransport extends IGetCredentialCallback.Stub {
// TODO: listen for cancellation to release callback.
diff --git a/core/java/android/credentials/GetCandidateCredentialsException.java b/core/java/android/credentials/GetCandidateCredentialsException.java
new file mode 100644
index 0000000..40650d0
--- /dev/null
+++ b/core/java/android/credentials/GetCandidateCredentialsException.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 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.credentials;
+
+import android.annotation.Hide;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * Represents an error encountered during the
+ * {@link CredentialManager#getCandidateCredentials} operation.
+ *
+ * @hide
+ */
+@Hide
+public class GetCandidateCredentialsException extends Exception {
+ /**
+ * The error type value for when the given operation failed due to an unknown reason.
+ */
+ @NonNull
+ public static final String TYPE_UNKNOWN =
+ "android.credentials.GetCandidateCredentialsException.TYPE_UNKNOWN";
+
+ /**
+ * The error type value for when no credential is found available for the given {@link
+ * CredentialManager#getCandidateCredentials} request.
+ */
+ @NonNull
+ public static final String TYPE_NO_CREDENTIAL =
+ "android.credentials.GetCandidateCredentialsException.TYPE_NO_CREDENTIAL";
+
+ @NonNull
+ private final String mType;
+
+ /** Returns the specific exception type. */
+ @NonNull
+ public String getType() {
+ return mType;
+ }
+
+ /**
+ * Constructs a {@link GetCandidateCredentialsException}.
+ *
+ * @throws IllegalArgumentException If type is empty.
+ */
+ public GetCandidateCredentialsException(@NonNull String type, @Nullable String message) {
+ this(type, message, null);
+ }
+
+ /**
+ * Constructs a {@link GetCandidateCredentialsException}.
+ *
+ * @throws IllegalArgumentException If type is empty.
+ */
+ public GetCandidateCredentialsException(
+ @NonNull String type, @Nullable String message, @Nullable Throwable cause) {
+ super(message, cause);
+ this.mType = Preconditions.checkStringNotEmpty(type,
+ "type must not be empty");
+ }
+
+ /**
+ * Constructs a {@link GetCandidateCredentialsException}.
+ *
+ * @throws IllegalArgumentException If type is empty.
+ */
+ public GetCandidateCredentialsException(@NonNull String type, @Nullable Throwable cause) {
+ this(type, null, cause);
+ }
+
+ /**
+ * Constructs a {@link GetCandidateCredentialsException}.
+ *
+ * @throws IllegalArgumentException If type is empty.
+ */
+ public GetCandidateCredentialsException(@NonNull String type) {
+ this(type, null, null);
+ }
+}
diff --git a/core/java/android/credentials/GetCandidateCredentialsRequest.aidl b/core/java/android/credentials/GetCandidateCredentialsRequest.aidl
new file mode 100644
index 0000000..d361089
--- /dev/null
+++ b/core/java/android/credentials/GetCandidateCredentialsRequest.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 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.credentials;
+
+parcelable GetCandidateCredentialsRequest;
\ No newline at end of file
diff --git a/core/java/android/credentials/GetCandidateCredentialsRequest.java b/core/java/android/credentials/GetCandidateCredentialsRequest.java
new file mode 100644
index 0000000..7f0dcaf
--- /dev/null
+++ b/core/java/android/credentials/GetCandidateCredentialsRequest.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright 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.credentials;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.Hide;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.AnnotationValidations;
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A request to retrieve a list of candidate credentials against the list of credential
+ * options
+ *
+ * @hide
+ */
+@Hide
+public final class GetCandidateCredentialsRequest implements Parcelable {
+
+ /**
+ * The list of credential requests.
+ */
+ @NonNull
+ private final List<CredentialOption> mCredentialOptions;
+
+ /**
+ * The top request level data.
+ */
+ @NonNull
+ private final Bundle mData;
+
+ /**
+ * The origin of the calling app. Callers of this special API (e.g. browsers)
+ * can set this origin for an app different from their own, to be able to get credentials
+ * on behalf of that app.
+ */
+ @Nullable
+ private String mOrigin;
+
+ /**
+ * Returns the list of credential options to be requested.
+ */
+ @NonNull
+ public List<CredentialOption> getCredentialOptions() {
+ return mCredentialOptions;
+ }
+
+ /**
+ * Returns the top request level data.
+ */
+ @NonNull
+ public Bundle getData() {
+ return mData;
+ }
+
+ /**
+ * Returns the origin of the calling app if set otherwise returns null.
+ */
+ @Nullable
+ public String getOrigin() {
+ return mOrigin;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeTypedList(mCredentialOptions, flags);
+ dest.writeBundle(mData);
+ dest.writeString8(mOrigin);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ return "GetCandidateCredentialsRequest {credentialOption=" + mCredentialOptions
+ + ", data=" + mData
+ + ", origin=" + mOrigin
+ + "}";
+ }
+
+ private GetCandidateCredentialsRequest(@NonNull List<CredentialOption> credentialOptions,
+ @NonNull Bundle data, String origin) {
+ Preconditions.checkCollectionNotEmpty(
+ credentialOptions,
+ /*valueName=*/ "credentialOptions");
+ Preconditions.checkCollectionElementsNotNull(
+ credentialOptions,
+ /*valueName=*/ "credentialOptions");
+ mCredentialOptions = credentialOptions;
+ mData = requireNonNull(data,
+ "data must not be null");
+ mOrigin = origin;
+ }
+
+ private GetCandidateCredentialsRequest(@NonNull Parcel in) {
+ List<CredentialOption> credentialOptions = new ArrayList<CredentialOption>();
+ in.readTypedList(credentialOptions, CredentialOption.CREATOR);
+ mCredentialOptions = credentialOptions;
+ AnnotationValidations.validate(NonNull.class, null, mCredentialOptions);
+
+ Bundle data = in.readBundle();
+ mData = data;
+ AnnotationValidations.validate(NonNull.class, null, mData);
+
+ mOrigin = in.readString8();
+ }
+
+ @NonNull
+ public static final Creator<GetCandidateCredentialsRequest> CREATOR =
+ new Creator<>() {
+ @Override
+ public GetCandidateCredentialsRequest[] newArray(int size) {
+ return new GetCandidateCredentialsRequest[size];
+ }
+
+ @Override
+ public GetCandidateCredentialsRequest createFromParcel(@NonNull Parcel in) {
+ return new GetCandidateCredentialsRequest(in);
+ }
+ };
+}
diff --git a/core/java/android/credentials/GetCandidateCredentialsResponse.aidl b/core/java/android/credentials/GetCandidateCredentialsResponse.aidl
new file mode 100644
index 0000000..ffcd3e7
--- /dev/null
+++ b/core/java/android/credentials/GetCandidateCredentialsResponse.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 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.credentials;
+
+parcelable GetCandidateCredentialsResponse;
\ No newline at end of file
diff --git a/core/java/android/credentials/GetCandidateCredentialsResponse.java b/core/java/android/credentials/GetCandidateCredentialsResponse.java
new file mode 100644
index 0000000..231e4bc
--- /dev/null
+++ b/core/java/android/credentials/GetCandidateCredentialsResponse.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 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.credentials;
+
+import android.annotation.Hide;
+import android.annotation.NonNull;
+import android.credentials.ui.GetCredentialProviderData;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.AnnotationValidations;
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A list of candidate credentials.
+ *
+ * @hide
+ */
+@Hide
+public final class GetCandidateCredentialsResponse implements Parcelable {
+ // TODO(b/299321990): Add members
+
+ @NonNull
+ private final List<GetCredentialProviderData> mCandidateProviderDataList;
+
+ /**
+ * @hide
+ */
+ @Hide
+ public GetCandidateCredentialsResponse(
+ List<GetCredentialProviderData> candidateProviderDataList
+ ) {
+ Preconditions.checkCollectionNotEmpty(
+ candidateProviderDataList,
+ /*valueName=*/ "candidateProviderDataList");
+ mCandidateProviderDataList = new ArrayList<>(candidateProviderDataList);
+ }
+
+ protected GetCandidateCredentialsResponse(Parcel in) {
+ List<GetCredentialProviderData> candidateProviderDataList = new ArrayList<>();
+ in.readTypedList(candidateProviderDataList, GetCredentialProviderData.CREATOR);
+ mCandidateProviderDataList = candidateProviderDataList;
+
+ AnnotationValidations.validate(NonNull.class, null, mCandidateProviderDataList);
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeTypedList(mCandidateProviderDataList);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Creator<GetCandidateCredentialsResponse> CREATOR =
+ new Creator<>() {
+ @Override
+ public GetCandidateCredentialsResponse createFromParcel(Parcel in) {
+ return new GetCandidateCredentialsResponse(in);
+ }
+
+ @Override
+ public GetCandidateCredentialsResponse[] newArray(int size) {
+ return new GetCandidateCredentialsResponse[size];
+ }
+ };
+}
diff --git a/core/java/android/credentials/ICredentialManager.aidl b/core/java/android/credentials/ICredentialManager.aidl
index dec729f..d081576 100644
--- a/core/java/android/credentials/ICredentialManager.aidl
+++ b/core/java/android/credentials/ICredentialManager.aidl
@@ -21,12 +21,14 @@
import android.credentials.CredentialProviderInfo;
import android.credentials.ClearCredentialStateRequest;
import android.credentials.CreateCredentialRequest;
+import android.credentials.GetCandidateCredentialsRequest;
import android.credentials.GetCredentialRequest;
import android.credentials.RegisterCredentialDescriptionRequest;
import android.credentials.UnregisterCredentialDescriptionRequest;
import android.credentials.IClearCredentialStateCallback;
import android.credentials.ICreateCredentialCallback;
import android.credentials.IGetCredentialCallback;
+import android.credentials.IGetCandidateCredentialsCallback;
import android.credentials.IPrepareGetCredentialCallback;
import android.credentials.ISetEnabledProvidersCallback;
import android.content.ComponentName;
@@ -45,6 +47,8 @@
@nullable ICancellationSignal executeCreateCredential(in CreateCredentialRequest request, in ICreateCredentialCallback callback, String callingPackage);
+ @nullable ICancellationSignal getCandidateCredentials(in GetCredentialRequest request, in IGetCandidateCredentialsCallback callback, String callingPackage);
+
@nullable ICancellationSignal clearCredentialState(in ClearCredentialStateRequest request, in IClearCredentialStateCallback callback, String callingPackage);
void setEnabledProviders(in List<String> primaryProviders, in List<String> providers, in int userId, in ISetEnabledProvidersCallback callback);
diff --git a/core/java/android/credentials/IGetCandidateCredentialsCallback.aidl b/core/java/android/credentials/IGetCandidateCredentialsCallback.aidl
new file mode 100644
index 0000000..729176a
--- /dev/null
+++ b/core/java/android/credentials/IGetCandidateCredentialsCallback.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright 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.credentials;
+
+import android.app.PendingIntent;
+import android.credentials.GetCandidateCredentialsResponse;
+
+/**
+ * Listener for a getCandidateCredentials request.
+ *
+ * @hide
+ */
+interface IGetCandidateCredentialsCallback {
+ oneway void onResponse(in GetCandidateCredentialsResponse response);
+ oneway void onError(String errorType, String message);
+}
\ No newline at end of file
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index 4700720..b1aa7de 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -32,7 +32,6 @@
import android.view.SurfaceControl.RefreshRateRange;
import android.view.SurfaceControl.Transaction;
import android.window.DisplayWindowPolicyController;
-import android.window.ScreenCapture;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -112,25 +111,6 @@
public abstract void unregisterDisplayGroupListener(DisplayGroupListener listener);
/**
- * Screenshot for internal system-only use such as rotation, etc. This method includes
- * secure layers and the result should never be exposed to non-system applications.
- * This method does not apply any rotation and provides the output in natural orientation.
- *
- * @param displayId The display id to take the screenshot of.
- * @return The buffer or null if we have failed.
- */
- public abstract ScreenCapture.ScreenshotHardwareBuffer systemScreenshot(int displayId);
-
- /**
- * General screenshot functionality that excludes secure layers and applies appropriate
- * rotation that the device is currently in.
- *
- * @param displayId The display id to take the screenshot of.
- * @return The buffer or null if we have failed.
- */
- public abstract ScreenCapture.ScreenshotHardwareBuffer userScreenshot(int displayId);
-
- /**
* Returns information about the specified logical display.
*
* @param displayId The logical display id.
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index c3fae55..88d7231 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -41,6 +41,7 @@
import android.view.InputEvent;
import android.view.InputMonitor;
import android.view.PointerIcon;
+import android.view.KeyCharacterMap;
import android.view.VerifiedInputEvent;
/** @hide */
@@ -63,6 +64,8 @@
// active keyboard layout.
int getKeyCodeForKeyLocation(int deviceId, in int locationKeyCode);
+ KeyCharacterMap getKeyCharacterMap(String layoutDescriptor);
+
// Temporarily changes the pointer speed.
void tryPointerSpeed(int speed);
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index a0cceae..ff1a6ac 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -16,6 +16,8 @@
package android.hardware.input;
+import static com.android.hardware.input.Flags.keyboardLayoutPreviewFlag;
+
import android.Manifest;
import android.annotation.FloatRange;
import android.annotation.IntDef;
@@ -31,6 +33,7 @@
import android.compat.annotation.ChangeId;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
+import android.graphics.drawable.Drawable;
import android.hardware.BatteryState;
import android.os.Build;
import android.os.Handler;
@@ -931,6 +934,31 @@
}
/**
+ * Provides a Keyboard layout preview of a particular dimension.
+ *
+ * @param keyboardLayout Layout whose preview is requested. If null, will return preview of
+ * the default Keyboard layout defined by {@code Generic.kl}.
+ * @param width Expected width of the drawable
+ * @param height Expected height of the drawable
+ *
+ * NOTE: Width and height will auto-adjust to the width and height of the ImageView that
+ * shows the drawable but this allows the caller to provide an intrinsic width and height of
+ * the drawable allowing the ImageView to properly wrap the drawable content.
+ *
+ * @hide
+ */
+ @Nullable
+ public Drawable getKeyboardLayoutPreview(@Nullable KeyboardLayout keyboardLayout, int width,
+ int height) {
+ if (!keyboardLayoutPreviewFlag()) {
+ return null;
+ }
+ PhysicalKeyLayout keyLayout = new PhysicalKeyLayout(
+ mGlobal.getKeyCharacterMap(keyboardLayout), keyboardLayout);
+ return new KeyboardLayoutPreviewDrawable(mContext, keyLayout, width, height);
+ }
+
+ /**
* Injects an input event into the event system, targeting windows owned by the provided uid.
*
* If a valid targetUid is provided, the system will only consider injecting the input event
diff --git a/core/java/android/hardware/input/InputManagerGlobal.java b/core/java/android/hardware/input/InputManagerGlobal.java
index e886f68..8c598ae 100644
--- a/core/java/android/hardware/input/InputManagerGlobal.java
+++ b/core/java/android/hardware/input/InputManagerGlobal.java
@@ -51,6 +51,7 @@
import android.view.InputDevice;
import android.view.InputEvent;
import android.view.InputMonitor;
+import android.view.KeyCharacterMap;
import android.view.PointerIcon;
import com.android.internal.annotations.GuardedBy;
@@ -1206,6 +1207,21 @@
}
/**
+ * Returns KeyCharacterMap for the provided Keyboard layout. If provided layout is null it will
+ * return KeyCharacter map for the default layout {@code Generic.kl}.
+ */
+ public KeyCharacterMap getKeyCharacterMap(@Nullable KeyboardLayout keyboardLayout) {
+ if (keyboardLayout == null) {
+ return KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
+ }
+ try {
+ return mIm.getKeyCharacterMap(keyboardLayout.getDescriptor());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* @see InputManager#injectInputEvent(InputEvent, int, int)
*/
diff --git a/core/java/android/hardware/input/KeyboardLayout.java b/core/java/android/hardware/input/KeyboardLayout.java
index 4403251..bbfed24 100644
--- a/core/java/android/hardware/input/KeyboardLayout.java
+++ b/core/java/android/hardware/input/KeyboardLayout.java
@@ -22,6 +22,7 @@
import android.os.Parcelable;
import java.util.HashMap;
+import java.util.Locale;
import java.util.Map;
import java.util.Objects;
@@ -230,6 +231,33 @@
return mProductId;
}
+ /**
+ * Returns if the Keyboard layout follows the ANSI Physical key layout.
+ */
+ public boolean isAnsiLayout() {
+ for (int i = 0; i < mLocales.size(); i++) {
+ Locale locale = mLocales.get(i);
+ if (locale != null && locale.getCountry().equalsIgnoreCase("us")
+ && mLayoutType != LayoutType.EXTENDED) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns if the Keyboard layout follows the JIS Physical key layout.
+ */
+ public boolean isJisLayout() {
+ for (int i = 0; i < mLocales.size(); i++) {
+ Locale locale = mLocales.get(i);
+ if (locale != null && locale.getCountry().equalsIgnoreCase("jp")) {
+ return true;
+ }
+ }
+ return false;
+ }
+
@Override
public int describeContents() {
return 0;
diff --git a/core/java/android/hardware/input/KeyboardLayoutPreviewDrawable.java b/core/java/android/hardware/input/KeyboardLayoutPreviewDrawable.java
new file mode 100644
index 0000000..d943c37
--- /dev/null
+++ b/core/java/android/hardware/input/KeyboardLayoutPreviewDrawable.java
@@ -0,0 +1,504 @@
+/*
+ * 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.hardware.input;
+
+import android.annotation.ColorInt;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
+import android.util.Slog;
+import android.util.TypedValue;
+import android.view.KeyEvent;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A custom drawable class that draws preview of a Physical keyboard layout.
+ */
+final class KeyboardLayoutPreviewDrawable extends Drawable {
+
+ private static final String TAG = "KeyboardLayoutPreview";
+ private static final int GRAVITY_LEFT = 0x1;
+ private static final int GRAVITY_RIGHT = 0x2;
+ private static final int GRAVITY_TOP = 0x4;
+ private static final int GRAVITY_BOTTOM = 0x8;
+ private static final int GRAVITY_CENTER =
+ GRAVITY_LEFT | GRAVITY_RIGHT | GRAVITY_TOP | GRAVITY_BOTTOM;
+ private static final int GRAVITY_CENTER_HORIZONTAL = GRAVITY_LEFT | GRAVITY_RIGHT;
+ private static final int KEY_PADDING_IN_DP = 3;
+ private static final int KEYBOARD_PADDING_IN_DP = 10;
+ private static final int KEY_RADIUS_IN_DP = 5;
+ private static final int KEYBOARD_RADIUS_IN_DP = 10;
+ private static final int GLYPH_TEXT_SIZE_IN_SP = 10;
+
+ private final List<KeyDrawable> mKeyDrawables = new ArrayList<>();
+
+ private final int mWidth;
+ private final int mHeight;
+ private final RectF mKeyboardBackground = new RectF();
+ private final ResourceProvider mResourceProvider;
+ private final PhysicalKeyLayout mKeyLayout;
+
+ public KeyboardLayoutPreviewDrawable(Context context, PhysicalKeyLayout keyLayout, int width,
+ int height) {
+ mWidth = width;
+ mHeight = height;
+ mResourceProvider = new ResourceProvider(context);
+ mKeyLayout = keyLayout;
+ }
+
+ @Override
+ public int getIntrinsicWidth() {
+ return mWidth;
+ }
+
+ @Override
+ public int getIntrinsicHeight() {
+ return mHeight;
+ }
+
+ @Override
+ protected void onBoundsChange(@NonNull Rect bounds) {
+ super.onBoundsChange(bounds);
+ mKeyDrawables.clear();
+ final PhysicalKeyLayout.LayoutKey[][] keys = mKeyLayout.getKeys();
+ if (keys == null) {
+ return;
+ }
+ final PhysicalKeyLayout.EnterKey enterKey = mKeyLayout.getEnterKey();
+ int width = bounds.width();
+ int height = bounds.height();
+ final int keyboardPadding = mResourceProvider.getKeyboardPadding();
+ final int keyPadding = mResourceProvider.getKeyPadding();
+ final float keyRadius = mResourceProvider.getKeyRadius();
+ mKeyboardBackground.set(0, 0, width, height);
+ width -= keyboardPadding * 2;
+ height -= keyboardPadding * 2;
+ if (width <= 0 || height <= 0) {
+ Slog.e(TAG, "Invalid width and height to draw layout preview, width = " + width
+ + ", height = " + height);
+ return;
+ }
+ int rowCount = keys.length;
+ float keyHeight = (float) (height - rowCount * 2 * keyPadding) / rowCount;
+ float isoEnterKeyLeft = 0;
+ float isoEnterKeyTop = 0;
+ float isoEnterWidthUnit = 0;
+ for (int i = 0; i < rowCount; i++) {
+ PhysicalKeyLayout.LayoutKey[] row = keys[i];
+ float totalRowWeight = 0;
+ int keysInRow = row.length;
+ for (PhysicalKeyLayout.LayoutKey layoutKey : row) {
+ totalRowWeight += layoutKey.keyWeight();
+ }
+ float keyWidthInPx = (width - keysInRow * 2 * keyPadding) / totalRowWeight;
+ float rowWeightOnLeft = 0;
+ float top = keyboardPadding + keyPadding * (2 * i + 1) + i * keyHeight;
+ for (int j = 0; j < keysInRow; j++) {
+ float left =
+ keyboardPadding + keyPadding * (2 * j + 1) + rowWeightOnLeft * keyWidthInPx;
+ rowWeightOnLeft += row[j].keyWeight();
+ RectF keyRect = new RectF(left, top, left + keyWidthInPx * row[j].keyWeight(),
+ top + keyHeight);
+ if (enterKey != null && row[j].keyCode() == KeyEvent.KEYCODE_ENTER) {
+ if (enterKey.row() == i && enterKey.column() == j) {
+ isoEnterKeyLeft = keyRect.left;
+ isoEnterKeyTop = keyRect.top;
+ isoEnterWidthUnit = keyWidthInPx;
+ }
+ continue;
+ }
+ if (PhysicalKeyLayout.isSpecialKey(row[j])) {
+ mKeyDrawables.add(new TypingKey(null, keyRect, keyRadius,
+ mResourceProvider.getSpecialKeyPaint(),
+ mResourceProvider.getSpecialKeyPaint(),
+ mResourceProvider.getSpecialKeyPaint()));
+ } else if (PhysicalKeyLayout.isKeyPositionUnsure(row[j])) {
+ mKeyDrawables.add(new UnsureTypingKey(row[j].glyph(), keyRect,
+ keyRadius, mResourceProvider.getTypingKeyPaint(),
+ mResourceProvider.getPrimaryGlyphPaint(),
+ mResourceProvider.getSecondaryGlyphPaint()));
+ } else {
+ mKeyDrawables.add(new TypingKey(row[j].glyph(), keyRect, keyRadius,
+ mResourceProvider.getTypingKeyPaint(),
+ mResourceProvider.getPrimaryGlyphPaint(),
+ mResourceProvider.getSecondaryGlyphPaint()));
+ }
+ }
+ }
+ if (enterKey != null) {
+ IsoEnterKey.Builder isoEnterKeyBuilder = new IsoEnterKey.Builder(keyRadius,
+ mResourceProvider.getSpecialKeyPaint());
+ isoEnterKeyBuilder.setTopWidth(enterKey.topKeyWeight() * isoEnterWidthUnit)
+ .setStartPoint(isoEnterKeyLeft, isoEnterKeyTop)
+ .setVerticalEdges(keyHeight, 2 * (keyHeight + keyPadding))
+ .setBottomWidth(enterKey.bottomKeyWeight() * isoEnterWidthUnit);
+ mKeyDrawables.add(isoEnterKeyBuilder.build());
+ }
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ final float keyboardRadius = mResourceProvider.getBackgroundRadius();
+ canvas.drawRoundRect(mKeyboardBackground, keyboardRadius, keyboardRadius,
+ mResourceProvider.getBackgroundPaint());
+ for (KeyDrawable key : mKeyDrawables) {
+ key.draw(canvas);
+ }
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+ // Do nothing
+ }
+
+ @Override
+ public void setColorFilter(@Nullable ColorFilter colorFilter) {
+ // Do nothing
+ }
+
+ @Override
+ public int getOpacity() {
+ return PixelFormat.OPAQUE;
+ }
+
+ private static class TypingKey implements KeyDrawable {
+
+ private final RectF mKeyRect;
+ private final float mKeyRadius;
+ private final Paint mKeyPaint;
+ private final Paint mBaseTextPaint;
+ private final Paint mModifierTextPaint;
+ private final List<GlyphDrawable> mGlyphDrawables = new ArrayList<>();
+
+ private TypingKey(@Nullable PhysicalKeyLayout.KeyGlyph glyphData, RectF keyRect,
+ float keyRadius, Paint keyPaint, Paint baseTextPaint, Paint modifierTextPaint) {
+ mKeyRect = keyRect;
+ mKeyRadius = keyRadius;
+ mKeyPaint = keyPaint;
+ mBaseTextPaint = baseTextPaint;
+ mModifierTextPaint = modifierTextPaint;
+ initGlyphs(glyphData);
+ }
+
+ private void initGlyphs(@Nullable PhysicalKeyLayout.KeyGlyph glyphData) {
+ createGlyphs(glyphData);
+ measureGlyphs();
+ }
+
+ private void createGlyphs(@Nullable PhysicalKeyLayout.KeyGlyph glyphData) {
+ if (glyphData == null) {
+ return;
+ }
+ if (!glyphData.hasBaseText()) {
+ return;
+ }
+ if (glyphData.hasValidShiftText() && glyphData.hasValidAltGrText()) {
+ mGlyphDrawables.add(new GlyphDrawable(glyphData.getBaseText(), new RectF(),
+ GRAVITY_BOTTOM | GRAVITY_LEFT, mBaseTextPaint));
+ mGlyphDrawables.add(new GlyphDrawable(glyphData.getShiftText(), new RectF(),
+ GRAVITY_TOP | GRAVITY_LEFT, mModifierTextPaint));
+ mGlyphDrawables.add(new GlyphDrawable(glyphData.getAltGrText(), new RectF(),
+ GRAVITY_BOTTOM | GRAVITY_RIGHT, mModifierTextPaint));
+ } else if (glyphData.hasValidShiftText()) {
+ mGlyphDrawables.add(new GlyphDrawable(glyphData.getBaseText(), new RectF(),
+ GRAVITY_BOTTOM | GRAVITY_CENTER_HORIZONTAL, mBaseTextPaint));
+ mGlyphDrawables.add(new GlyphDrawable(glyphData.getShiftText(), new RectF(),
+ GRAVITY_TOP | GRAVITY_CENTER_HORIZONTAL, mModifierTextPaint));
+ } else if (glyphData.hasValidAltGrText()) {
+ mGlyphDrawables.add(new GlyphDrawable(glyphData.getBaseText(), new RectF(),
+ GRAVITY_BOTTOM | GRAVITY_LEFT, mBaseTextPaint));
+ mGlyphDrawables.add(new GlyphDrawable(glyphData.getAltGrText(), new RectF(),
+ GRAVITY_BOTTOM | GRAVITY_RIGHT, mModifierTextPaint));
+ } else {
+ mGlyphDrawables.add(new GlyphDrawable(glyphData.getBaseText(), new RectF(),
+ GRAVITY_CENTER, mBaseTextPaint));
+ }
+ }
+
+ private void measureGlyphs() {
+ float keyWidth = mKeyRect.width();
+ float keyHeight = mKeyRect.height();
+ for (GlyphDrawable glyph : mGlyphDrawables) {
+ float centerX = keyWidth / 2;
+ float centerY = keyHeight / 2;
+ if ((glyph.gravity & GRAVITY_LEFT) != 0) {
+ centerX -= keyWidth / 4;
+ }
+ if ((glyph.gravity & GRAVITY_RIGHT) != 0) {
+ centerX += keyWidth / 4;
+ }
+ if ((glyph.gravity & GRAVITY_TOP) != 0) {
+ centerY -= keyHeight / 4;
+ }
+ if ((glyph.gravity & GRAVITY_BOTTOM) != 0) {
+ centerY += keyHeight / 4;
+ }
+ Rect textBounds = new Rect();
+ glyph.paint.getTextBounds(glyph.text, 0, glyph.text.length(), textBounds);
+ float textWidth = textBounds.width();
+ float textHeight = textBounds.height();
+ glyph.rect.set(centerX - textWidth / 2, centerY - textHeight / 2 - textBounds.top,
+ centerX + textWidth / 2, centerY + textHeight / 2 - textBounds.top);
+ }
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ canvas.drawRoundRect(mKeyRect, mKeyRadius, mKeyRadius, mKeyPaint);
+ for (GlyphDrawable glyph : mGlyphDrawables) {
+ float textWidth = glyph.rect.width();
+ float textHeight = glyph.rect.height();
+ float keyWidth = mKeyRect.width();
+ float keyHeight = mKeyRect.height();
+ if (textWidth == 0 || textHeight == 0 || keyWidth == 0 || keyHeight == 0) {
+ return;
+ }
+ canvas.drawText(glyph.text, 0, glyph.text.length(), mKeyRect.left + glyph.rect.left,
+ mKeyRect.top + glyph.rect.top, glyph.paint);
+ }
+ }
+ }
+
+ private static class UnsureTypingKey extends TypingKey {
+
+ private UnsureTypingKey(@Nullable PhysicalKeyLayout.KeyGlyph glyphData,
+ RectF keyRect, float keyRadius, Paint keyPaint, Paint baseTextPaint,
+ Paint modifierTextPaint) {
+ super(glyphData, keyRect, keyRadius, createGreyedOutPaint(keyPaint),
+ createGreyedOutPaint(baseTextPaint), createGreyedOutPaint(modifierTextPaint));
+ }
+ }
+
+ private static class IsoEnterKey implements KeyDrawable {
+
+ private final Paint mKeyPaint;
+ private final Path mPath;
+
+ private IsoEnterKey(Paint keyPaint, @NonNull Path path) {
+ mKeyPaint = keyPaint;
+ mPath = path;
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ canvas.drawPath(mPath, mKeyPaint);
+ }
+
+ private static class Builder {
+ private final float mKeyRadius;
+ private final Paint mKeyPaint;
+ private float mLeft;
+ private float mTop;
+ private float mTopWidth;
+ private float mBottomWidth;
+ private float mLeftHeight;
+ private float mRightHeight;
+
+ private Builder(float keyRadius, Paint keyPaint) {
+ mKeyRadius = keyRadius;
+ mKeyPaint = keyPaint;
+ }
+
+ private Builder setStartPoint(float left, float top) {
+ mLeft = left;
+ mTop = top;
+ return this;
+ }
+
+ private Builder setTopWidth(float width) {
+ mTopWidth = width;
+ return this;
+ }
+
+ private Builder setBottomWidth(float width) {
+ mBottomWidth = width;
+ return this;
+ }
+
+ private Builder setVerticalEdges(float leftHeight, float rightHeight) {
+ mLeftHeight = leftHeight;
+ mRightHeight = rightHeight;
+ return this;
+ }
+
+ private IsoEnterKey build() {
+ Path enterKey = new Path();
+ RectF oval = new RectF(-mKeyRadius, -mKeyRadius, mKeyRadius, mKeyRadius);
+ // Horizontal top line
+ enterKey.moveTo(mLeft + mKeyRadius, mTop);
+ enterKey.lineTo(mLeft + mTopWidth - mKeyRadius, mTop);
+ // Rounded top right corner
+ oval.offsetTo(mLeft + mTopWidth - 2 * mKeyRadius, mTop);
+ enterKey.arcTo(oval, 270, 90);
+ // Vertical right line
+ enterKey.lineTo(mLeft + mTopWidth, mTop + mRightHeight - mKeyRadius);
+ // Rounded bottom right corner
+ oval.offsetTo(mLeft + mTopWidth - 2 * mKeyRadius,
+ mTop + mRightHeight - 2 * mKeyRadius);
+ enterKey.arcTo(oval, 0, 90);
+ // Horizontal bottom line
+ enterKey.lineTo(mLeft + mTopWidth - mBottomWidth + mKeyRadius, mTop + mRightHeight);
+ // Rounded bottom left corner
+ oval.offsetTo(mLeft + mTopWidth - mBottomWidth,
+ mTop + mRightHeight - 2 * mKeyRadius);
+ enterKey.arcTo(oval, 90, 90);
+ // Vertical left line (bottom half)
+ enterKey.lineTo(mLeft + mTopWidth - mBottomWidth, mTop + mLeftHeight - mKeyRadius);
+ // Rounded corner
+ oval.offsetTo(mLeft + mTopWidth - mBottomWidth - 2 * mKeyRadius,
+ mTop + mLeftHeight);
+ enterKey.arcTo(oval, 0, -90);
+ // Horizontal line in the mid part
+ enterKey.lineTo(mLeft + mKeyRadius, mTop + mLeftHeight);
+ // Rounded corner
+ oval.offsetTo(mLeft, mTop + mLeftHeight - 2 * mKeyRadius);
+ enterKey.arcTo(oval, 90, 90);
+ // Vertical left line (top half)
+ enterKey.lineTo(mLeft, mTop + mKeyRadius);
+ // Rounded top left corner
+ oval.offsetTo(mLeft, mTop);
+ enterKey.arcTo(oval, 180, 90);
+ enterKey.close();
+ return new IsoEnterKey(mKeyPaint, enterKey);
+ }
+ }
+ }
+
+ private record GlyphDrawable(String text, RectF rect, int gravity, Paint paint) {}
+
+ private interface KeyDrawable {
+ void draw(Canvas canvas);
+ }
+
+ private static class ResourceProvider {
+ // Resources
+ private final Paint mBackgroundPaint;
+ private final Paint mTypingKeyPaint;
+ private final Paint mSpecialKeyPaint;
+ private final Paint mPrimaryGlyphPaint;
+ private final Paint mSecondaryGlyphPaint;
+ private final int mKeyPadding;
+ private final int mKeyboardPadding;
+ private final float mKeyRadius;
+ private final float mBackgroundRadius;
+
+ private ResourceProvider(Context context) {
+ mKeyPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+ KEY_PADDING_IN_DP, context.getResources().getDisplayMetrics());
+ mKeyboardPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+ KEYBOARD_PADDING_IN_DP, context.getResources().getDisplayMetrics());
+ mKeyRadius = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+ KEY_RADIUS_IN_DP, context.getResources().getDisplayMetrics());
+ mBackgroundRadius = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+ KEYBOARD_RADIUS_IN_DP, context.getResources().getDisplayMetrics());
+ int textSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
+ GLYPH_TEXT_SIZE_IN_SP, context.getResources().getDisplayMetrics());
+ boolean isDark = (context.getResources().getConfiguration().uiMode
+ & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
+ int typingKeyColor = context.getColor(
+ isDark ? android.R.color.system_outline_variant_dark
+ : android.R.color.system_surface_container_lowest_light);
+ int specialKeyColor = context.getColor(isDark ? android.R.color.system_neutral1_800
+ : android.R.color.system_secondary_container_light);
+ int primaryGlyphColor = context.getColor(isDark ? android.R.color.system_on_surface_dark
+ : android.R.color.system_on_surface_light);
+ int secondaryGlyphColor = context.getColor(isDark ? android.R.color.system_outline_dark
+ : android.R.color.system_outline_light);
+ int backgroundColor = context.getColor(
+ isDark ? android.R.color.system_surface_container_dark
+ : android.R.color.system_surface_container_light);
+ mPrimaryGlyphPaint = createTextPaint(primaryGlyphColor, textSize,
+ Typeface.create(Typeface.SANS_SERIF, Typeface.BOLD));
+ mSecondaryGlyphPaint = createTextPaint(secondaryGlyphColor, textSize,
+ Typeface.create(Typeface.SANS_SERIF, Typeface.NORMAL));
+ mTypingKeyPaint = createFillPaint(typingKeyColor);
+ mSpecialKeyPaint = createFillPaint(specialKeyColor);
+ mBackgroundPaint = createFillPaint(backgroundColor);
+ }
+
+ private Paint getBackgroundPaint() {
+ return mBackgroundPaint;
+ }
+
+ private Paint getTypingKeyPaint() {
+ return mTypingKeyPaint;
+ }
+
+ private Paint getSpecialKeyPaint() {
+ return mSpecialKeyPaint;
+ }
+
+ private Paint getPrimaryGlyphPaint() {
+ return mPrimaryGlyphPaint;
+ }
+
+ private Paint getSecondaryGlyphPaint() {
+ return mSecondaryGlyphPaint;
+ }
+
+ private int getKeyPadding() {
+ return mKeyPadding;
+ }
+
+ private int getKeyboardPadding() {
+ return mKeyboardPadding;
+ }
+
+ private float getKeyRadius() {
+ return mKeyRadius;
+ }
+
+ private float getBackgroundRadius() {
+ return mBackgroundRadius;
+ }
+ }
+
+ private static Paint createTextPaint(@ColorInt int textColor, int textSize, Typeface typeface) {
+ Paint paint = new Paint();
+ paint.setColor(textColor);
+ paint.setStyle(Paint.Style.FILL);
+ paint.setTextSize(textSize);
+ paint.setTypeface(typeface);
+ return paint;
+ }
+
+ private static Paint createFillPaint(@ColorInt int color) {
+ Paint paint = new Paint();
+ paint.setColor(color);
+ paint.setStyle(Paint.Style.FILL);
+ return paint;
+ }
+
+ private static Paint createGreyedOutPaint(Paint paint) {
+ Paint result = new Paint(paint);
+ result.setAlpha(100);
+ return result;
+ }
+}
diff --git a/core/java/android/hardware/input/PhysicalKeyLayout.java b/core/java/android/hardware/input/PhysicalKeyLayout.java
new file mode 100644
index 0000000..241c452
--- /dev/null
+++ b/core/java/android/hardware/input/PhysicalKeyLayout.java
@@ -0,0 +1,434 @@
+/*
+ * 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.hardware.input;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.text.TextUtils;
+import android.util.SparseIntArray;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
+
+import java.util.Locale;
+
+/**
+ * A complimentary class to {@link KeyboardLayoutPreviewDrawable} describing the physical key layout
+ * of a Physical keyboard and provides information regarding the scan codes produced by the physical
+ * keys.
+ */
+final class PhysicalKeyLayout {
+
+ private static final String TAG = "KeyboardLayoutPreview";
+ private static final int SCANCODE_1 = 2;
+ private static final int SCANCODE_2 = 3;
+ private static final int SCANCODE_3 = 4;
+ private static final int SCANCODE_4 = 5;
+ private static final int SCANCODE_5 = 6;
+ private static final int SCANCODE_6 = 7;
+ private static final int SCANCODE_7 = 8;
+ private static final int SCANCODE_8 = 9;
+ private static final int SCANCODE_9 = 10;
+ private static final int SCANCODE_0 = 11;
+ private static final int SCANCODE_MINUS = 12;
+ private static final int SCANCODE_EQUALS = 13;
+ private static final int SCANCODE_Q = 16;
+ private static final int SCANCODE_W = 17;
+ private static final int SCANCODE_E = 18;
+ private static final int SCANCODE_R = 19;
+ private static final int SCANCODE_T = 20;
+ private static final int SCANCODE_Y = 21;
+ private static final int SCANCODE_U = 22;
+ private static final int SCANCODE_I = 23;
+ private static final int SCANCODE_O = 24;
+ private static final int SCANCODE_P = 25;
+ private static final int SCANCODE_LEFT_BRACKET = 26;
+ private static final int SCANCODE_RIGHT_BRACKET = 27;
+ private static final int SCANCODE_A = 30;
+ private static final int SCANCODE_S = 31;
+ private static final int SCANCODE_D = 32;
+ private static final int SCANCODE_F = 33;
+ private static final int SCANCODE_G = 34;
+ private static final int SCANCODE_H = 35;
+ private static final int SCANCODE_J = 36;
+ private static final int SCANCODE_K = 37;
+ private static final int SCANCODE_L = 38;
+ private static final int SCANCODE_SEMICOLON = 39;
+ private static final int SCANCODE_APOSTROPHE = 40;
+ private static final int SCANCODE_GRAVE = 41;
+ private static final int SCANCODE_BACKSLASH1 = 43;
+ private static final int SCANCODE_Z = 44;
+ private static final int SCANCODE_X = 45;
+ private static final int SCANCODE_C = 46;
+ private static final int SCANCODE_V = 47;
+ private static final int SCANCODE_B = 48;
+ private static final int SCANCODE_N = 49;
+ private static final int SCANCODE_M = 50;
+ private static final int SCANCODE_COMMA = 51;
+ private static final int SCANCODE_PERIOD = 52;
+ private static final int SCANCODE_SLASH = 53;
+ private static final int SCANCODE_BACKSLASH2 = 86;
+ private static final int SCANCODE_YEN = 124;
+
+ private static final SparseIntArray DEFAULT_KEYCODE_FOR_SCANCODE = new SparseIntArray();
+
+ static {
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_1, KeyEvent.KEYCODE_1);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_2, KeyEvent.KEYCODE_2);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_3, KeyEvent.KEYCODE_3);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_4, KeyEvent.KEYCODE_4);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_5, KeyEvent.KEYCODE_5);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_6, KeyEvent.KEYCODE_6);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_7, KeyEvent.KEYCODE_7);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_8, KeyEvent.KEYCODE_8);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_9, KeyEvent.KEYCODE_9);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_0, KeyEvent.KEYCODE_0);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_MINUS, KeyEvent.KEYCODE_MINUS);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_EQUALS, KeyEvent.KEYCODE_EQUALS);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_Q, KeyEvent.KEYCODE_Q);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_W, KeyEvent.KEYCODE_W);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_E, KeyEvent.KEYCODE_E);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_R, KeyEvent.KEYCODE_R);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_T, KeyEvent.KEYCODE_T);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_Y, KeyEvent.KEYCODE_Y);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_U, KeyEvent.KEYCODE_U);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_I, KeyEvent.KEYCODE_I);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_O, KeyEvent.KEYCODE_O);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_P, KeyEvent.KEYCODE_P);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_LEFT_BRACKET, KeyEvent.KEYCODE_LEFT_BRACKET);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_RIGHT_BRACKET, KeyEvent.KEYCODE_RIGHT_BRACKET);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_A, KeyEvent.KEYCODE_A);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_S, KeyEvent.KEYCODE_S);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_D, KeyEvent.KEYCODE_D);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_F, KeyEvent.KEYCODE_F);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_G, KeyEvent.KEYCODE_G);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_H, KeyEvent.KEYCODE_H);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_J, KeyEvent.KEYCODE_J);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_K, KeyEvent.KEYCODE_K);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_L, KeyEvent.KEYCODE_L);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_SEMICOLON, KeyEvent.KEYCODE_SEMICOLON);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_APOSTROPHE, KeyEvent.KEYCODE_APOSTROPHE);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_GRAVE, KeyEvent.KEYCODE_GRAVE);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_BACKSLASH1, KeyEvent.KEYCODE_BACKSLASH);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_Z, KeyEvent.KEYCODE_Z);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_X, KeyEvent.KEYCODE_X);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_C, KeyEvent.KEYCODE_C);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_V, KeyEvent.KEYCODE_V);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_B, KeyEvent.KEYCODE_B);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_N, KeyEvent.KEYCODE_N);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_M, KeyEvent.KEYCODE_M);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_COMMA, KeyEvent.KEYCODE_COMMA);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_PERIOD, KeyEvent.KEYCODE_PERIOD);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_SLASH, KeyEvent.KEYCODE_SLASH);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_BACKSLASH2, KeyEvent.KEYCODE_BACKSLASH);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_YEN, KeyEvent.KEYCODE_YEN);
+ }
+
+ private LayoutKey[][] mKeys = null;
+ private EnterKey mEnterKey = null;
+
+ public PhysicalKeyLayout(@NonNull KeyCharacterMap kcm, @Nullable KeyboardLayout layout) {
+ initLayoutKeys(kcm, layout);
+ }
+
+ private void initLayoutKeys(KeyCharacterMap kcm, KeyboardLayout layout) {
+ if (layout == null) {
+ createIsoLayout(kcm);
+ return;
+ }
+ if (layout.isAnsiLayout()) {
+ createAnsiLayout(kcm);
+ } else if (layout.isJisLayout()) {
+ createJisLayout(kcm);
+ } else {
+ createIsoLayout(kcm);
+ }
+ }
+
+ public LayoutKey[][] getKeys() {
+ return mKeys;
+ }
+
+ /**
+ * @return Special enter key (if required) that can span multiple rows like ISO enter key.
+ */
+ @Nullable
+ public EnterKey getEnterKey() {
+ return mEnterKey;
+ }
+
+ private void createAnsiLayout(KeyCharacterMap kcm) {
+ mKeys = new LayoutKey[][]{
+ {
+ getKey(kcm, SCANCODE_GRAVE), getKey(kcm, SCANCODE_1),
+ getKey(kcm, SCANCODE_2), getKey(kcm, SCANCODE_3), getKey(kcm, SCANCODE_4),
+ getKey(kcm, SCANCODE_5), getKey(kcm, SCANCODE_6), getKey(kcm, SCANCODE_7),
+ getKey(kcm, SCANCODE_8), getKey(kcm, SCANCODE_9), getKey(kcm, SCANCODE_0),
+ getKey(kcm, SCANCODE_MINUS), getKey(kcm, SCANCODE_EQUALS),
+ getKey(KeyEvent.KEYCODE_DEL, 1.5F)
+ },
+ {
+ getKey(KeyEvent.KEYCODE_TAB, 1.5F), getKey(kcm, SCANCODE_Q),
+ getKey(kcm, SCANCODE_W), getKey(kcm, SCANCODE_E), getKey(kcm, SCANCODE_R),
+ getKey(kcm, SCANCODE_T), getKey(kcm, SCANCODE_Y), getKey(kcm, SCANCODE_U),
+ getKey(kcm, SCANCODE_I), getKey(kcm, SCANCODE_O), getKey(kcm, SCANCODE_P),
+ getKey(kcm, SCANCODE_LEFT_BRACKET), getKey(kcm, SCANCODE_RIGHT_BRACKET),
+ getKey(kcm, SCANCODE_BACKSLASH1)
+ },
+ {
+ getKey(KeyEvent.KEYCODE_CAPS_LOCK, 1.75F),
+ getKey(kcm, SCANCODE_A), getKey(kcm, SCANCODE_S), getKey(kcm, SCANCODE_D),
+ getKey(kcm, SCANCODE_F), getKey(kcm, SCANCODE_G), getKey(kcm, SCANCODE_H),
+ getKey(kcm, SCANCODE_J), getKey(kcm, SCANCODE_K), getKey(kcm, SCANCODE_L),
+ getKey(kcm, SCANCODE_SEMICOLON), getKey(kcm, SCANCODE_APOSTROPHE),
+ getKey(KeyEvent.KEYCODE_ENTER, 1.75F)
+ },
+ {
+ getKey(KeyEvent.KEYCODE_SHIFT_LEFT, 2.5F),
+ getKey(kcm, SCANCODE_Z), getKey(kcm, SCANCODE_X), getKey(kcm, SCANCODE_C),
+ getKey(kcm, SCANCODE_V), getKey(kcm, SCANCODE_B), getKey(kcm, SCANCODE_N),
+ getKey(kcm, SCANCODE_M), getKey(kcm, SCANCODE_COMMA),
+ getKey(kcm, SCANCODE_PERIOD), getKey(kcm, SCANCODE_SLASH),
+ getKey(KeyEvent.KEYCODE_SHIFT_RIGHT, 2.5F),
+ },
+ {
+ getKey(KeyEvent.KEYCODE_CTRL_LEFT, 1.0F),
+ getKey(KeyEvent.KEYCODE_FUNCTION, 1.0F),
+ getKey(KeyEvent.KEYCODE_META_LEFT, 1.0F),
+ getKey(KeyEvent.KEYCODE_ALT_LEFT, 1.0F),
+ getKey(KeyEvent.KEYCODE_SPACE, 6.5F),
+ getKey(KeyEvent.KEYCODE_ALT_RIGHT, 1.0F),
+ getKey(KeyEvent.KEYCODE_META_RIGHT, 1.0F),
+ getKey(KeyEvent.KEYCODE_MENU, 1.0F),
+ getKey(KeyEvent.KEYCODE_CTRL_RIGHT, 1.0F),
+ }
+ };
+ }
+
+ private void createIsoLayout(KeyCharacterMap kcm) {
+ mKeys = new LayoutKey[][]{
+ {
+ getKey(kcm, SCANCODE_GRAVE), getKey(kcm, SCANCODE_1),
+ getKey(kcm, SCANCODE_2), getKey(kcm, SCANCODE_3), getKey(kcm, SCANCODE_4),
+ getKey(kcm, SCANCODE_5), getKey(kcm, SCANCODE_6), getKey(kcm, SCANCODE_7),
+ getKey(kcm, SCANCODE_8), getKey(kcm, SCANCODE_9), getKey(kcm, SCANCODE_0),
+ getKey(kcm, SCANCODE_MINUS), getKey(kcm, SCANCODE_EQUALS),
+ getKey(KeyEvent.KEYCODE_DEL, 1.5F)
+ },
+ {
+ getKey(KeyEvent.KEYCODE_TAB, 1.15F), getKey(kcm, SCANCODE_Q),
+ getKey(kcm, SCANCODE_W), getKey(kcm, SCANCODE_E), getKey(kcm, SCANCODE_R),
+ getKey(kcm, SCANCODE_T), getKey(kcm, SCANCODE_Y), getKey(kcm, SCANCODE_U),
+ getKey(kcm, SCANCODE_I), getKey(kcm, SCANCODE_O), getKey(kcm, SCANCODE_P),
+ getKey(kcm, SCANCODE_LEFT_BRACKET), getKey(kcm, SCANCODE_RIGHT_BRACKET),
+ getKey(KeyEvent.KEYCODE_ENTER, 1.35F)
+ },
+ {
+ getKey(KeyEvent.KEYCODE_TAB, 1.5F), getKey(kcm, SCANCODE_A),
+ getKey(kcm, SCANCODE_S), getKey(kcm, SCANCODE_D), getKey(kcm, SCANCODE_F),
+ getKey(kcm, SCANCODE_G), getKey(kcm, SCANCODE_H), getKey(kcm, SCANCODE_J),
+ getKey(kcm, SCANCODE_K), getKey(kcm, SCANCODE_L),
+ getKey(kcm, SCANCODE_SEMICOLON), getKey(kcm, SCANCODE_APOSTROPHE),
+ getKey(kcm, SCANCODE_BACKSLASH1),
+ getKey(KeyEvent.KEYCODE_ENTER, 1.0F)
+ },
+ {
+ getKey(KeyEvent.KEYCODE_SHIFT_LEFT, 1.15F),
+ getKey(kcm, SCANCODE_BACKSLASH2), getKey(kcm, SCANCODE_Z),
+ getKey(kcm, SCANCODE_X), getKey(kcm, SCANCODE_C), getKey(kcm, SCANCODE_V),
+ getKey(kcm, SCANCODE_B), getKey(kcm, SCANCODE_N), getKey(kcm, SCANCODE_M),
+ getKey(kcm, SCANCODE_COMMA), getKey(kcm, SCANCODE_PERIOD),
+ getKey(kcm, SCANCODE_SLASH),
+ getKey(KeyEvent.KEYCODE_SHIFT_RIGHT, 2.35F)
+ },
+ {
+ getKey(KeyEvent.KEYCODE_CTRL_LEFT, 1.0F),
+ getKey(KeyEvent.KEYCODE_FUNCTION, 1.0F),
+ getKey(KeyEvent.KEYCODE_META_LEFT, 1.0F),
+ getKey(KeyEvent.KEYCODE_ALT_LEFT, 1.0F),
+ getKey(KeyEvent.KEYCODE_SPACE, 6.5F),
+ getKey(KeyEvent.KEYCODE_ALT_RIGHT, 1.0F),
+ getKey(KeyEvent.KEYCODE_META_RIGHT, 1.0F),
+ getKey(KeyEvent.KEYCODE_MENU, 1.0F),
+ getKey(KeyEvent.KEYCODE_CTRL_RIGHT, 1.0F),
+ }
+ };
+ mEnterKey = new EnterKey(1, 13, 1.35F, 1.0F);
+ }
+
+ private void createJisLayout(KeyCharacterMap kcm) {
+ mKeys = new LayoutKey[][]{
+ {
+ getKey(kcm, SCANCODE_GRAVE), getKey(kcm, SCANCODE_1),
+ getKey(kcm, SCANCODE_2), getKey(kcm, SCANCODE_3), getKey(kcm, SCANCODE_4),
+ getKey(kcm, SCANCODE_5), getKey(kcm, SCANCODE_6), getKey(kcm, SCANCODE_7),
+ getKey(kcm, SCANCODE_8), getKey(kcm, SCANCODE_9), getKey(kcm, SCANCODE_0),
+ getKey(kcm, SCANCODE_MINUS, 0.8F), getKey(kcm, SCANCODE_EQUALS, 0.8f),
+ getKey(kcm, SCANCODE_YEN, 0.8f), getKey(KeyEvent.KEYCODE_DEL, 1.1F)
+ },
+ {
+ getKey(KeyEvent.KEYCODE_TAB, 1.15F), getKey(kcm, SCANCODE_Q),
+ getKey(kcm, SCANCODE_W), getKey(kcm, SCANCODE_E), getKey(kcm, SCANCODE_R),
+ getKey(kcm, SCANCODE_T), getKey(kcm, SCANCODE_Y), getKey(kcm, SCANCODE_U),
+ getKey(kcm, SCANCODE_I), getKey(kcm, SCANCODE_O), getKey(kcm, SCANCODE_P),
+ getKey(kcm, SCANCODE_LEFT_BRACKET), getKey(kcm, SCANCODE_RIGHT_BRACKET),
+ getKey(KeyEvent.KEYCODE_ENTER, 1.35F)
+ },
+ {
+ getKey(KeyEvent.KEYCODE_TAB, 1.5F), getKey(kcm, SCANCODE_A),
+ getKey(kcm, SCANCODE_S), getKey(kcm, SCANCODE_D), getKey(kcm, SCANCODE_F),
+ getKey(kcm, SCANCODE_G), getKey(kcm, SCANCODE_H), getKey(kcm, SCANCODE_J),
+ getKey(kcm, SCANCODE_K), getKey(kcm, SCANCODE_L),
+ getKey(kcm, SCANCODE_SEMICOLON), getKey(kcm, SCANCODE_APOSTROPHE),
+ getKey(kcm, SCANCODE_BACKSLASH2),
+ getKey(KeyEvent.KEYCODE_ENTER, 1.0F)
+ },
+ {
+ getKey(KeyEvent.KEYCODE_SHIFT_LEFT, 1.15F),
+ getKey(kcm, SCANCODE_Z), getKey(kcm, SCANCODE_X), getKey(kcm, SCANCODE_C),
+ getKey(kcm, SCANCODE_V), getKey(kcm, SCANCODE_B), getKey(kcm, SCANCODE_N),
+ getKey(kcm, SCANCODE_M), getKey(kcm, SCANCODE_COMMA),
+ getKey(kcm, SCANCODE_PERIOD), getKey(kcm, SCANCODE_SLASH),
+ getKey(kcm, SCANCODE_BACKSLASH1),
+ getKey(KeyEvent.KEYCODE_SHIFT_RIGHT, 2.35F)
+ },
+ {
+ getKey(KeyEvent.KEYCODE_CTRL_LEFT, 1.0F),
+ getKey(KeyEvent.KEYCODE_FUNCTION, 1.0F),
+ getKey(KeyEvent.KEYCODE_META_LEFT, 1.0F),
+ getKey(KeyEvent.KEYCODE_ALT_LEFT, 1.0F),
+ getKey(KeyEvent.KEYCODE_UNKNOWN, 1.0F),
+ getKey(KeyEvent.KEYCODE_SPACE, 3.5F),
+ getKey(KeyEvent.KEYCODE_UNKNOWN, 1.0F),
+ getKey(KeyEvent.KEYCODE_UNKNOWN, 1.0F),
+ getKey(KeyEvent.KEYCODE_ALT_RIGHT, 1.0F),
+ getKey(KeyEvent.KEYCODE_META_RIGHT, 1.0F),
+ getKey(KeyEvent.KEYCODE_MENU, 1.0F),
+ getKey(KeyEvent.KEYCODE_CTRL_RIGHT, 1.0F),
+ }
+ };
+ mEnterKey = new EnterKey(1, 13, 1.35F, 1.0F);
+ }
+
+ private static LayoutKey getKey(KeyCharacterMap kcm, int scanCode, float keyWeight) {
+ int keyCode = kcm.getMappedKeyOrDefault(scanCode,
+ DEFAULT_KEYCODE_FOR_SCANCODE.get(scanCode, KeyEvent.KEYCODE_UNKNOWN));
+ return new LayoutKey(keyCode, scanCode, keyWeight, new KeyGlyph(kcm, keyCode));
+ }
+
+ private static LayoutKey getKey(KeyCharacterMap kcm, int scanCode) {
+ return getKey(kcm, scanCode, 1.0F);
+ }
+
+ private static String getKeyText(KeyCharacterMap kcm, int keyCode, int modifierState) {
+ if (isSpecialKey(keyCode)) {
+ return "";
+ }
+ int utf8Char = (kcm.get(keyCode, modifierState) & KeyCharacterMap.COMBINING_ACCENT_MASK);
+ if (Character.isValidCodePoint(utf8Char)) {
+ return String.valueOf(Character.toChars(utf8Char)).toUpperCase(Locale.getDefault());
+ } else {
+ return String.valueOf(kcm.getDisplayLabel(keyCode)).toUpperCase(Locale.getDefault());
+ }
+ }
+
+ private static LayoutKey getKey(int keyCode, float keyWeight) {
+ return new LayoutKey(keyCode, keyCode, keyWeight, null);
+ }
+
+ /**
+ * Util function that tells if a key corresponds to a special key which are keys on a Physical
+ * layout that perform some special action like modifier keys, enter key, space key, character
+ * set changing keys, etc.
+ */
+ private static boolean isSpecialKey(int keyCode) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_DEL:
+ case KeyEvent.KEYCODE_TAB:
+ case KeyEvent.KEYCODE_CAPS_LOCK:
+ case KeyEvent.KEYCODE_ENTER:
+ case KeyEvent.KEYCODE_SHIFT_LEFT:
+ case KeyEvent.KEYCODE_SHIFT_RIGHT:
+ case KeyEvent.KEYCODE_CTRL_LEFT:
+ case KeyEvent.KEYCODE_CTRL_RIGHT:
+ case KeyEvent.KEYCODE_FUNCTION:
+ case KeyEvent.KEYCODE_ALT_LEFT:
+ case KeyEvent.KEYCODE_ALT_RIGHT:
+ case KeyEvent.KEYCODE_META_LEFT:
+ case KeyEvent.KEYCODE_META_RIGHT:
+ case KeyEvent.KEYCODE_SPACE:
+ case KeyEvent.KEYCODE_MENU:
+ case KeyEvent.KEYCODE_UNKNOWN:
+ return true;
+ }
+ return false;
+ }
+
+ public static boolean isSpecialKey(LayoutKey key) {
+ return isSpecialKey(key.keyCode);
+ }
+
+ public static boolean isKeyPositionUnsure(LayoutKey key) {
+ switch (key.scanCode) {
+ case SCANCODE_GRAVE:
+ case SCANCODE_BACKSLASH1:
+ case SCANCODE_BACKSLASH2:
+ return true;
+ }
+ return false;
+ }
+
+ public record LayoutKey(int keyCode, int scanCode, float keyWeight, KeyGlyph glyph) {}
+ public record EnterKey(int row, int column, float topKeyWeight, float bottomKeyWeight) {}
+
+ public static class KeyGlyph {
+ private final String mBaseText;
+ private final String mShiftText;
+ private final String mAltGrText;
+
+ public KeyGlyph(KeyCharacterMap kcm, int keyCode) {
+ mBaseText = getKeyText(kcm, keyCode, 0);
+ mShiftText = getKeyText(kcm, keyCode,
+ KeyEvent.META_SHIFT_ON | KeyEvent.META_SHIFT_LEFT_ON);
+ mAltGrText = getKeyText(kcm, keyCode,
+ KeyEvent.META_ALT_ON | KeyEvent.META_ALT_RIGHT_ON);
+ }
+
+ public String getBaseText() {
+ return mBaseText;
+ }
+
+ public String getShiftText() {
+ return mShiftText;
+ }
+
+ public String getAltGrText() {
+ return mAltGrText;
+ }
+
+ public boolean hasBaseText() {
+ return !TextUtils.isEmpty(mBaseText);
+ }
+
+ public boolean hasValidShiftText() {
+ return !TextUtils.isEmpty(mShiftText) && !TextUtils.equals(mBaseText, mShiftText);
+ }
+
+ public boolean hasValidAltGrText() {
+ return !TextUtils.isEmpty(mAltGrText) && !TextUtils.equals(mBaseText, mAltGrText);
+ }
+ }
+}
diff --git a/core/java/android/inputmethodservice/InkWindow.java b/core/java/android/inputmethodservice/InkWindow.java
index 24d1c95..1b8d925 100644
--- a/core/java/android/inputmethodservice/InkWindow.java
+++ b/core/java/android/inputmethodservice/InkWindow.java
@@ -104,7 +104,11 @@
*/
void hide(boolean remove) {
if (getDecorView() != null) {
- getDecorView().setVisibility(remove ? View.GONE : View.INVISIBLE);
+ if (remove) {
+ mWindowManager.removeViewImmediate(getDecorView());
+ } else {
+ getDecorView().setVisibility(View.INVISIBLE);
+ }
}
}
diff --git a/core/java/android/inputmethodservice/SoftInputWindow.java b/core/java/android/inputmethodservice/SoftInputWindow.java
index 7f6ec58..cf47786 100644
--- a/core/java/android/inputmethodservice/SoftInputWindow.java
+++ b/core/java/android/inputmethodservice/SoftInputWindow.java
@@ -16,14 +16,12 @@
package android.inputmethodservice;
-import static android.inputmethodservice.SoftInputWindowProto.BOUNDS;
import static android.inputmethodservice.SoftInputWindowProto.WINDOW_STATE;
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.annotation.IntDef;
import android.app.Dialog;
-import android.graphics.Rect;
import android.os.Debug;
import android.os.IBinder;
import android.util.Log;
@@ -45,7 +43,6 @@
private static final String TAG = "SoftInputWindow";
private final KeyEvent.DispatcherState mDispatcherState;
- private final Rect mBounds = new Rect();
private final InputMethodService mService;
@Retention(SOURCE)
@@ -150,22 +147,6 @@
}
@Override
- public boolean dispatchTouchEvent(MotionEvent ev) {
- getWindow().getDecorView().getHitRect(mBounds);
-
- if (ev.isWithinBoundsNoHistory(mBounds.left, mBounds.top,
- mBounds.right - 1, mBounds.bottom - 1)) {
- return super.dispatchTouchEvent(ev);
- } else {
- MotionEvent temp = ev.clampNoHistory(mBounds.left, mBounds.top,
- mBounds.right - 1, mBounds.bottom - 1);
- boolean handled = super.dispatchTouchEvent(temp);
- temp.recycle();
- return handled;
- }
- }
-
- @Override
public void show() {
switch (mWindowState) {
case WindowState.TOKEN_PENDING:
@@ -273,7 +254,6 @@
void dumpDebug(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
- mBounds.dumpDebug(proto, BOUNDS);
proto.write(WINDOW_STATE, mWindowState);
proto.end(token);
}
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index f40efc8..218d4bb 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -186,6 +186,8 @@
/**
* Get the binder transaction observer for this process.
*
+ * TODO(b/299356196): only applies to Java code, not C++/Rust
+ *
* @hide
*/
public static void setObserver(@Nullable BinderInternal.Observer observer) {
@@ -202,6 +204,8 @@
* that require a result must be sent as {@link IBinder#FLAG_ONEWAY} calls
* which deliver results through a callback interface.
*
+ * TODO(b/299355525): only applies to Java code, not C++/Rust
+ *
* @hide
*/
public static void setWarnOnBlocking(boolean warnOnBlocking) {
@@ -218,6 +222,8 @@
* interfaces hosted by package that could be upgraded or replaced,
* otherwise you risk system instability if that remote interface wedges.
*
+ * TODO(b/299355525): only applies to Java code, not C++/Rust
+ *
* @hide
*/
public static IBinder allowBlocking(IBinder binder) {
@@ -1307,6 +1313,8 @@
int callingUid) {
// Make sure the observer won't change while processing a transaction.
final BinderInternal.Observer observer = sObserver;
+
+ // TODO(b/299356196): observer should also observe transactions in native code
final CallSession callSession =
observer != null ? observer.callStarted(this, code, UNSET_WORKSOURCE) : null;
// Theoretically, we should call transact, which will call onTransact,
@@ -1329,7 +1337,7 @@
final boolean tracingEnabled = tagEnabled && transactionTraceName != null;
try {
- // TODO - this logic should not be in Java - it should be in native
+ // TODO(b/299356201) - this logic should not be in Java - it should be in native
// code in libbinder so that it works for all binder users.
final BinderCallHeavyHitterWatcher heavyHitterWatcher = sHeavyHitterWatcher;
if (heavyHitterWatcher != null && callingUid != -1) {
@@ -1340,9 +1348,9 @@
Trace.traceBegin(Trace.TRACE_TAG_AIDL, transactionTraceName);
}
- // TODO - this logic should not be in Java - it should be in native
- // code in libbinder so that it works for all binder users. Further,
- // this should not re-use flags.
+ // TODO(b/299353919) - this logic should not be in Java - it should be
+ // in native code in libbinder so that it works for all binder users.
+ // Further, this should not re-use flags.
if ((flags & FLAG_COLLECT_NOTED_APP_OPS) != 0 && callingUid != -1) {
AppOpsManager.startNotedAppOpsCollection(callingUid);
try {
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index 62d9c69..c527cb5 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -16,6 +16,7 @@
package android.os;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppGlobals;
@@ -1953,6 +1954,23 @@
*/
public static native long getPss(int pid, long[] outUssSwapPssRss, long[] outMemtrack);
+ /**
+ * Retrieves the RSS memory used by the process as given by the status file.
+ */
+ @FlaggedApi(Flags.FLAG_REMOVE_APP_PROFILER_PSS_COLLECTION)
+ public static native long getRss();
+
+ /**
+ * Retrieves the RSS memory used by the process as given by the status file. Optionally supply a
+ * long array of up to 4 entries to retrieve the total memtrack reported size, memtrack
+ * graphics, memtrack gl, and memtrack other.
+ *
+ * @return The RSS memory usage, or 0 if retrieval failed (i.e. the PID is gone).
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_REMOVE_APP_PROFILER_PSS_COLLECTION)
+ public static native long getRss(int pid, long[] outMemtrack);
+
/** @hide */
public static final int MEMINFO_TOTAL = 0;
/** @hide */
diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig
new file mode 100644
index 0000000..77be5d4
--- /dev/null
+++ b/core/java/android/permission/flags.aconfig
@@ -0,0 +1,8 @@
+package: "android.permission.flags"
+
+flag {
+ name: "device_aware_permission_apis"
+ namespace: "permissions"
+ description: "enable device aware permission APIs"
+ bug: "274852670"
+}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 7e71134f..c19c20c 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3596,12 +3596,9 @@
}
Bundle b;
- // b/252663068: if we're in system server and the caller did not call
+ // If we're in system server and the caller did not call
// clearCallingIdentity, the read would fail due to mismatched AttributionSources.
- // TODO(b/256013480): remove this bypass after fixing the callers in system server.
- if (namespace.equals(DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER)
- && Settings.isInSystemServer()
- && Binder.getCallingUid() != Process.myUid()) {
+ if (Settings.isInSystemServer() && Binder.getCallingUid() != Process.myUid()) {
final long token = Binder.clearCallingIdentity();
try {
// Fetch all flags for the namespace at once for caching purposes
diff --git a/core/java/android/security/TEST_MAPPING b/core/java/android/security/TEST_MAPPING
index 7e43381..5a679b1 100644
--- a/core/java/android/security/TEST_MAPPING
+++ b/core/java/android/security/TEST_MAPPING
@@ -1,5 +1,5 @@
{
- "postsubmit": [
+ "presubmit": [
{
"name": "CtsSecurityTestCases",
"options": [
diff --git a/core/java/android/security/flags.aconfig b/core/java/android/security/flags.aconfig
index cfc6f48..800149c 100644
--- a/core/java/android/security/flags.aconfig
+++ b/core/java/android/security/flags.aconfig
@@ -20,3 +20,11 @@
description: "Feature flag for deprecating .fsv_sig"
bug: "277916185"
}
+
+flag {
+ name: "extend_vb_chain_to_updated_apk"
+ namespace: "hardware_backed_security"
+ description: "Use v4 signature and fs-verity to chain verification of allowlisted APKs to Verified Boot"
+ bug: "277916185"
+ is_fixed_read_only: true
+}
diff --git a/core/java/android/service/autofill/AutofillServiceInfo.java b/core/java/android/service/autofill/AutofillServiceInfo.java
index 00c30b1..83f9662 100644
--- a/core/java/android/service/autofill/AutofillServiceInfo.java
+++ b/core/java/android/service/autofill/AutofillServiceInfo.java
@@ -63,6 +63,10 @@
private static final String TAG_AUTOFILL_SERVICE = "autofill-service";
private static final String TAG_COMPATIBILITY_PACKAGE = "compatibility-package";
+ private static final ComponentName CREDMAN_SERVICE_COMPONENT_NAME =
+ new ComponentName("com.android.credentialmanager",
+ "com.android.credentialmanager.autofill.CredentialAutofillService");
+
private static ServiceInfo getServiceInfoOrThrow(ComponentName comp, int userHandle)
throws PackageManager.NameNotFoundException {
try {
@@ -307,6 +311,11 @@
for (ResolveInfo resolveInfo : resolveInfos) {
final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
try {
+ if (serviceInfo != null && isCredentialManagerAutofillService(
+ serviceInfo.getComponentName())) {
+ // Skip this service as it is for internal use only
+ continue;
+ }
services.add(new AutofillServiceInfo(context, serviceInfo));
} catch (SecurityException e) {
// Service does not declare the proper permission, ignore it.
@@ -316,6 +325,13 @@
return services;
}
+ private static boolean isCredentialManagerAutofillService(ComponentName componentName) {
+ if (componentName == null) {
+ return false;
+ }
+ return componentName.equals(CREDMAN_SERVICE_COMPONENT_NAME);
+ }
+
@Override
public String toString() {
final StringBuilder builder = new StringBuilder();
diff --git a/core/java/android/util/Patterns.java b/core/java/android/util/Patterns.java
index ece069f..c4660c4 100644
--- a/core/java/android/util/Patterns.java
+++ b/core/java/android/util/Patterns.java
@@ -111,7 +111,7 @@
/**
* Regular expression to match all IANA top-level domains.
*
- * List accurate as of 2015/11/24. List taken from:
+ * List accurate as of 2023/09/11. List taken from:
* http://data.iana.org/TLD/tlds-alpha-by-domain.txt
* This pattern is auto-generated by frameworks/ex/common/tools/make-iana-tld-pattern.py
*
@@ -119,121 +119,167 @@
*/
static final String IANA_TOP_LEVEL_DOMAINS =
"(?:"
- + "(?:aaa|aarp|abb|abbott|abogado|academy|accenture|accountant|accountants|aco|active"
- + "|actor|ads|adult|aeg|aero|afl|agency|aig|airforce|airtel|allfinanz|alsace|amica|amsterdam"
- + "|android|apartments|app|apple|aquarelle|aramco|archi|army|arpa|arte|asia|associates"
- + "|attorney|auction|audio|auto|autos|axa|azure|a[cdefgilmoqrstuwxz])"
- + "|(?:band|bank|bar|barcelona|barclaycard|barclays|bargains|bauhaus|bayern|bbc|bbva"
- + "|bcn|beats|beer|bentley|berlin|best|bet|bharti|bible|bid|bike|bing|bingo|bio|biz|black"
- + "|blackfriday|bloomberg|blue|bms|bmw|bnl|bnpparibas|boats|bom|bond|boo|boots|boutique"
- + "|bradesco|bridgestone|broadway|broker|brother|brussels|budapest|build|builders|business"
- + "|buzz|bzh|b[abdefghijmnorstvwyz])"
- + "|(?:cab|cafe|cal|camera|camp|cancerresearch|canon|capetown|capital|car|caravan|cards"
- + "|care|career|careers|cars|cartier|casa|cash|casino|cat|catering|cba|cbn|ceb|center|ceo"
- + "|cern|cfa|cfd|chanel|channel|chat|cheap|chloe|christmas|chrome|church|cipriani|cisco"
- + "|citic|city|cityeats|claims|cleaning|click|clinic|clothing|cloud|club|clubmed|coach"
- + "|codes|coffee|college|cologne|com|commbank|community|company|computer|comsec|condos"
- + "|construction|consulting|contractors|cooking|cool|coop|corsica|country|coupons|courses"
- + "|credit|creditcard|creditunion|cricket|crown|crs|cruises|csc|cuisinella|cymru|cyou|c[acdfghiklmnoruvwxyz])"
- + "|(?:dabur|dad|dance|date|dating|datsun|day|dclk|deals|degree|delivery|dell|delta"
- + "|democrat|dental|dentist|desi|design|dev|diamonds|diet|digital|direct|directory|discount"
- + "|dnp|docs|dog|doha|domains|doosan|download|drive|durban|dvag|d[ejkmoz])"
- + "|(?:earth|eat|edu|education|email|emerck|energy|engineer|engineering|enterprises"
- + "|epson|equipment|erni|esq|estate|eurovision|eus|events|everbank|exchange|expert|exposed"
- + "|express|e[cegrstu])"
- + "|(?:fage|fail|fairwinds|faith|family|fan|fans|farm|fashion|feedback|ferrero|film"
- + "|final|finance|financial|firmdale|fish|fishing|fit|fitness|flights|florist|flowers|flsmidth"
- + "|fly|foo|football|forex|forsale|forum|foundation|frl|frogans|fund|furniture|futbol|fyi"
- + "|f[ijkmor])"
- + "|(?:gal|gallery|game|garden|gbiz|gdn|gea|gent|genting|ggee|gift|gifts|gives|giving"
- + "|glass|gle|global|globo|gmail|gmo|gmx|gold|goldpoint|golf|goo|goog|google|gop|gov|grainger"
- + "|graphics|gratis|green|gripe|group|gucci|guge|guide|guitars|guru|g[abdefghilmnpqrstuwy])"
- + "|(?:hamburg|hangout|haus|healthcare|help|here|hermes|hiphop|hitachi|hiv|hockey|holdings"
- + "|holiday|homedepot|homes|honda|horse|host|hosting|hoteles|hotmail|house|how|hsbc|hyundai"
- + "|h[kmnrtu])"
- + "|(?:ibm|icbc|ice|icu|ifm|iinet|immo|immobilien|industries|infiniti|info|ing|ink|institute"
- + "|insure|int|international|investments|ipiranga|irish|ist|istanbul|itau|iwc|i[delmnoqrst])"
- + "|(?:jaguar|java|jcb|jetzt|jewelry|jlc|jll|jobs|joburg|jprs|juegos|j[emop])"
- + "|(?:kaufen|kddi|kia|kim|kinder|kitchen|kiwi|koeln|komatsu|krd|kred|kyoto|k[eghimnprwyz])"
- + "|(?:lacaixa|lancaster|land|landrover|lasalle|lat|latrobe|law|lawyer|lds|lease|leclerc"
- + "|legal|lexus|lgbt|liaison|lidl|life|lifestyle|lighting|limited|limo|linde|link|live"
- + "|lixil|loan|loans|lol|london|lotte|lotto|love|ltd|ltda|lupin|luxe|luxury|l[abcikrstuvy])"
- + "|(?:madrid|maif|maison|man|management|mango|market|marketing|markets|marriott|mba"
- + "|media|meet|melbourne|meme|memorial|men|menu|meo|miami|microsoft|mil|mini|mma|mobi|moda"
- + "|moe|moi|mom|monash|money|montblanc|mormon|mortgage|moscow|motorcycles|mov|movie|movistar"
- + "|mtn|mtpc|mtr|museum|mutuelle|m[acdeghklmnopqrstuvwxyz])"
- + "|(?:nadex|nagoya|name|navy|nec|net|netbank|network|neustar|new|news|nexus|ngo|nhk"
- + "|nico|ninja|nissan|nokia|nra|nrw|ntt|nyc|n[acefgilopruz])"
- + "|(?:obi|office|okinawa|omega|one|ong|onl|online|ooo|oracle|orange|org|organic|osaka"
- + "|otsuka|ovh|om)"
- + "|(?:page|panerai|paris|partners|parts|party|pet|pharmacy|philips|photo|photography"
- + "|photos|physio|piaget|pics|pictet|pictures|ping|pink|pizza|place|play|playstation|plumbing"
- + "|plus|pohl|poker|porn|post|praxi|press|pro|prod|productions|prof|properties|property"
- + "|protection|pub|p[aefghklmnrstwy])"
- + "|(?:qpon|quebec|qa)"
- + "|(?:racing|realtor|realty|recipes|red|redstone|rehab|reise|reisen|reit|ren|rent|rentals"
- + "|repair|report|republican|rest|restaurant|review|reviews|rich|ricoh|rio|rip|rocher|rocks"
- + "|rodeo|rsvp|ruhr|run|rwe|ryukyu|r[eosuw])"
- + "|(?:saarland|sakura|sale|samsung|sandvik|sandvikcoromant|sanofi|sap|sapo|sarl|saxo"
- + "|sbs|sca|scb|schmidt|scholarships|school|schule|schwarz|science|scor|scot|seat|security"
- + "|seek|sener|services|seven|sew|sex|sexy|shiksha|shoes|show|shriram|singles|site|ski"
- + "|sky|skype|sncf|soccer|social|software|sohu|solar|solutions|sony|soy|space|spiegel|spreadbetting"
- + "|srl|stada|starhub|statoil|stc|stcgroup|stockholm|studio|study|style|sucks|supplies"
- + "|supply|support|surf|surgery|suzuki|swatch|swiss|sydney|systems|s[abcdeghijklmnortuvxyz])"
- + "|(?:tab|taipei|tatamotors|tatar|tattoo|tax|taxi|team|tech|technology|tel|telefonica"
- + "|temasek|tennis|thd|theater|theatre|tickets|tienda|tips|tires|tirol|today|tokyo|tools"
- + "|top|toray|toshiba|tours|town|toyota|toys|trade|trading|training|travel|trust|tui|t[cdfghjklmnortvwz])"
- + "|(?:ubs|university|uno|uol|u[agksyz])"
- + "|(?:vacations|vana|vegas|ventures|versicherung|vet|viajes|video|villas|vin|virgin"
- + "|vision|vista|vistaprint|viva|vlaanderen|vodka|vote|voting|voto|voyage|v[aceginu])"
- + "|(?:wales|walter|wang|watch|webcam|website|wed|wedding|weir|whoswho|wien|wiki|williamhill"
- + "|win|windows|wine|wme|work|works|world|wtc|wtf|w[fs])"
- + "|(?:\u03b5\u03bb|\u0431\u0435\u043b|\u0434\u0435\u0442\u0438|\u043a\u043e\u043c|\u043c\u043a\u0434"
+ + "(?:aaa|aarp|abb|abbott|abbvie|abc|able|abogado|abudhabi|academy|accenture|accountant"
+ + "|accountants|aco|actor|ads|adult|aeg|aero|aetna|afl|africa|agakhan|agency|aig|airbus"
+ + "|airforce|airtel|akdn|alibaba|alipay|allfinanz|allstate|ally|alsace|alstom|amazon|americanexpress"
+ + "|americanfamily|amex|amfam|amica|amsterdam|analytics|android|anquan|anz|aol|apartments"
+ + "|app|apple|aquarelle|arab|aramco|archi|army|arpa|art|arte|asda|asia|associates|athleta"
+ + "|attorney|auction|audi|audible|audio|auspost|author|auto|autos|avianca|aws|axa|azure"
+ + "|a[cdefgilmoqrstuwxz])"
+ + "|(?:baby|baidu|banamex|bananarepublic|band|bank|bar|barcelona|barclaycard|barclays"
+ + "|barefoot|bargains|baseball|basketball|bauhaus|bayern|bbc|bbt|bbva|bcg|bcn|beats|beauty"
+ + "|beer|bentley|berlin|best|bestbuy|bet|bharti|bible|bid|bike|bing|bingo|bio|biz|black"
+ + "|blackfriday|blockbuster|blog|bloomberg|blue|bms|bmw|bnpparibas|boats|boehringer|bofa"
+ + "|bom|bond|boo|book|booking|bosch|bostik|boston|bot|boutique|box|bradesco|bridgestone"
+ + "|broadway|broker|brother|brussels|build|builders|business|buy|buzz|bzh|b[abdefghijmnorstvwyz])"
+ + "|(?:cab|cafe|cal|call|calvinklein|cam|camera|camp|canon|capetown|capital|capitalone"
+ + "|car|caravan|cards|care|career|careers|cars|casa|case|cash|casino|cat|catering|catholic"
+ + "|cba|cbn|cbre|cbs|center|ceo|cern|cfa|cfd|chanel|channel|charity|chase|chat|cheap|chintai"
+ + "|christmas|chrome|church|cipriani|circle|cisco|citadel|citi|citic|city|cityeats|claims"
+ + "|cleaning|click|clinic|clinique|clothing|cloud|club|clubmed|coach|codes|coffee|college"
+ + "|cologne|com|comcast|commbank|community|company|compare|computer|comsec|condos|construction"
+ + "|consulting|contact|contractors|cooking|cool|coop|corsica|country|coupon|coupons|courses"
+ + "|cpa|credit|creditcard|creditunion|cricket|crown|crs|cruise|cruises|cuisinella|cymru"
+ + "|cyou|c[acdfghiklmnoruvwxyz])"
+ + "|(?:dabur|dad|dance|data|date|dating|datsun|day|dclk|dds|deal|dealer|deals|degree"
+ + "|delivery|dell|deloitte|delta|democrat|dental|dentist|desi|design|dev|dhl|diamonds|diet"
+ + "|digital|direct|directory|discount|discover|dish|diy|dnp|docs|doctor|dog|domains|dot"
+ + "|download|drive|dtv|dubai|dunlop|dupont|durban|dvag|dvr|d[ejkmoz])"
+ + "|(?:earth|eat|eco|edeka|edu|education|email|emerck|energy|engineer|engineering|enterprises"
+ + "|epson|equipment|ericsson|erni|esq|estate|etisalat|eurovision|eus|events|exchange|expert"
+ + "|exposed|express|extraspace|e[cegrstu])"
+ + "|(?:fage|fail|fairwinds|faith|family|fan|fans|farm|farmers|fashion|fast|fedex|feedback"
+ + "|ferrari|ferrero|fidelity|fido|film|final|finance|financial|fire|firestone|firmdale"
+ + "|fish|fishing|fit|fitness|flickr|flights|flir|florist|flowers|fly|foo|food|football"
+ + "|ford|forex|forsale|forum|foundation|fox|free|fresenius|frl|frogans|frontdoor|frontier"
+ + "|ftr|fujitsu|fun|fund|furniture|futbol|fyi|f[ijkmor])"
+ + "|(?:gal|gallery|gallo|gallup|game|games|gap|garden|gay|gbiz|gdn|gea|gent|genting"
+ + "|george|ggee|gift|gifts|gives|giving|glass|gle|global|globo|gmail|gmbh|gmo|gmx|godaddy"
+ + "|gold|goldpoint|golf|goo|goodyear|goog|google|gop|got|gov|grainger|graphics|gratis|green"
+ + "|gripe|grocery|group|guardian|gucci|guge|guide|guitars|guru|g[abdefghilmnpqrstuwy])"
+ + "|(?:hair|hamburg|hangout|haus|hbo|hdfc|hdfcbank|health|healthcare|help|helsinki|here"
+ + "|hermes|hiphop|hisamitsu|hitachi|hiv|hkt|hockey|holdings|holiday|homedepot|homegoods"
+ + "|homes|homesense|honda|horse|hospital|host|hosting|hot|hotels|hotmail|house|how|hsbc"
+ + "|hughes|hyatt|hyundai|h[kmnrtu])"
+ + "|(?:ibm|icbc|ice|icu|ieee|ifm|ikano|imamat|imdb|immo|immobilien|inc|industries|infiniti"
+ + "|info|ing|ink|institute|insurance|insure|int|international|intuit|investments|ipiranga"
+ + "|irish|ismaili|ist|istanbul|itau|itv|i[delmnoqrst])"
+ + "|(?:jaguar|java|jcb|jeep|jetzt|jewelry|jio|jll|jmp|jnj|jobs|joburg|jot|joy|jpmorgan"
+ + "|jprs|juegos|juniper|j[emop])"
+ + "|(?:kaufen|kddi|kerryhotels|kerrylogistics|kerryproperties|kfh|kia|kids|kim|kinder"
+ + "|kindle|kitchen|kiwi|koeln|komatsu|kosher|kpmg|kpn|krd|kred|kuokgroup|kyoto|k[eghimnprwyz])"
+ + "|(?:lacaixa|lamborghini|lamer|lancaster|land|landrover|lanxess|lasalle|lat|latino"
+ + "|latrobe|law|lawyer|lds|lease|leclerc|lefrak|legal|lego|lexus|lgbt|lidl|life|lifeinsurance"
+ + "|lifestyle|lighting|like|lilly|limited|limo|lincoln|link|lipsy|live|living|llc|llp|loan"
+ + "|loans|locker|locus|lol|london|lotte|lotto|love|lpl|lplfinancial|ltd|ltda|lundbeck|luxe"
+ + "|luxury|l[abcikrstuvy])"
+ + "|(?:madrid|maif|maison|makeup|man|management|mango|map|market|marketing|markets|marriott"
+ + "|marshalls|mattel|mba|mckinsey|med|media|meet|melbourne|meme|memorial|men|menu|merckmsd"
+ + "|miami|microsoft|mil|mini|mint|mit|mitsubishi|mlb|mls|mma|mobi|mobile|moda|moe|moi|mom"
+ + "|monash|money|monster|mormon|mortgage|moscow|moto|motorcycles|mov|movie|msd|mtn|mtr"
+ + "|museum|music|m[acdeghklmnopqrstuvwxyz])"
+ + "|(?:nab|nagoya|name|natura|navy|nba|nec|net|netbank|netflix|network|neustar|new|news"
+ + "|next|nextdirect|nexus|nfl|ngo|nhk|nico|nike|nikon|ninja|nissan|nissay|nokia|norton"
+ + "|now|nowruz|nowtv|nra|nrw|ntt|nyc|n[acefgilopruz])"
+ + "|(?:obi|observer|office|okinawa|olayan|olayangroup|oldnavy|ollo|omega|one|ong|onl"
+ + "|online|ooo|open|oracle|orange|org|organic|origins|osaka|otsuka|ott|ovh|om)"
+ + "|(?:page|panasonic|paris|pars|partners|parts|party|pay|pccw|pet|pfizer|pharmacy|phd"
+ + "|philips|phone|photo|photography|photos|physio|pics|pictet|pictures|pid|pin|ping|pink"
+ + "|pioneer|pizza|place|play|playstation|plumbing|plus|pnc|pohl|poker|politie|porn|post"
+ + "|pramerica|praxi|press|prime|pro|prod|productions|prof|progressive|promo|properties"
+ + "|property|protection|pru|prudential|pub|pwc|p[aefghklmnrstwy])"
+ + "|(?:qpon|quebec|quest|qa)"
+ + "|(?:racing|radio|read|realestate|realtor|realty|recipes|red|redstone|redumbrella"
+ + "|rehab|reise|reisen|reit|reliance|ren|rent|rentals|repair|report|republican|rest|restaurant"
+ + "|review|reviews|rexroth|rich|richardli|ricoh|ril|rio|rip|rocher|rocks|rodeo|rogers|room"
+ + "|rsvp|rugby|ruhr|run|rwe|ryukyu|r[eosuw])"
+ + "|(?:saarland|safe|safety|sakura|sale|salon|samsclub|samsung|sandvik|sandvikcoromant"
+ + "|sanofi|sap|sarl|sas|save|saxo|sbi|sbs|sca|scb|schaeffler|schmidt|scholarships|school"
+ + "|schule|schwarz|science|scot|search|seat|secure|security|seek|select|sener|services"
+ + "|seven|sew|sex|sexy|sfr|shangrila|sharp|shaw|shell|shia|shiksha|shoes|shop|shopping"
+ + "|shouji|show|showtime|silk|sina|singles|site|ski|skin|sky|skype|sling|smart|smile|sncf"
+ + "|soccer|social|softbank|software|sohu|solar|solutions|song|sony|soy|spa|space|sport"
+ + "|spot|srl|stada|staples|star|statebank|statefarm|stc|stcgroup|stockholm|storage|store"
+ + "|stream|studio|study|style|sucks|supplies|supply|support|surf|surgery|suzuki|swatch"
+ + "|swiss|sydney|systems|s[abcdeghijklmnorstuvxyz])"
+ + "|(?:tab|taipei|talk|taobao|target|tatamotors|tatar|tattoo|tax|taxi|tci|tdk|team|tech"
+ + "|technology|tel|temasek|tennis|teva|thd|theater|theatre|tiaa|tickets|tienda|tips|tires"
+ + "|tirol|tjmaxx|tjx|tkmaxx|tmall|today|tokyo|tools|top|toray|toshiba|total|tours|town"
+ + "|toyota|toys|trade|trading|training|travel|travelers|travelersinsurance|trust|trv|tube"
+ + "|tui|tunes|tushu|tvs|t[cdfghjklmnortvwz])"
+ + "|(?:ubank|ubs|unicom|university|uno|uol|ups|u[agksyz])"
+ + "|(?:vacations|vana|vanguard|vegas|ventures|verisign|versicherung|vet|viajes|video"
+ + "|vig|viking|villas|vin|vip|virgin|visa|vision|viva|vivo|vlaanderen|vodka|volkswagen"
+ + "|volvo|vote|voting|voto|voyage|v[aceginu])"
+ + "|(?:wales|walmart|walter|wang|wanggou|watch|watches|weather|weatherchannel|webcam"
+ + "|weber|website|wed|wedding|weibo|weir|whoswho|wien|wiki|williamhill|win|windows|wine"
+ + "|winners|wme|wolterskluwer|woodside|work|works|world|wow|wtc|wtf|w[fs])"
+ + "|(?:\u03b5\u03bb|\u03b5\u03c5|\u0431\u0433|\u0431\u0435\u043b|\u0434\u0435\u0442\u0438"
+ + "|\u0435\u044e|\u043a\u0430\u0442\u043e\u043b\u0438\u043a|\u043a\u043e\u043c|\u043c\u043a\u0434"
+ "|\u043c\u043e\u043d|\u043c\u043e\u0441\u043a\u0432\u0430|\u043e\u043d\u043b\u0430\u0439\u043d"
+ "|\u043e\u0440\u0433|\u0440\u0443\u0441|\u0440\u0444|\u0441\u0430\u0439\u0442|\u0441\u0440\u0431"
- + "|\u0443\u043a\u0440|\u049b\u0430\u0437|\u0570\u0561\u0575|\u05e7\u05d5\u05dd|\u0627\u0631\u0627\u0645\u0643\u0648"
- + "|\u0627\u0644\u0627\u0631\u062f\u0646|\u0627\u0644\u062c\u0632\u0627\u0626\u0631|\u0627\u0644\u0633\u0639\u0648\u062f\u064a\u0629"
- + "|\u0627\u0644\u0645\u063a\u0631\u0628|\u0627\u0645\u0627\u0631\u0627\u062a|\u0627\u06cc\u0631\u0627\u0646"
- + "|\u0628\u0627\u0632\u0627\u0631|\u0628\u06be\u0627\u0631\u062a|\u062a\u0648\u0646\u0633"
- + "|\u0633\u0648\u062f\u0627\u0646|\u0633\u0648\u0631\u064a\u0629|\u0634\u0628\u0643\u0629"
- + "|\u0639\u0631\u0627\u0642|\u0639\u0645\u0627\u0646|\u0641\u0644\u0633\u0637\u064a\u0646"
- + "|\u0642\u0637\u0631|\u0643\u0648\u0645|\u0645\u0635\u0631|\u0645\u0644\u064a\u0633\u064a\u0627"
- + "|\u0645\u0648\u0642\u0639|\u0915\u0949\u092e|\u0928\u0947\u091f|\u092d\u093e\u0930\u0924"
- + "|\u0938\u0902\u0917\u0920\u0928|\u09ad\u09be\u09b0\u09a4|\u0a2d\u0a3e\u0a30\u0a24|\u0aad\u0abe\u0ab0\u0aa4"
- + "|\u0b87\u0ba8\u0bcd\u0ba4\u0bbf\u0baf\u0bbe|\u0b87\u0bb2\u0b99\u0bcd\u0b95\u0bc8|\u0b9a\u0bbf\u0b99\u0bcd\u0b95\u0baa\u0bcd\u0baa\u0bc2\u0bb0\u0bcd"
- + "|\u0c2d\u0c3e\u0c30\u0c24\u0c4d|\u0dbd\u0d82\u0d9a\u0dcf|\u0e04\u0e2d\u0e21|\u0e44\u0e17\u0e22"
- + "|\u10d2\u10d4|\u307f\u3093\u306a|\u30b0\u30fc\u30b0\u30eb|\u30b3\u30e0|\u4e16\u754c"
- + "|\u4e2d\u4fe1|\u4e2d\u56fd|\u4e2d\u570b|\u4e2d\u6587\u7f51|\u4f01\u4e1a|\u4f5b\u5c71"
- + "|\u4fe1\u606f|\u5065\u5eb7|\u516b\u5366|\u516c\u53f8|\u516c\u76ca|\u53f0\u6e7e|\u53f0\u7063"
- + "|\u5546\u57ce|\u5546\u5e97|\u5546\u6807|\u5728\u7ebf|\u5927\u62ff|\u5a31\u4e50|\u5de5\u884c"
- + "|\u5e7f\u4e1c|\u6148\u5584|\u6211\u7231\u4f60|\u624b\u673a|\u653f\u52a1|\u653f\u5e9c"
- + "|\u65b0\u52a0\u5761|\u65b0\u95fb|\u65f6\u5c1a|\u673a\u6784|\u6de1\u9a6c\u9521|\u6e38\u620f"
- + "|\u70b9\u770b|\u79fb\u52a8|\u7ec4\u7ec7\u673a\u6784|\u7f51\u5740|\u7f51\u5e97|\u7f51\u7edc"
- + "|\u8c37\u6b4c|\u96c6\u56e2|\u98de\u5229\u6d66|\u9910\u5385|\u9999\u6e2f|\ub2f7\ub137"
- + "|\ub2f7\ucef4|\uc0bc\uc131|\ud55c\uad6d|xbox"
- + "|xerox|xin|xn\\-\\-11b4c3d|xn\\-\\-1qqw23a|xn\\-\\-30rr7y|xn\\-\\-3bst00m|xn\\-\\-3ds443g"
- + "|xn\\-\\-3e0b707e|xn\\-\\-3pxu8k|xn\\-\\-42c2d9a|xn\\-\\-45brj9c|xn\\-\\-45q11c|xn\\-\\-4gbrim"
- + "|xn\\-\\-55qw42g|xn\\-\\-55qx5d|xn\\-\\-6frz82g|xn\\-\\-6qq986b3xl|xn\\-\\-80adxhks"
- + "|xn\\-\\-80ao21a|xn\\-\\-80asehdb|xn\\-\\-80aswg|xn\\-\\-90a3ac|xn\\-\\-90ais|xn\\-\\-9dbq2a"
- + "|xn\\-\\-9et52u|xn\\-\\-b4w605ferd|xn\\-\\-c1avg|xn\\-\\-c2br7g|xn\\-\\-cg4bki|xn\\-\\-clchc0ea0b2g2a9gcd"
- + "|xn\\-\\-czr694b|xn\\-\\-czrs0t|xn\\-\\-czru2d|xn\\-\\-d1acj3b|xn\\-\\-d1alf|xn\\-\\-efvy88h"
- + "|xn\\-\\-estv75g|xn\\-\\-fhbei|xn\\-\\-fiq228c5hs|xn\\-\\-fiq64b|xn\\-\\-fiqs8s|xn\\-\\-fiqz9s"
- + "|xn\\-\\-fjq720a|xn\\-\\-flw351e|xn\\-\\-fpcrj9c3d|xn\\-\\-fzc2c9e2c|xn\\-\\-gecrj9c"
- + "|xn\\-\\-h2brj9c|xn\\-\\-hxt814e|xn\\-\\-i1b6b1a6a2e|xn\\-\\-imr513n|xn\\-\\-io0a7i"
- + "|xn\\-\\-j1aef|xn\\-\\-j1amh|xn\\-\\-j6w193g|xn\\-\\-kcrx77d1x4a|xn\\-\\-kprw13d|xn\\-\\-kpry57d"
+ + "|\u0443\u043a\u0440|\u049b\u0430\u0437|\u0570\u0561\u0575|\u05d9\u05e9\u05e8\u05d0\u05dc"
+ + "|\u05e7\u05d5\u05dd|\u0627\u0628\u0648\u0638\u0628\u064a|\u0627\u062a\u0635\u0627\u0644\u0627\u062a"
+ + "|\u0627\u0631\u0627\u0645\u0643\u0648|\u0627\u0644\u0627\u0631\u062f\u0646|\u0627\u0644\u0628\u062d\u0631\u064a\u0646"
+ + "|\u0627\u0644\u062c\u0632\u0627\u0626\u0631|\u0627\u0644\u0633\u0639\u0648\u062f\u064a\u0629"
+ + "|\u0627\u0644\u0639\u0644\u064a\u0627\u0646|\u0627\u0644\u0645\u063a\u0631\u0628|\u0627\u0645\u0627\u0631\u0627\u062a"
+ + "|\u0627\u06cc\u0631\u0627\u0646|\u0628\u0627\u0631\u062a|\u0628\u0627\u0632\u0627\u0631"
+ + "|\u0628\u064a\u062a\u0643|\u0628\u06be\u0627\u0631\u062a|\u062a\u0648\u0646\u0633|\u0633\u0648\u062f\u0627\u0646"
+ + "|\u0633\u0648\u0631\u064a\u0629|\u0634\u0628\u0643\u0629|\u0639\u0631\u0627\u0642|\u0639\u0631\u0628"
+ + "|\u0639\u0645\u0627\u0646|\u0641\u0644\u0633\u0637\u064a\u0646|\u0642\u0637\u0631|\u0643\u0627\u062b\u0648\u0644\u064a\u0643"
+ + "|\u0643\u0648\u0645|\u0645\u0635\u0631|\u0645\u0644\u064a\u0633\u064a\u0627|\u0645\u0648\u0631\u064a\u062a\u0627\u0646\u064a\u0627"
+ + "|\u0645\u0648\u0642\u0639|\u0647\u0645\u0631\u0627\u0647|\u067e\u0627\u06a9\u0633\u062a\u0627\u0646"
+ + "|\u0680\u0627\u0631\u062a|\u0915\u0949\u092e|\u0928\u0947\u091f|\u092d\u093e\u0930\u0924"
+ + "|\u092d\u093e\u0930\u0924\u092e\u094d|\u092d\u093e\u0930\u094b\u0924|\u0938\u0902\u0917\u0920\u0928"
+ + "|\u09ac\u09be\u0982\u09b2\u09be|\u09ad\u09be\u09b0\u09a4|\u09ad\u09be\u09f0\u09a4|\u0a2d\u0a3e\u0a30\u0a24"
+ + "|\u0aad\u0abe\u0ab0\u0aa4|\u0b2d\u0b3e\u0b30\u0b24|\u0b87\u0ba8\u0bcd\u0ba4\u0bbf\u0baf\u0bbe"
+ + "|\u0b87\u0bb2\u0b99\u0bcd\u0b95\u0bc8|\u0b9a\u0bbf\u0b99\u0bcd\u0b95\u0baa\u0bcd\u0baa\u0bc2\u0bb0\u0bcd"
+ + "|\u0c2d\u0c3e\u0c30\u0c24\u0c4d|\u0cad\u0cbe\u0cb0\u0ca4|\u0d2d\u0d3e\u0d30\u0d24\u0d02"
+ + "|\u0dbd\u0d82\u0d9a\u0dcf|\u0e04\u0e2d\u0e21|\u0e44\u0e17\u0e22|\u0ea5\u0eb2\u0ea7|\u10d2\u10d4"
+ + "|\u307f\u3093\u306a|\u30a2\u30de\u30be\u30f3|\u30af\u30e9\u30a6\u30c9|\u30b0\u30fc\u30b0\u30eb"
+ + "|\u30b3\u30e0|\u30b9\u30c8\u30a2|\u30bb\u30fc\u30eb|\u30d5\u30a1\u30c3\u30b7\u30e7\u30f3"
+ + "|\u30dd\u30a4\u30f3\u30c8|\u4e16\u754c|\u4e2d\u4fe1|\u4e2d\u56fd|\u4e2d\u570b|\u4e2d\u6587\u7f51"
+ + "|\u4e9a\u9a6c\u900a|\u4f01\u4e1a|\u4f5b\u5c71|\u4fe1\u606f|\u5065\u5eb7|\u516b\u5366"
+ + "|\u516c\u53f8|\u516c\u76ca|\u53f0\u6e7e|\u53f0\u7063|\u5546\u57ce|\u5546\u5e97|\u5546\u6807"
+ + "|\u5609\u91cc|\u5609\u91cc\u5927\u9152\u5e97|\u5728\u7ebf|\u5927\u62ff|\u5929\u4e3b\u6559"
+ + "|\u5a31\u4e50|\u5bb6\u96fb|\u5e7f\u4e1c|\u5fae\u535a|\u6148\u5584|\u6211\u7231\u4f60"
+ + "|\u624b\u673a|\u62db\u8058|\u653f\u52a1|\u653f\u5e9c|\u65b0\u52a0\u5761|\u65b0\u95fb"
+ + "|\u65f6\u5c1a|\u66f8\u7c4d|\u673a\u6784|\u6de1\u9a6c\u9521|\u6e38\u620f|\u6fb3\u9580"
+ + "|\u70b9\u770b|\u79fb\u52a8|\u7ec4\u7ec7\u673a\u6784|\u7f51\u5740|\u7f51\u5e97|\u7f51\u7ad9"
+ + "|\u7f51\u7edc|\u8054\u901a|\u8c37\u6b4c|\u8d2d\u7269|\u901a\u8ca9|\u96c6\u56e2|\u96fb\u8a0a\u76c8\u79d1"
+ + "|\u98de\u5229\u6d66|\u98df\u54c1|\u9910\u5385|\u9999\u683c\u91cc\u62c9|\u9999\u6e2f"
+ + "|\ub2f7\ub137|\ub2f7\ucef4|\uc0bc\uc131|\ud55c\uad6d"
+ + "|xbox|xerox|xfinity|xihuan|xin|xn\\-\\-11b4c3d|xn\\-\\-1ck2e1b|xn\\-\\-1qqw23a|xn\\-\\-2scrj9c"
+ + "|xn\\-\\-30rr7y|xn\\-\\-3bst00m|xn\\-\\-3ds443g|xn\\-\\-3e0b707e|xn\\-\\-3hcrj9c|xn\\-\\-3pxu8k"
+ + "|xn\\-\\-42c2d9a|xn\\-\\-45br5cyl|xn\\-\\-45brj9c|xn\\-\\-45q11c|xn\\-\\-4dbrk0ce|xn\\-\\-4gbrim"
+ + "|xn\\-\\-54b7fta0cc|xn\\-\\-55qw42g|xn\\-\\-55qx5d|xn\\-\\-5su34j936bgsg|xn\\-\\-5tzm5g"
+ + "|xn\\-\\-6frz82g|xn\\-\\-6qq986b3xl|xn\\-\\-80adxhks|xn\\-\\-80ao21a|xn\\-\\-80aqecdr1a"
+ + "|xn\\-\\-80asehdb|xn\\-\\-80aswg|xn\\-\\-8y0a063a|xn\\-\\-90a3ac|xn\\-\\-90ae|xn\\-\\-90ais"
+ + "|xn\\-\\-9dbq2a|xn\\-\\-9et52u|xn\\-\\-9krt00a|xn\\-\\-b4w605ferd|xn\\-\\-bck1b9a5dre4c"
+ + "|xn\\-\\-c1avg|xn\\-\\-c2br7g|xn\\-\\-cck2b3b|xn\\-\\-cckwcxetd|xn\\-\\-cg4bki|xn\\-\\-clchc0ea0b2g2a9gcd"
+ + "|xn\\-\\-czr694b|xn\\-\\-czrs0t|xn\\-\\-czru2d|xn\\-\\-d1acj3b|xn\\-\\-d1alf|xn\\-\\-e1a4c"
+ + "|xn\\-\\-eckvdtc9d|xn\\-\\-efvy88h|xn\\-\\-fct429k|xn\\-\\-fhbei|xn\\-\\-fiq228c5hs"
+ + "|xn\\-\\-fiq64b|xn\\-\\-fiqs8s|xn\\-\\-fiqz9s|xn\\-\\-fjq720a|xn\\-\\-flw351e|xn\\-\\-fpcrj9c3d"
+ + "|xn\\-\\-fzc2c9e2c|xn\\-\\-fzys8d69uvgm|xn\\-\\-g2xx48c|xn\\-\\-gckr3f0f|xn\\-\\-gecrj9c"
+ + "|xn\\-\\-gk3at1e|xn\\-\\-h2breg3eve|xn\\-\\-h2brj9c|xn\\-\\-h2brj9c8c|xn\\-\\-hxt814e"
+ + "|xn\\-\\-i1b6b1a6a2e|xn\\-\\-imr513n|xn\\-\\-io0a7i|xn\\-\\-j1aef|xn\\-\\-j1amh|xn\\-\\-j6w193g"
+ + "|xn\\-\\-jlq480n2rg|xn\\-\\-jvr189m|xn\\-\\-kcrx77d1x4a|xn\\-\\-kprw13d|xn\\-\\-kpry57d"
+ "|xn\\-\\-kput3i|xn\\-\\-l1acc|xn\\-\\-lgbbat1ad8j|xn\\-\\-mgb9awbf|xn\\-\\-mgba3a3ejt"
- + "|xn\\-\\-mgba3a4f16a|xn\\-\\-mgbaam7a8h|xn\\-\\-mgbab2bd|xn\\-\\-mgbayh7gpa|xn\\-\\-mgbbh1a71e"
- + "|xn\\-\\-mgbc0a9azcg|xn\\-\\-mgberp4a5d4ar|xn\\-\\-mgbpl2fh|xn\\-\\-mgbtx2b|xn\\-\\-mgbx4cd0ab"
- + "|xn\\-\\-mk1bu44c|xn\\-\\-mxtq1m|xn\\-\\-ngbc5azd|xn\\-\\-node|xn\\-\\-nqv7f|xn\\-\\-nqv7fs00ema"
- + "|xn\\-\\-nyqy26a|xn\\-\\-o3cw4h|xn\\-\\-ogbpf8fl|xn\\-\\-p1acf|xn\\-\\-p1ai|xn\\-\\-pgbs0dh"
- + "|xn\\-\\-pssy2u|xn\\-\\-q9jyb4c|xn\\-\\-qcka1pmc|xn\\-\\-qxam|xn\\-\\-rhqv96g|xn\\-\\-s9brj9c"
- + "|xn\\-\\-ses554g|xn\\-\\-t60b56a|xn\\-\\-tckwe|xn\\-\\-unup4y|xn\\-\\-vermgensberater\\-ctb"
- + "|xn\\-\\-vermgensberatung\\-pwb|xn\\-\\-vhquv|xn\\-\\-vuq861b|xn\\-\\-wgbh1c|xn\\-\\-wgbl6a"
+ + "|xn\\-\\-mgba3a4f16a|xn\\-\\-mgba7c0bbn0a|xn\\-\\-mgbaakc7dvf|xn\\-\\-mgbaam7a8h|xn\\-\\-mgbab2bd"
+ + "|xn\\-\\-mgbah1a3hjkrd|xn\\-\\-mgbai9azgqp6j|xn\\-\\-mgbayh7gpa|xn\\-\\-mgbbh1a|xn\\-\\-mgbbh1a71e"
+ + "|xn\\-\\-mgbc0a9azcg|xn\\-\\-mgbca7dzdo|xn\\-\\-mgbcpq6gpa1a|xn\\-\\-mgberp4a5d4ar|xn\\-\\-mgbgu82a"
+ + "|xn\\-\\-mgbi4ecexp|xn\\-\\-mgbpl2fh|xn\\-\\-mgbt3dhd|xn\\-\\-mgbtx2b|xn\\-\\-mgbx4cd0ab"
+ + "|xn\\-\\-mix891f|xn\\-\\-mk1bu44c|xn\\-\\-mxtq1m|xn\\-\\-ngbc5azd|xn\\-\\-ngbe9e0a|xn\\-\\-ngbrx"
+ + "|xn\\-\\-node|xn\\-\\-nqv7f|xn\\-\\-nqv7fs00ema|xn\\-\\-nyqy26a|xn\\-\\-o3cw4h|xn\\-\\-ogbpf8fl"
+ + "|xn\\-\\-otu796d|xn\\-\\-p1acf|xn\\-\\-p1ai|xn\\-\\-pgbs0dh|xn\\-\\-pssy2u|xn\\-\\-q7ce6a"
+ + "|xn\\-\\-q9jyb4c|xn\\-\\-qcka1pmc|xn\\-\\-qxa6a|xn\\-\\-qxam|xn\\-\\-rhqv96g|xn\\-\\-rovu88b"
+ + "|xn\\-\\-rvc1e0am3e|xn\\-\\-s9brj9c|xn\\-\\-ses554g|xn\\-\\-t60b56a|xn\\-\\-tckwe|xn\\-\\-tiq49xqyj"
+ + "|xn\\-\\-unup4y|xn\\-\\-vermgensberater\\-ctb|xn\\-\\-vermgensberatung\\-pwb|xn\\-\\-vhquv"
+ + "|xn\\-\\-vuq861b|xn\\-\\-w4r85el8fhu5dnra|xn\\-\\-w4rs40l|xn\\-\\-wgbh1c|xn\\-\\-wgbl6a"
+ "|xn\\-\\-xhq521b|xn\\-\\-xkc2al3hye2a|xn\\-\\-xkc2dl3a5ee0h|xn\\-\\-y9a3aq|xn\\-\\-yfro4i67o"
- + "|xn\\-\\-ygbi2ammx|xn\\-\\-zfr164b|xperia|xxx|xyz)"
- + "|(?:yachts|yamaxun|yandex|yodobashi|yoga|yokohama|youtube|y[et])"
- + "|(?:zara|zip|zone|zuerich|z[amw]))";
-
+ + "|xn\\-\\-ygbi2ammx|xn\\-\\-zfr164b|xxx|xyz)"
+ + "|(?:yachts|yahoo|yamaxun|yandex|yodobashi|yoga|yokohama|you|youtube|yun|y[et])"
+ + "|(?:zappos|zara|zero|zip|zone|zuerich|z[amw]))";
/**
* Kept for backward compatibility reasons.
*
diff --git a/core/java/android/view/InputWindowHandle.java b/core/java/android/view/InputWindowHandle.java
index bc83750..2761aae 100644
--- a/core/java/android/view/InputWindowHandle.java
+++ b/core/java/android/view/InputWindowHandle.java
@@ -35,8 +35,6 @@
* @hide
*/
public final class InputWindowHandle {
- // TODO (b/300094445): Convert to use correct flagging infrastructure
- public static final boolean USE_SURFACE_TRUSTED_OVERLAY = true;
/**
* An internal annotation for all the {@link android.os.InputConfig} flags that can be
@@ -61,6 +59,7 @@
InputConfig.DUPLICATE_TOUCH_TO_WALLPAPER,
InputConfig.IS_WALLPAPER,
InputConfig.PAUSE_DISPATCHING,
+ InputConfig.TRUSTED_OVERLAY,
InputConfig.WATCH_OUTSIDE_TOUCH,
InputConfig.SLIPPERY,
InputConfig.DISABLE_USER_ACTIVITY,
@@ -273,13 +272,4 @@
}
this.inputConfig &= ~inputConfig;
}
-
- public void setTrustedOverlay(SurfaceControl.Transaction t, SurfaceControl sc,
- boolean isTrusted) {
- if (USE_SURFACE_TRUSTED_OVERLAY) {
- t.setTrustedOverlay(sc, isTrusted);
- } else if (isTrusted) {
- inputConfig |= InputConfig.TRUSTED_OVERLAY;
- }
- }
}
diff --git a/core/java/android/view/KeyCharacterMap.aidl b/core/java/android/view/KeyCharacterMap.aidl
new file mode 100644
index 0000000..1a761a67
--- /dev/null
+++ b/core/java/android/view/KeyCharacterMap.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.view;
+
+parcelable KeyCharacterMap;
\ No newline at end of file
diff --git a/core/java/android/view/KeyCharacterMap.java b/core/java/android/view/KeyCharacterMap.java
index d8221a6..4fe53c2 100644
--- a/core/java/android/view/KeyCharacterMap.java
+++ b/core/java/android/view/KeyCharacterMap.java
@@ -16,6 +16,7 @@
package android.view;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.hardware.input.InputManagerGlobal;
@@ -309,6 +310,10 @@
private static native KeyCharacterMap nativeObtainEmptyKeyCharacterMap(int deviceId);
private static native boolean nativeEquals(long ptr1, long ptr2);
+ private static native void nativeApplyOverlay(long ptr, String layoutDescriptor,
+ String overlay);
+ private static native int nativeGetMappedKey(long ptr, int scanCode);
+
private KeyCharacterMap(Parcel in) {
if (in == null) {
throw new IllegalArgumentException("parcel must not be null");
@@ -368,6 +373,38 @@
}
/**
+ * Loads the key character map with applied KCM overlay.
+ *
+ * @param layoutDescriptor descriptor of the applied overlay KCM
+ * @param overlay string describing the overlay KCM
+ * @return The resultant key character map.
+ * @throws {@link UnavailableException} if the key character map
+ * could not be loaded because it was malformed or the default key character map
+ * is missing from the system.
+ * @hide
+ */
+ public static KeyCharacterMap load(@NonNull String layoutDescriptor, @NonNull String overlay) {
+ KeyCharacterMap kcm = KeyCharacterMap.load(VIRTUAL_KEYBOARD);
+ kcm.applyOverlay(layoutDescriptor, overlay);
+ return kcm;
+ }
+
+ private void applyOverlay(@NonNull String layoutDescriptor, @NonNull String overlay) {
+ nativeApplyOverlay(mPtr, layoutDescriptor, overlay);
+ }
+
+ /**
+ * Gets the mapped key for the provided scan code. Returns the provided default if no mapping
+ * found in the KeyCharacterMap.
+ *
+ * @hide
+ */
+ public int getMappedKeyOrDefault(int scanCode, int defaultKeyCode) {
+ int keyCode = nativeGetMappedKey(mPtr, scanCode);
+ return keyCode == KeyEvent.KEYCODE_UNKNOWN ? defaultKeyCode : keyCode;
+ }
+
+ /**
* Gets the Unicode character generated by the specified key and meta
* key state combination.
* <p>
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 776eeda..cdf5eec3 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -18,6 +18,7 @@
import static android.os.IInputConstants.INPUT_EVENT_FLAG_IS_ACCESSIBILITY_EVENT;
import static android.view.Display.DEFAULT_DISPLAY;
+
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.annotation.IntDef;
@@ -4360,17 +4361,6 @@
public boolean isResampled;
/**
- * Returns true if this pointer coords object was generated by resampling, rather than from
- * an actual input event from the device at this time.
- *
- * @hide
- */
- @TestApi
- public boolean isResampled() {
- return isResampled;
- }
-
- /**
* Clears the contents of this object.
* Resets all axes to zero.
*/
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 30fd2cf..4d53b2c 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1584,11 +1584,11 @@
*/
static final int DISABLED = 0x00000020;
- /**
- * Mask for use with setFlags indicating bits used for indicating whether
- * this view is enabled
- * {@hide}
- */
+ /**
+ * Mask for use with setFlags indicating bits used for indicating whether
+ * this view is enabled
+ * {@hide}
+ */
static final int ENABLED_MASK = 0x00000020;
/**
@@ -15251,13 +15251,13 @@
}
}
- /**
- * @see #performAccessibilityAction(int, Bundle)
- *
- * Note: Called from the default {@link AccessibilityDelegate}.
- *
- * @hide
- */
+ /**
+ * @see #performAccessibilityAction(int, Bundle)
+ *
+ * Note: Called from the default {@link AccessibilityDelegate}.
+ *
+ * @hide
+ */
@UnsupportedAppUsage
public boolean performAccessibilityActionInternal(int action, @Nullable Bundle arguments) {
if (isNestedScrollingEnabled()
@@ -17391,7 +17391,7 @@
return attachInfo.mHandler.hasCallbacks(mPendingCheckForLongPress);
}
- /**
+ /**
* Remove the pending click action
*/
@UnsupportedAppUsage
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index fe515cd..5720fc0 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -3752,7 +3752,15 @@
&& !child.isActivityDeniedForAutofillForUnimportantView())
|| (shouldIncludeAllChildrenViewWithAutofillTypeNotNone(afm)
&& child.getAutofillType() != AUTOFILL_TYPE_NONE)
- || shouldIncludeAllChildrenViews(afm)){
+ || shouldIncludeAllChildrenViews(afm)
+ || (child instanceof ViewGroup && child.getVisibility() != View.VISIBLE)) {
+ // If the child is a ViewGroup object and its visibility is not visible, include
+ // it as part of the assist structure. The children of these invisible ViewGroup
+ // objects are parsed and included in the assist structure. When the Autofill
+ // Provider determines the visibility of these children, it looks at their
+ // visibility as well as their parent's visibility. Omitting invisible parents
+ // will lead to the Autofill Provider incorrectly assuming that these children
+ // of invisible parents are actually visible.
list.add(child);
} else if (child instanceof ViewGroup) {
((ViewGroup) child).populateChildrenForAutofill(list, flags);
@@ -7404,19 +7412,26 @@
}
target = next;
}
- if (!childIsHit) {
+ if (!childIsHit && mFirstHoverTarget != null) {
target = mFirstHoverTarget;
+ final ArrayList<View> preorderedList = buildTouchDispatchChildList();
while (notEmpty && target != null) {
final HoverTarget next = target.next;
final View hoveredView = target.child;
- rect.set(hoveredView.mLeft, hoveredView.mTop, hoveredView.mRight,
- hoveredView.mBottom);
- matrix.mapRect(rect);
- notEmpty = region.op(Math.round(rect.left), Math.round(rect.top),
- Math.round(rect.right), Math.round(rect.bottom), Region.Op.DIFFERENCE);
+ if (!isOnTop(child, hoveredView, preorderedList)) {
+ rect.set(hoveredView.mLeft, hoveredView.mTop, hoveredView.mRight,
+ hoveredView.mBottom);
+ matrix.mapRect(rect);
+ notEmpty = region.op(Math.round(rect.left), Math.round(rect.top),
+ Math.round(rect.right), Math.round(rect.bottom),
+ Region.Op.DIFFERENCE);
+ }
target = next;
}
+ if (preorderedList != null) {
+ preorderedList.clear();
+ }
}
} else {
TouchTarget target = mFirstTouchTarget;
@@ -7429,19 +7444,26 @@
}
target = next;
}
- if (!childIsHit) {
+ if (!childIsHit && mFirstTouchTarget != null) {
target = mFirstTouchTarget;
+ final ArrayList<View> preorderedList = buildOrderedChildList();
while (notEmpty && target != null) {
final TouchTarget next = target.next;
final View touchedView = target.child;
- rect.set(touchedView.mLeft, touchedView.mTop, touchedView.mRight,
- touchedView.mBottom);
- matrix.mapRect(rect);
- notEmpty = region.op(Math.round(rect.left), Math.round(rect.top),
- Math.round(rect.right), Math.round(rect.bottom), Region.Op.DIFFERENCE);
+ if (!isOnTop(child, touchedView, preorderedList)) {
+ rect.set(touchedView.mLeft, touchedView.mTop, touchedView.mRight,
+ touchedView.mBottom);
+ matrix.mapRect(rect);
+ notEmpty = region.op(Math.round(rect.left), Math.round(rect.top),
+ Math.round(rect.right), Math.round(rect.bottom),
+ Region.Op.DIFFERENCE);
+ }
target = next;
}
+ if (preorderedList != null) {
+ preorderedList.clear();
+ }
}
}
@@ -7451,6 +7473,28 @@
return notEmpty;
}
+ /**
+ * Return true if the given {@code view} is drawn on top of the {@code otherView}.
+ * Both the {@code view} and {@code otherView} must be children of this ViewGroup.
+ * Otherwise, the returned value is meaningless.
+ */
+ private boolean isOnTop(View view, View otherView, ArrayList<View> preorderedList) {
+ final int childrenCount = mChildrenCount;
+ final boolean customOrder = preorderedList == null && isChildrenDrawingOrderEnabled();
+ final View[] children = mChildren;
+ for (int i = childrenCount - 1; i >= 0; i--) {
+ final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
+ final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);
+ if (child == view) {
+ return true;
+ }
+ if (child == otherView) {
+ return false;
+ }
+ }
+ // Can't find the view and otherView in the children list. Return value is meaningless.
+ return false;
+ }
private static void applyOpToRegionByBounds(Region region, View view, Region.Op op) {
final int[] locationInWindow = new int[2];
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index d191ccd..ff6165b 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -461,6 +461,9 @@
@NonNull Display mDisplay;
final String mBasePackageName;
+ // If we would like to keep a particular eye on the corresponding package.
+ final boolean mExtraDisplayListenerLogging;
+
final int[] mTmpLocation = new int[2];
final TypedValue mTmpValue = new TypedValue();
@@ -1010,6 +1013,8 @@
mWindowLayout = windowLayout;
mDisplay = display;
mBasePackageName = context.getBasePackageName();
+ final String name = DisplayProperties.debug_vri_package().orElse(null);
+ mExtraDisplayListenerLogging = !TextUtils.isEmpty(name) && name.equals(mBasePackageName);
mThread = Thread.currentThread();
mLocation = new WindowLeaked(null);
mLocation.fillInStackTrace();
@@ -1444,6 +1449,10 @@
// We should update mAttachInfo.mDisplayState after registerDisplayListener
// because displayState might be changed before registerDisplayListener.
mAttachInfo.mDisplayState = mDisplay.getState();
+ if (mExtraDisplayListenerLogging) {
+ Slog.i(mTag, "(" + mBasePackageName + ") Initial DisplayState: "
+ + mAttachInfo.mDisplayState, new Throwable());
+ }
if ((res & WindowManagerGlobal.ADD_FLAG_USE_BLAST) != 0) {
mUseBLASTAdapter = true;
}
@@ -1528,6 +1537,9 @@
* Register any kind of listeners if setView was success.
*/
private void registerListeners() {
+ if (mExtraDisplayListenerLogging) {
+ Slog.i(mTag, "Register listeners: " + mBasePackageName);
+ }
mAccessibilityManager.addAccessibilityStateChangeListener(
mAccessibilityInteractionConnectionManager, mHandler);
mAccessibilityManager.addHighTextContrastStateChangeListener(
@@ -1553,6 +1565,9 @@
DisplayManagerGlobal
.getInstance()
.unregisterDisplayListener(mDisplayListener);
+ if (mExtraDisplayListenerLogging) {
+ Slog.w(mTag, "Unregister listeners: " + mBasePackageName, new Throwable());
+ }
}
private void setTag() {
@@ -1960,9 +1975,16 @@
private final DisplayListener mDisplayListener = new DisplayListener() {
@Override
public void onDisplayChanged(int displayId) {
+ if (mExtraDisplayListenerLogging) {
+ Slog.i(mTag, "Received onDisplayChanged - " + mView);
+ }
if (mView != null && mDisplay.getDisplayId() == displayId) {
final int oldDisplayState = mAttachInfo.mDisplayState;
final int newDisplayState = mDisplay.getState();
+ if (mExtraDisplayListenerLogging) {
+ Slog.i(mTag, "DisplayState - old: " + oldDisplayState
+ + ", new: " + newDisplayState);
+ }
if (oldDisplayState != newDisplayState) {
mAttachInfo.mDisplayState = newDisplayState;
pokeDrawLockIfNeeded();
@@ -3995,8 +4017,6 @@
mWindowFocusChanged = false;
hasWindowFocus = mUpcomingWindowFocus;
}
- // TODO (b/131181940): Make sure this doesn't leak Activity with mActivityConfigCallback
- // config changes.
if (hasWindowFocus) {
mInsetsController.onWindowFocusGained(
getFocusedViewOrNull() != null /* hasViewFocused */);
diff --git a/core/java/android/view/WindowLayout.java b/core/java/android/view/WindowLayout.java
index 3b8298e..dda3993 100644
--- a/core/java/android/view/WindowLayout.java
+++ b/core/java/android/view/WindowLayout.java
@@ -124,16 +124,16 @@
|| cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES)) {
final Insets systemBarsInsets = state.calculateInsets(
displayFrame, systemBars(), requestedVisibleTypes);
- if (systemBarsInsets.left > 0) {
+ if (systemBarsInsets.left >= cutout.getSafeInsetLeft()) {
displayCutoutSafeExceptMaybeBars.left = MIN_X;
}
- if (systemBarsInsets.top > 0) {
+ if (systemBarsInsets.top >= cutout.getSafeInsetTop()) {
displayCutoutSafeExceptMaybeBars.top = MIN_Y;
}
- if (systemBarsInsets.right > 0) {
+ if (systemBarsInsets.right >= cutout.getSafeInsetRight()) {
displayCutoutSafeExceptMaybeBars.right = MAX_X;
}
- if (systemBarsInsets.bottom > 0) {
+ if (systemBarsInsets.bottom >= cutout.getSafeInsetBottom()) {
displayCutoutSafeExceptMaybeBars.bottom = MAX_Y;
}
}
diff --git a/core/java/android/view/inputmethod/flags.aconfig b/core/java/android/view/inputmethod/flags.aconfig
index 92d3408..c144289 100644
--- a/core/java/android/view/inputmethod/flags.aconfig
+++ b/core/java/android/view/inputmethod/flags.aconfig
@@ -2,7 +2,8 @@
flag {
name: "refactor_insets_controller"
- namespace: "inputmethod"
+ namespace: "input_method"
description: "Feature flag for refactoring InsetsController and removing ImeInsetsSourceConsumer"
bug: "298172246"
+ is_fixed_read_only: true
}
\ No newline at end of file
diff --git a/core/java/android/widget/AdapterViewFlipper.java b/core/java/android/widget/AdapterViewFlipper.java
index 065089f5..53c73c6 100644
--- a/core/java/android/widget/AdapterViewFlipper.java
+++ b/core/java/android/widget/AdapterViewFlipper.java
@@ -16,10 +16,7 @@
package android.widget;
-import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.content.res.TypedArray;
import android.os.Message;
import android.util.AttributeSet;
@@ -48,7 +45,6 @@
private boolean mRunning = false;
private boolean mStarted = false;
private boolean mVisible = false;
- private boolean mUserPresent = true;
private boolean mAdvancedByHost = false;
public AdapterViewFlipper(Context context) {
@@ -82,40 +78,10 @@
a.recycle();
}
- private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
- if (Intent.ACTION_SCREEN_OFF.equals(action)) {
- mUserPresent = false;
- updateRunning();
- } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
- mUserPresent = true;
- updateRunning(false);
- }
- }
- };
-
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- // Listen for broadcasts related to user-presence
- final IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_SCREEN_OFF);
- filter.addAction(Intent.ACTION_USER_PRESENT);
-
- // OK, this is gross but needed. This class is supported by the
- // remote views machanism and as a part of that the remote views
- // can be inflated by a context for another user without the app
- // having interact users permission - just for loading resources.
- // For exmaple, when adding widgets from a user profile to the
- // home screen. Therefore, we register the receiver as the current
- // user not the one the context is for.
- getContext().registerReceiverAsUser(mReceiver, android.os.Process.myUserHandle(),
- filter, null, getHandler());
-
-
if (mAutoStart) {
// Automatically start when requested
startFlipping();
@@ -126,8 +92,6 @@
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
mVisible = false;
-
- getContext().unregisterReceiver(mReceiver);
updateRunning();
}
@@ -235,8 +199,7 @@
* true.
*/
private void updateRunning(boolean flipNow) {
- boolean running = !mAdvancedByHost && mVisible && mStarted && mUserPresent
- && mAdapter != null;
+ boolean running = !mAdvancedByHost && mVisible && mStarted && mAdapter != null;
if (running != mRunning) {
if (running) {
showOnly(mWhichChild, flipNow);
@@ -248,7 +211,7 @@
}
if (LOGD) {
Log.d(TAG, "updateRunning() mVisible=" + mVisible + ", mStarted=" + mStarted
- + ", mUserPresent=" + mUserPresent + ", mRunning=" + mRunning);
+ + ", mRunning=" + mRunning);
}
}
diff --git a/core/java/android/widget/AnalogClock.java b/core/java/android/widget/AnalogClock.java
index 1f0e95e..e0158332 100644
--- a/core/java/android/widget/AnalogClock.java
+++ b/core/java/android/widget/AnalogClock.java
@@ -23,7 +23,6 @@
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.BlendMode;
@@ -37,6 +36,9 @@
import android.view.View;
import android.view.inspector.InspectableProperty;
import android.widget.RemoteViews.RemoteView;
+import android.widget.TextClock.ClockEventDelegate;
+
+import com.android.internal.util.Preconditions;
import java.time.Clock;
import java.time.DateTimeException;
@@ -112,6 +114,7 @@
public AnalogClock(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+ mClockEventDelegate = new ClockEventDelegate(context);
mSecondsHandFps = AppGlobals.getIntCoreSetting(
WidgetFlags.KEY_ANALOG_CLOCK_SECONDS_HAND_FPS,
context.getResources()
@@ -584,21 +587,9 @@
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- IntentFilter filter = new IntentFilter();
if (!mReceiverAttached) {
- filter.addAction(Intent.ACTION_TIME_CHANGED);
- filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
-
- // OK, this is gross but needed. This class is supported by the
- // remote views mechanism and as a part of that the remote views
- // can be inflated by a context for another user without the app
- // having interact users permission - just for loading resources.
- // For example, when adding widgets from a user profile to the
- // home screen. Therefore, we register the receiver as the current
- // user not the one the context is for.
- getContext().registerReceiverAsUser(mIntentReceiver,
- android.os.Process.myUserHandle(), filter, null, getHandler());
+ mClockEventDelegate.registerTimeChangeReceiver(mIntentReceiver, getHandler());
mReceiverAttached = true;
}
@@ -615,12 +606,23 @@
@Override
protected void onDetachedFromWindow() {
if (mReceiverAttached) {
- getContext().unregisterReceiver(mIntentReceiver);
+ mClockEventDelegate.unregisterTimeChangeReceiver(mIntentReceiver);
mReceiverAttached = false;
}
super.onDetachedFromWindow();
}
+ /**
+ * Sets a delegate to handle clock event registration. This must be called before the view is
+ * attached to the window
+ *
+ * @hide
+ */
+ public void setClockEventDelegate(ClockEventDelegate delegate) {
+ Preconditions.checkState(!mReceiverAttached, "Clock events already registered");
+ mClockEventDelegate = delegate;
+ }
+
private void onVisible() {
if (!mVisible) {
mVisible = true;
@@ -797,6 +799,7 @@
}
};
private boolean mReceiverAttached;
+ private ClockEventDelegate mClockEventDelegate;
private final Runnable mTick = new Runnable() {
@Override
diff --git a/core/java/android/widget/TextClock.java b/core/java/android/widget/TextClock.java
index e48afb2..255bd67 100644
--- a/core/java/android/widget/TextClock.java
+++ b/core/java/android/widget/TextClock.java
@@ -16,6 +16,7 @@
package android.widget;
+import static android.os.Process.myUserHandle;
import static android.view.ViewDebug.ExportedProperty;
import static android.widget.RemoteViews.RemoteView;
@@ -24,7 +25,6 @@
import android.app.ActivityManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -43,6 +43,7 @@
import android.view.inspector.InspectableProperty;
import com.android.internal.R;
+import com.android.internal.util.Preconditions;
import java.time.Duration;
import java.time.Instant;
@@ -141,6 +142,8 @@
private boolean mRegistered;
private boolean mShouldRunTicker;
+ private ClockEventDelegate mClockEventDelegate;
+
private Calendar mTime;
private String mTimeZone;
@@ -178,8 +181,7 @@
if (mTimeZone == null && Intent.ACTION_TIMEZONE_CHANGED.equals(intent.getAction())) {
final String timeZone = intent.getStringExtra(Intent.EXTRA_TIMEZONE);
createTime(timeZone);
- } else if (!mShouldRunTicker && (Intent.ACTION_TIME_TICK.equals(intent.getAction())
- || Intent.ACTION_TIME_CHANGED.equals(intent.getAction()))) {
+ } else if (!mShouldRunTicker && Intent.ACTION_TIME_CHANGED.equals(intent.getAction())) {
return;
}
onTimeChanged();
@@ -282,6 +284,7 @@
if (mFormat24 == null) {
mFormat24 = getBestDateTimePattern("Hm");
}
+ mClockEventDelegate = new ClockEventDelegate(getContext());
createTime(mTimeZone);
chooseFormat();
@@ -431,6 +434,17 @@
}
/**
+ * Sets a delegate to handle clock event registration. This must be called before the view is
+ * attached to the window
+ *
+ * @hide
+ */
+ public void setClockEventDelegate(ClockEventDelegate delegate) {
+ Preconditions.checkState(!mRegistered, "Clock events already registered");
+ mClockEventDelegate = delegate;
+ }
+
+ /**
* Update the displayed time if necessary and invalidate the view.
*/
public void refreshTime() {
@@ -557,7 +571,7 @@
if (!mRegistered) {
mRegistered = true;
- registerReceiver();
+ mClockEventDelegate.registerTimeChangeReceiver(mIntentReceiver, getHandler());
registerObserver();
createTime(mTimeZone);
@@ -582,7 +596,7 @@
super.onDetachedFromWindow();
if (mRegistered) {
- unregisterReceiver();
+ mClockEventDelegate.unregisterTimeChangeReceiver(mIntentReceiver);
unregisterObserver();
mRegistered = false;
@@ -598,56 +612,27 @@
mStopTicking = true;
}
- private void registerReceiver() {
- final IntentFilter filter = new IntentFilter();
-
- filter.addAction(Intent.ACTION_TIME_CHANGED);
- filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
-
- // OK, this is gross but needed. This class is supported by the
- // remote views mechanism and as a part of that the remote views
- // can be inflated by a context for another user without the app
- // having interact users permission - just for loading resources.
- // For example, when adding widgets from a managed profile to the
- // home screen. Therefore, we register the receiver as the user
- // the app is running as not the one the context is for.
- getContext().registerReceiverAsUser(mIntentReceiver, android.os.Process.myUserHandle(),
- filter, null, getHandler());
- }
-
private void registerObserver() {
if (mRegistered) {
if (mFormatChangeObserver == null) {
mFormatChangeObserver = new FormatChangeObserver(getHandler());
}
- final ContentResolver resolver = getContext().getContentResolver();
- Uri uri = Settings.System.getUriFor(Settings.System.TIME_12_24);
- if (mShowCurrentUserTime) {
- resolver.registerContentObserver(uri, true,
- mFormatChangeObserver, UserHandle.USER_ALL);
- } else {
- // UserHandle.myUserId() is needed. This class is supported by the
- // remote views mechanism and as a part of that the remote views
- // can be inflated by a context for another user without the app
- // having interact users permission - just for loading resources.
- // For example, when adding widgets from a managed profile to the
- // home screen. Therefore, we register the ContentObserver with the user
- // the app is running (e.g. the launcher) and not the user of the
- // context (e.g. the widget's profile).
- resolver.registerContentObserver(uri, true,
- mFormatChangeObserver, UserHandle.myUserId());
- }
+ // UserHandle.myUserId() is needed. This class is supported by the
+ // remote views mechanism and as a part of that the remote views
+ // can be inflated by a context for another user without the app
+ // having interact users permission - just for loading resources.
+ // For example, when adding widgets from a managed profile to the
+ // home screen. Therefore, we register the ContentObserver with the user
+ // the app is running (e.g. the launcher) and not the user of the
+ // context (e.g. the widget's profile).
+ int userHandle = mShowCurrentUserTime ? UserHandle.USER_ALL : UserHandle.myUserId();
+ mClockEventDelegate.registerFormatChangeObserver(mFormatChangeObserver, userHandle);
}
}
- private void unregisterReceiver() {
- getContext().unregisterReceiver(mIntentReceiver);
- }
-
private void unregisterObserver() {
if (mFormatChangeObserver != null) {
- final ContentResolver resolver = getContext().getContentResolver();
- resolver.unregisterContentObserver(mFormatChangeObserver);
+ mClockEventDelegate.unregisterFormatChangeObserver(mFormatChangeObserver);
}
}
@@ -674,4 +659,59 @@
stream.addProperty("format", mFormat == null ? null : mFormat.toString());
stream.addProperty("hasSeconds", mHasSeconds);
}
+
+ /**
+ * Utility class to delegate some system event handling to allow overring the default behavior
+ *
+ * @hide
+ */
+ public static class ClockEventDelegate {
+
+ private final Context mContext;
+
+ public ClockEventDelegate(Context context) {
+ mContext = context;
+ }
+
+ /**
+ * Registers a receiver for actions {@link Intent#ACTION_TIME_CHANGED} and
+ * {@link Intent#ACTION_TIMEZONE_CHANGED}
+ *
+ * OK, this is gross but needed. This class is supported by the remote views mechanism and
+ * as a part of that the remote views can be inflated by a context for another user without
+ * the app having interact users permission - just for loading resources. For example,
+ * when adding widgets from a managed profile to the home screen. Therefore, we register
+ * the receiver as the user the app is running as not the one the context is for.
+ */
+ public void registerTimeChangeReceiver(BroadcastReceiver receiver, Handler handler) {
+ final IntentFilter filter = new IntentFilter();
+
+ filter.addAction(Intent.ACTION_TIME_CHANGED);
+ filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
+
+ mContext.registerReceiverAsUser(receiver, myUserHandle(), filter, null, handler);
+ }
+
+ /**
+ * Unregisters a previously registered receiver
+ */
+ public void unregisterTimeChangeReceiver(BroadcastReceiver receiver) {
+ mContext.unregisterReceiver(receiver);
+ }
+
+ /**
+ * Registers an observer for time format changes
+ */
+ public void registerFormatChangeObserver(ContentObserver observer, int userHandle) {
+ Uri uri = Settings.System.getUriFor(Settings.System.TIME_12_24);
+ mContext.getContentResolver().registerContentObserver(uri, true, observer, userHandle);
+ }
+
+ /**
+ * Unregisters a previously registered observer
+ */
+ public void unregisterFormatChangeObserver(ContentObserver observer) {
+ mContext.getContentResolver().unregisterContentObserver(observer);
+ }
+ }
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 05063365..a0d0656 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -27,6 +27,7 @@
import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX;
import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY;
import static android.view.inputmethod.CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION;
+
import static com.android.text.flags.Flags.FLAG_USE_BOUNDS_FOR_WIDTH;
import android.R;
@@ -240,7 +241,6 @@
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastMath;
import com.android.internal.util.Preconditions;
-import com.android.text.flags.Flags;
import libcore.util.EmptyArray;
@@ -1634,7 +1634,7 @@
}
if (CompatChanges.isChangeEnabled(USE_BOUNDS_FOR_WIDTH)) {
- mUseBoundsForWidth = Flags.useBoundsForWidth();
+ mUseBoundsForWidth = false; // TODO: Connect to the flag.
} else {
mUseBoundsForWidth = false;
}
@@ -14151,7 +14151,8 @@
selectionStart, OffsetMapping.MAP_STRATEGY_CURSOR);
final int line = layout.getLineForOffset(offsetTransformed);
final float insertionMarkerX =
- layout.getPrimaryHorizontal(offsetTransformed)
+ layout.getPrimaryHorizontal(
+ offsetTransformed, layout.shouldClampCursor(line))
+ viewportToContentHorizontalOffset;
final float insertionMarkerTop = layout.getLineTop(line)
+ viewportToContentVerticalOffset;
diff --git a/core/java/android/widget/ViewFlipper.java b/core/java/android/widget/ViewFlipper.java
index 5abb6e1..eaf037e 100644
--- a/core/java/android/widget/ViewFlipper.java
+++ b/core/java/android/widget/ViewFlipper.java
@@ -18,10 +18,7 @@
import android.annotation.IntRange;
import android.compat.annotation.UnsupportedAppUsage;
-import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.content.res.TypedArray;
import android.os.Build;
import android.os.Message;
@@ -51,8 +48,6 @@
private boolean mRunning = false;
private boolean mStarted = false;
private boolean mVisible = false;
- @UnsupportedAppUsage
- private boolean mUserPresent = true;
public ViewFlipper(Context context) {
super(context);
@@ -70,39 +65,10 @@
a.recycle();
}
- private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
- if (Intent.ACTION_SCREEN_OFF.equals(action)) {
- mUserPresent = false;
- updateRunning();
- } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
- mUserPresent = true;
- updateRunning(false);
- }
- }
- };
-
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- // Listen for broadcasts related to user-presence
- final IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_SCREEN_OFF);
- filter.addAction(Intent.ACTION_USER_PRESENT);
-
- // OK, this is gross but needed. This class is supported by the
- // remote views machanism and as a part of that the remote views
- // can be inflated by a context for another user without the app
- // having interact users permission - just for loading resources.
- // For exmaple, when adding widgets from a user profile to the
- // home screen. Therefore, we register the receiver as the current
- // user not the one the context is for.
- getContext().registerReceiverAsUser(mReceiver, android.os.Process.myUserHandle(),
- filter, null, getHandler());
-
if (mAutoStart) {
// Automatically start when requested
startFlipping();
@@ -113,8 +79,6 @@
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
mVisible = false;
-
- getContext().unregisterReceiver(mReceiver);
updateRunning();
}
@@ -186,7 +150,7 @@
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void updateRunning(boolean flipNow) {
- boolean running = mVisible && mStarted && mUserPresent;
+ boolean running = mVisible && mStarted;
if (running != mRunning) {
if (running) {
showOnly(mWhichChild, flipNow);
@@ -198,7 +162,7 @@
}
if (LOGD) {
Log.d(TAG, "updateRunning() mVisible=" + mVisible + ", mStarted=" + mStarted
- + ", mUserPresent=" + mUserPresent + ", mRunning=" + mRunning);
+ + ", mRunning=" + mRunning);
}
}
diff --git a/core/java/android/window/ScreenCapture.java b/core/java/android/window/ScreenCapture.java
index 95e9e86..e42193d 100644
--- a/core/java/android/window/ScreenCapture.java
+++ b/core/java/android/window/ScreenCapture.java
@@ -24,7 +24,6 @@
import android.graphics.Rect;
import android.hardware.HardwareBuffer;
import android.os.Build;
-import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
@@ -35,7 +34,6 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.function.ObjIntConsumer;
-
/**
* Handles display and layer captures for the system.
*
@@ -45,8 +43,6 @@
private static final String TAG = "ScreenCapture";
private static final int SCREENSHOT_WAIT_TIME_S = 4 * Build.HW_TIMEOUT_MULTIPLIER;
- private static native int nativeCaptureDisplay(DisplayCaptureArgs captureArgs,
- long captureListener);
private static native int nativeCaptureLayers(LayerCaptureArgs captureArgs,
long captureListener);
private static native long nativeCreateScreenCaptureListener(
@@ -56,37 +52,6 @@
private static native long getNativeListenerFinalizer();
/**
- * @param captureArgs Arguments about how to take the screenshot
- * @param captureListener A listener to receive the screenshot callback
- * @hide
- */
- public static int captureDisplay(@NonNull DisplayCaptureArgs captureArgs,
- @NonNull ScreenCaptureListener captureListener) {
- return nativeCaptureDisplay(captureArgs, captureListener.mNativeObject);
- }
-
- /**
- * Captures all the surfaces in a display and returns a {@link ScreenshotHardwareBuffer} with
- * the content.
- *
- * @hide
- */
- public static ScreenshotHardwareBuffer captureDisplay(
- DisplayCaptureArgs captureArgs) {
- SynchronousScreenCaptureListener syncScreenCapture = createSyncCaptureListener();
- int status = captureDisplay(captureArgs, syncScreenCapture);
- if (status != 0) {
- return null;
- }
-
- try {
- return syncScreenCapture.getBuffer();
- } catch (Exception e) {
- return null;
- }
- }
-
- /**
* Captures a layer and its children and returns a {@link HardwareBuffer} with the content.
*
* @param layer The root layer to capture.
@@ -519,92 +484,6 @@
}
/**
- * The arguments class used to make display capture requests.
- *
- * @hide
- * @see #nativeCaptureDisplay(DisplayCaptureArgs, long)
- */
- public static class DisplayCaptureArgs extends CaptureArgs {
- private final IBinder mDisplayToken;
- private final int mWidth;
- private final int mHeight;
- private final boolean mUseIdentityTransform;
-
- private DisplayCaptureArgs(Builder builder) {
- super(builder);
- mDisplayToken = builder.mDisplayToken;
- mWidth = builder.mWidth;
- mHeight = builder.mHeight;
- mUseIdentityTransform = builder.mUseIdentityTransform;
- }
-
- /**
- * The Builder class used to construct {@link DisplayCaptureArgs}
- */
- public static class Builder extends CaptureArgs.Builder<Builder> {
- private IBinder mDisplayToken;
- private int mWidth;
- private int mHeight;
- private boolean mUseIdentityTransform;
-
- /**
- * Construct a new {@link LayerCaptureArgs} with the set parameters. The builder
- * remains valid.
- */
- public DisplayCaptureArgs build() {
- if (mDisplayToken == null) {
- throw new IllegalStateException(
- "Can't take screenshot with null display token");
- }
- return new DisplayCaptureArgs(this);
- }
-
- public Builder(IBinder displayToken) {
- setDisplayToken(displayToken);
- }
-
- /**
- * The display to take the screenshot of.
- */
- public Builder setDisplayToken(IBinder displayToken) {
- mDisplayToken = displayToken;
- return this;
- }
-
- /**
- * Set the desired size of the returned buffer. The raw screen will be scaled down to
- * this size
- *
- * @param width The desired width of the returned buffer. Caller may pass in 0 if no
- * scaling is desired.
- * @param height The desired height of the returned buffer. Caller may pass in 0 if no
- * scaling is desired.
- */
- public Builder setSize(int width, int height) {
- mWidth = width;
- mHeight = height;
- return this;
- }
-
- /**
- * Replace the rotation transform of the display with the identity transformation while
- * taking the screenshot. This ensures the screenshot is taken in the ROTATION_0
- * orientation. Set this value to false if the screenshot should be taken in the
- * current screen orientation.
- */
- public Builder setUseIdentityTransform(boolean useIdentityTransform) {
- mUseIdentityTransform = useIdentityTransform;
- return this;
- }
-
- @Override
- Builder getThis() {
- return this;
- }
- }
- }
-
- /**
* The arguments class used to make layer capture requests.
*
* @hide
@@ -682,7 +561,6 @@
/**
* The object used to receive the results when invoking screen capture requests via
- * {@link #captureDisplay(DisplayCaptureArgs, ScreenCaptureListener)} or
* {@link #captureLayers(LayerCaptureArgs, ScreenCaptureListener)}
*
* This listener can only be used for a single call to capture content call.
@@ -784,8 +662,7 @@
/**
* Helper class to synchronously get the {@link ScreenshotHardwareBuffer} when calling
- * {@link #captureLayers(LayerCaptureArgs, ScreenCaptureListener)} or
- * {@link #captureDisplay(DisplayCaptureArgs, ScreenCaptureListener)}
+ * {@link #captureLayers(LayerCaptureArgs, ScreenCaptureListener)}
*/
public abstract static class SynchronousScreenCaptureListener extends ScreenCaptureListener {
SynchronousScreenCaptureListener(ObjIntConsumer<ScreenshotHardwareBuffer> consumer) {
diff --git a/core/java/android/window/StartingWindowInfo.java b/core/java/android/window/StartingWindowInfo.java
index 451acbe..a88e394 100644
--- a/core/java/android/window/StartingWindowInfo.java
+++ b/core/java/android/window/StartingWindowInfo.java
@@ -122,7 +122,7 @@
TYPE_PARAMETER_PROCESS_RUNNING,
TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT,
TYPE_PARAMETER_ACTIVITY_CREATED,
- TYPE_PARAMETER_USE_SOLID_COLOR_SPLASH_SCREEN,
+ TYPE_PARAMETER_ALLOW_ICON,
TYPE_PARAMETER_ALLOW_HANDLE_SOLID_COLOR_SCREEN,
TYPE_PARAMETER_WINDOWLESS,
TYPE_PARAMETER_LEGACY_SPLASH_SCREEN
@@ -143,7 +143,7 @@
/** @hide */
public static final int TYPE_PARAMETER_ACTIVITY_CREATED = 0x00000010;
/** @hide */
- public static final int TYPE_PARAMETER_USE_SOLID_COLOR_SPLASH_SCREEN = 0x00000020;
+ public static final int TYPE_PARAMETER_ALLOW_ICON = 0x00000020;
/**
* The parameter which indicates if the activity has finished drawing.
* @hide
diff --git a/core/java/android/window/TransitionRequestInfo.java b/core/java/android/window/TransitionRequestInfo.java
index edea297..9b10a7f 100644
--- a/core/java/android/window/TransitionRequestInfo.java
+++ b/core/java/android/window/TransitionRequestInfo.java
@@ -36,11 +36,17 @@
private final @WindowManager.TransitionType int mType;
/**
- * If non-null, If non-null, the task containing the activity whose lifecycle change (start or
+ * If non-null, the task containing the activity whose lifecycle change (start or
* finish) has caused this transition to occur.
*/
private @Nullable ActivityManager.RunningTaskInfo mTriggerTask;
+ /**
+ * If non-null, the task containing the pip activity that participates in this
+ * transition.
+ */
+ private @Nullable ActivityManager.RunningTaskInfo mPipTask;
+
/** If non-null, a remote-transition associated with the source of this transition. */
private @Nullable RemoteTransition mRemoteTransition;
@@ -59,7 +65,8 @@
@WindowManager.TransitionType int type,
@Nullable ActivityManager.RunningTaskInfo triggerTask,
@Nullable RemoteTransition remoteTransition) {
- this(type, triggerTask, remoteTransition, null /* displayChange */, 0 /* flags */);
+ this(type, triggerTask, null /* pipTask */,
+ remoteTransition, null /* displayChange */, 0 /* flags */);
}
/** constructor override */
@@ -68,7 +75,17 @@
@Nullable ActivityManager.RunningTaskInfo triggerTask,
@Nullable RemoteTransition remoteTransition,
int flags) {
- this(type, triggerTask, remoteTransition, null /* displayChange */, flags);
+ this(type, triggerTask, null /* pipTask */,
+ remoteTransition, null /* displayChange */, flags);
+ }
+
+ public TransitionRequestInfo(
+ @WindowManager.TransitionType int type,
+ @Nullable ActivityManager.RunningTaskInfo triggerTask,
+ @Nullable RemoteTransition remoteTransition,
+ @Nullable TransitionRequestInfo.DisplayChange displayChange,
+ int flags) {
+ this(type, triggerTask, null /* pipTask */, remoteTransition, displayChange, flags);
}
/** Requested change to a display. */
@@ -246,7 +263,7 @@
};
@DataClass.Generated(
- time = 1691627678294L,
+ time = 1693425051905L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/window/TransitionRequestInfo.java",
inputSignatures = "private final int mDisplayId\nprivate @android.annotation.Nullable android.graphics.Rect mStartAbsBounds\nprivate @android.annotation.Nullable android.graphics.Rect mEndAbsBounds\nprivate int mStartRotation\nprivate int mEndRotation\nprivate boolean mPhysicalDisplayChanged\nclass DisplayChange extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genSetters=true, genBuilder=false, genConstructor=false)")
@@ -283,6 +300,9 @@
* @param triggerTask
* If non-null, If non-null, the task containing the activity whose lifecycle change (start or
* finish) has caused this transition to occur.
+ * @param pipTask
+ * If non-null, If non-null, the task containing the activity whose lifecycle change (start or
+ * finish) has caused this transition to occur.
* @param remoteTransition
* If non-null, a remote-transition associated with the source of this transition.
* @param displayChange
@@ -296,6 +316,7 @@
public TransitionRequestInfo(
@WindowManager.TransitionType int type,
@Nullable ActivityManager.RunningTaskInfo triggerTask,
+ @Nullable ActivityManager.RunningTaskInfo pipTask,
@Nullable RemoteTransition remoteTransition,
@Nullable TransitionRequestInfo.DisplayChange displayChange,
int flags) {
@@ -303,6 +324,7 @@
com.android.internal.util.AnnotationValidations.validate(
WindowManager.TransitionType.class, null, mType);
this.mTriggerTask = triggerTask;
+ this.mPipTask = pipTask;
this.mRemoteTransition = remoteTransition;
this.mDisplayChange = displayChange;
this.mFlags = flags;
@@ -319,7 +341,7 @@
}
/**
- * If non-null, If non-null, the task containing the activity whose lifecycle change (start or
+ * If non-null, the task containing the activity whose lifecycle change (start or
* finish) has caused this transition to occur.
*/
@DataClass.Generated.Member
@@ -328,6 +350,15 @@
}
/**
+ * If non-null, the task containing the pip activity that participates in this
+ * transition.
+ */
+ @DataClass.Generated.Member
+ public @Nullable ActivityManager.RunningTaskInfo getPipTask() {
+ return mPipTask;
+ }
+
+ /**
* If non-null, a remote-transition associated with the source of this transition.
*/
@DataClass.Generated.Member
@@ -354,7 +385,7 @@
}
/**
- * If non-null, If non-null, the task containing the activity whose lifecycle change (start or
+ * If non-null, the task containing the activity whose lifecycle change (start or
* finish) has caused this transition to occur.
*/
@DataClass.Generated.Member
@@ -364,6 +395,16 @@
}
/**
+ * If non-null, the task containing the pip activity that participates in this
+ * transition.
+ */
+ @DataClass.Generated.Member
+ public @android.annotation.NonNull TransitionRequestInfo setPipTask(@android.annotation.NonNull ActivityManager.RunningTaskInfo value) {
+ mPipTask = value;
+ return this;
+ }
+
+ /**
* If non-null, a remote-transition associated with the source of this transition.
*/
@DataClass.Generated.Member
@@ -392,6 +433,7 @@
return "TransitionRequestInfo { " +
"type = " + mType + ", " +
"triggerTask = " + mTriggerTask + ", " +
+ "pipTask = " + mPipTask + ", " +
"remoteTransition = " + mRemoteTransition + ", " +
"displayChange = " + mDisplayChange + ", " +
"flags = " + mFlags +
@@ -406,11 +448,13 @@
byte flg = 0;
if (mTriggerTask != null) flg |= 0x2;
- if (mRemoteTransition != null) flg |= 0x4;
- if (mDisplayChange != null) flg |= 0x8;
+ if (mPipTask != null) flg |= 0x4;
+ if (mRemoteTransition != null) flg |= 0x8;
+ if (mDisplayChange != null) flg |= 0x10;
dest.writeByte(flg);
dest.writeInt(mType);
if (mTriggerTask != null) dest.writeTypedObject(mTriggerTask, flags);
+ if (mPipTask != null) dest.writeTypedObject(mPipTask, flags);
if (mRemoteTransition != null) dest.writeTypedObject(mRemoteTransition, flags);
if (mDisplayChange != null) dest.writeTypedObject(mDisplayChange, flags);
dest.writeInt(mFlags);
@@ -430,14 +474,16 @@
byte flg = in.readByte();
int type = in.readInt();
ActivityManager.RunningTaskInfo triggerTask = (flg & 0x2) == 0 ? null : (ActivityManager.RunningTaskInfo) in.readTypedObject(ActivityManager.RunningTaskInfo.CREATOR);
- RemoteTransition remoteTransition = (flg & 0x4) == 0 ? null : (RemoteTransition) in.readTypedObject(RemoteTransition.CREATOR);
- TransitionRequestInfo.DisplayChange displayChange = (flg & 0x8) == 0 ? null : (TransitionRequestInfo.DisplayChange) in.readTypedObject(TransitionRequestInfo.DisplayChange.CREATOR);
+ ActivityManager.RunningTaskInfo pipTask = (flg & 0x4) == 0 ? null : (ActivityManager.RunningTaskInfo) in.readTypedObject(ActivityManager.RunningTaskInfo.CREATOR);
+ RemoteTransition remoteTransition = (flg & 0x8) == 0 ? null : (RemoteTransition) in.readTypedObject(RemoteTransition.CREATOR);
+ TransitionRequestInfo.DisplayChange displayChange = (flg & 0x10) == 0 ? null : (TransitionRequestInfo.DisplayChange) in.readTypedObject(TransitionRequestInfo.DisplayChange.CREATOR);
int flags = in.readInt();
this.mType = type;
com.android.internal.util.AnnotationValidations.validate(
WindowManager.TransitionType.class, null, mType);
this.mTriggerTask = triggerTask;
+ this.mPipTask = pipTask;
this.mRemoteTransition = remoteTransition;
this.mDisplayChange = displayChange;
this.mFlags = flags;
@@ -460,10 +506,10 @@
};
@DataClass.Generated(
- time = 1691627678327L,
+ time = 1693425051928L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/window/TransitionRequestInfo.java",
- inputSignatures = "private final @android.view.WindowManager.TransitionType int mType\nprivate @android.annotation.Nullable android.app.ActivityManager.RunningTaskInfo mTriggerTask\nprivate @android.annotation.Nullable android.window.RemoteTransition mRemoteTransition\nprivate @android.annotation.Nullable android.window.TransitionRequestInfo.DisplayChange mDisplayChange\nprivate final int mFlags\nclass TransitionRequestInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genSetters=true, genAidl=true)")
+ inputSignatures = "private final @android.view.WindowManager.TransitionType int mType\nprivate @android.annotation.Nullable android.app.ActivityManager.RunningTaskInfo mTriggerTask\nprivate @android.annotation.Nullable android.app.ActivityManager.RunningTaskInfo mPipTask\nprivate @android.annotation.Nullable android.window.RemoteTransition mRemoteTransition\nprivate @android.annotation.Nullable android.window.TransitionRequestInfo.DisplayChange mDisplayChange\nprivate final int mFlags\nclass TransitionRequestInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genSetters=true, genAidl=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 6d8512c..7e2c017 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -3024,28 +3024,31 @@
return shouldShowTabs()
&& (mMultiProfilePagerAdapter.getListAdapterForUserHandle(
UserHandle.of(UserHandle.myUserId())).getCount() > 0
- || shouldShowContentPreviewWhenEmpty())
+ || shouldShowStickyContentPreviewWhenEmpty())
&& shouldShowContentPreview();
}
/**
- * This method could be used to override the default behavior when we hide the preview area
- * when the current tab doesn't have any items.
+ * This method could be used to override the default behavior when we hide the sticky preview
+ * area when the current tab doesn't have any items.
*
- * @return true if we want to show the content preview area even if the tab for the current
- * user is empty
+ * @return {@code true} if we want to show the sticky content preview area even if the tab for
+ * the current user is empty
*/
- protected boolean shouldShowContentPreviewWhenEmpty() {
+ protected boolean shouldShowStickyContentPreviewWhenEmpty() {
return false;
}
- /**
- * @return true if we want to show the content preview area
- */
- protected boolean shouldShowContentPreview() {
+ @Override
+ public boolean shouldShowContentPreview() {
return isSendAction(getTargetIntent());
}
+ @Override
+ public boolean shouldShowServiceTargets() {
+ return shouldShowContentPreview() && !ActivityManager.isLowRamDeviceStatic();
+ }
+
private void updateStickyContentPreview() {
if (shouldShowStickyContentPreviewNoOrientationCheck()) {
// The sticky content preview is only shown when we show the work and personal tabs.
@@ -3407,11 +3410,7 @@
// There can be at most one row in the listview, that is internally
// a ViewGroup with 2 rows
public int getServiceTargetRowCount() {
- if (shouldShowContentPreview()
- && !ActivityManager.isLowRamDeviceStatic()) {
- return 1;
- }
- return 0;
+ return shouldShowServiceTargets() ? 1 : 0;
}
public int getAzLabelRowCount() {
diff --git a/core/java/com/android/internal/app/ChooserListAdapter.java b/core/java/com/android/internal/app/ChooserListAdapter.java
index f77e718..b3e828d 100644
--- a/core/java/com/android/internal/app/ChooserListAdapter.java
+++ b/core/java/com/android/internal/app/ChooserListAdapter.java
@@ -19,7 +19,6 @@
import static com.android.internal.app.ChooserActivity.TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE;
import static com.android.internal.app.ChooserActivity.TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER;
-import android.app.ActivityManager;
import android.app.prediction.AppPredictor;
import android.content.ComponentName;
import android.content.Context;
@@ -426,11 +425,9 @@
}
public int getServiceTargetCount() {
- if (mChooserListCommunicator.isSendAction(mChooserListCommunicator.getTargetIntent())
- && !ActivityManager.isLowRamDeviceStatic()) {
+ if (mChooserListCommunicator.shouldShowServiceTargets()) {
return Math.min(mServiceTargets.size(), mChooserListCommunicator.getMaxRankedTargets());
}
-
return 0;
}
@@ -779,6 +776,10 @@
void sendListViewUpdateMessage(UserHandle userHandle);
boolean isSendAction(Intent targetIntent);
+
+ boolean shouldShowContentPreview();
+
+ boolean shouldShowServiceTargets();
}
/**
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 9b5a3f7..e014ab0 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -40,6 +40,9 @@
import android.app.ActivityManager;
import android.app.KeyguardManager;
import android.app.SearchManager;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.Intent;
@@ -123,6 +126,7 @@
import com.android.internal.view.menu.MenuPresenter;
import com.android.internal.view.menu.MenuView;
import com.android.internal.widget.DecorContentParent;
+import com.android.window.flags.Flags;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -162,6 +166,14 @@
private final static int DEFAULT_BACKGROUND_FADE_DURATION_MS = 300;
+ /**
+ * Makes navigation bar color transparent by default if the target SDK is
+ * {@link Build.VERSION_CODES#VANILLA_ICE_CREAM} or above.
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ private static final long NAV_BAR_COLOR_DEFAULT_TRANSPARENT = 232195501L;
+
private static final int CUSTOM_TITLE_COMPATIBLE_FEATURES = DEFAULT_FEATURES |
(1 << FEATURE_CUSTOM_TITLE) |
(1 << FEATURE_CONTENT_TRANSITIONS) |
@@ -2525,6 +2537,8 @@
mNavigationBarColor =
navBarColor == navBarDefaultColor
+ && !(CompatChanges.isChangeEnabled(NAV_BAR_COLOR_DEFAULT_TRANSPARENT)
+ && Flags.navBarTransparentByDefault())
&& !context.getResources().getBoolean(
R.bool.config_navBarDefaultTransparent)
? navBarCompatibleColor
diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
index 52ffc98..a513ca5 100644
--- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java
+++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
@@ -21,6 +21,7 @@
import android.annotation.IdRes;
import android.content.Context;
+import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
@@ -53,10 +54,13 @@
private static final String TAG = "ResolverDrawerLayout";
private MetricsLogger mMetricsLogger;
+
+
/**
- * Max width of the whole drawer layout
+ * Max width of the whole drawer layout and its res id
*/
- private final int mMaxWidth;
+ private int mMaxWidthResId;
+ private int mMaxWidth;
/**
* Max total visible height of views not marked always-show when in the closed/initial state
@@ -152,6 +156,7 @@
final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ResolverDrawerLayout,
defStyleAttr, 0);
+ mMaxWidthResId = a.getResourceId(R.styleable.ResolverDrawerLayout_maxWidth, -1);
mMaxWidth = a.getDimensionPixelSize(R.styleable.ResolverDrawerLayout_maxWidth, -1);
mMaxCollapsedHeight = a.getDimensionPixelSize(
R.styleable.ResolverDrawerLayout_maxCollapsedHeight, 0);
@@ -1042,6 +1047,18 @@
return mAlwaysShowHeight;
}
+ /**
+ * Max width of the drawer needs to be updated after the configuration is changed.
+ * For example, foldables have different layout width when the device is folded and unfolded.
+ */
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ if (mMaxWidthResId > 0) {
+ mMaxWidth = getResources().getDimensionPixelSize(mMaxWidthResId);
+ }
+ }
+
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int width = getWidth();
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index fe95762..e0bcef6 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -565,6 +565,51 @@
return android_os_Debug_getPssPid(env, clazz, getpid(), NULL, NULL);
}
+static jlong android_os_Debug_getRssPid(JNIEnv* env, jobject clazz, jint pid,
+ jlongArray outMemtrack) {
+ jlong rss = 0;
+ jlong memtrack = 0;
+
+ struct graphics_memory_pss graphics_mem;
+ if (read_memtrack_memory(pid, &graphics_mem) == 0) {
+ rss = memtrack = graphics_mem.graphics + graphics_mem.gl + graphics_mem.other;
+ }
+
+ ::android::meminfo::ProcMemInfo proc_mem(pid);
+ uint64_t status_rss;
+ if (proc_mem.StatusVmRSS(&status_rss)) {
+ rss += status_rss;
+ } else {
+ return 0;
+ }
+
+ if (outMemtrack != NULL) {
+ int outLen = env->GetArrayLength(outMemtrack);
+ if (outLen >= 1) {
+ jlong* outMemtrackArray = env->GetLongArrayElements(outMemtrack, 0);
+ if (outMemtrackArray != NULL) {
+ outMemtrackArray[0] = memtrack;
+ if (outLen >= 2) {
+ outMemtrackArray[1] = graphics_mem.graphics;
+ }
+ if (outLen >= 3) {
+ outMemtrackArray[2] = graphics_mem.gl;
+ }
+ if (outLen >= 4) {
+ outMemtrackArray[3] = graphics_mem.other;
+ }
+ }
+ env->ReleaseLongArrayElements(outMemtrack, outMemtrackArray, 0);
+ }
+ }
+
+ return rss;
+}
+
+static jlong android_os_Debug_getRss(JNIEnv* env, jobject clazz) {
+ return android_os_Debug_getRssPid(env, clazz, getpid(), NULL);
+}
+
// The 1:1 mapping of MEMINFO_* enums here must match with the constants from
// Debug.java.
enum {
@@ -974,62 +1019,43 @@
*/
static const JNINativeMethod gMethods[] = {
- { "getNativeHeapSize", "()J",
- (void*) android_os_Debug_getNativeHeapSize },
- { "getNativeHeapAllocatedSize", "()J",
- (void*) android_os_Debug_getNativeHeapAllocatedSize },
- { "getNativeHeapFreeSize", "()J",
- (void*) android_os_Debug_getNativeHeapFreeSize },
- { "getMemoryInfo", "(Landroid/os/Debug$MemoryInfo;)V",
- (void*) android_os_Debug_getDirtyPages },
- { "getMemoryInfo", "(ILandroid/os/Debug$MemoryInfo;)Z",
- (void*) android_os_Debug_getDirtyPagesPid },
- { "getPss", "()J",
- (void*) android_os_Debug_getPss },
- { "getPss", "(I[J[J)J",
- (void*) android_os_Debug_getPssPid },
- { "getMemInfo", "([J)V",
- (void*) android_os_Debug_getMemInfo },
- { "dumpNativeHeap", "(Ljava/io/FileDescriptor;)V",
- (void*) android_os_Debug_dumpNativeHeap },
- { "dumpNativeMallocInfo", "(Ljava/io/FileDescriptor;)V",
- (void*) android_os_Debug_dumpNativeMallocInfo },
- { "getBinderSentTransactions", "()I",
- (void*) android_os_Debug_getBinderSentTransactions },
- { "getBinderReceivedTransactions", "()I",
- (void*) android_os_getBinderReceivedTransactions },
- { "getBinderLocalObjectCount", "()I",
- (void*)android_os_Debug_getLocalObjectCount },
- { "getBinderProxyObjectCount", "()I",
- (void*)android_os_Debug_getProxyObjectCount },
- { "getBinderDeathObjectCount", "()I",
- (void*)android_os_Debug_getDeathObjectCount },
- { "dumpJavaBacktraceToFileTimeout", "(ILjava/lang/String;I)Z",
- (void*)android_os_Debug_dumpJavaBacktraceToFileTimeout },
- { "dumpNativeBacktraceToFileTimeout", "(ILjava/lang/String;I)Z",
- (void*)android_os_Debug_dumpNativeBacktraceToFileTimeout },
- { "getUnreachableMemory", "(IZ)Ljava/lang/String;",
- (void*)android_os_Debug_getUnreachableMemory },
- { "getZramFreeKb", "()J",
- (void*)android_os_Debug_getFreeZramKb },
- { "getIonHeapsSizeKb", "()J",
- (void*)android_os_Debug_getIonHeapsSizeKb },
- { "getDmabufTotalExportedKb", "()J",
- (void*)android_os_Debug_getDmabufTotalExportedKb },
- { "getGpuPrivateMemoryKb", "()J",
- (void*)android_os_Debug_getGpuPrivateMemoryKb },
- { "getDmabufHeapTotalExportedKb", "()J",
- (void*)android_os_Debug_getDmabufHeapTotalExportedKb },
- { "getIonPoolsSizeKb", "()J",
- (void*)android_os_Debug_getIonPoolsSizeKb },
- { "getDmabufMappedSizeKb", "()J",
- (void*)android_os_Debug_getDmabufMappedSizeKb },
- { "getDmabufHeapPoolsSizeKb", "()J",
- (void*)android_os_Debug_getDmabufHeapPoolsSizeKb },
- { "getGpuTotalUsageKb", "()J",
- (void*)android_os_Debug_getGpuTotalUsageKb },
- { "isVmapStack", "()Z",
- (void*)android_os_Debug_isVmapStack },
+ {"getNativeHeapSize", "()J", (void*)android_os_Debug_getNativeHeapSize},
+ {"getNativeHeapAllocatedSize", "()J", (void*)android_os_Debug_getNativeHeapAllocatedSize},
+ {"getNativeHeapFreeSize", "()J", (void*)android_os_Debug_getNativeHeapFreeSize},
+ {"getMemoryInfo", "(Landroid/os/Debug$MemoryInfo;)V",
+ (void*)android_os_Debug_getDirtyPages},
+ {"getMemoryInfo", "(ILandroid/os/Debug$MemoryInfo;)Z",
+ (void*)android_os_Debug_getDirtyPagesPid},
+ {"getPss", "()J", (void*)android_os_Debug_getPss},
+ {"getPss", "(I[J[J)J", (void*)android_os_Debug_getPssPid},
+ {"getRss", "()J", (void*)android_os_Debug_getRss},
+ {"getRss", "(I[J)J", (void*)android_os_Debug_getRssPid},
+ {"getMemInfo", "([J)V", (void*)android_os_Debug_getMemInfo},
+ {"dumpNativeHeap", "(Ljava/io/FileDescriptor;)V", (void*)android_os_Debug_dumpNativeHeap},
+ {"dumpNativeMallocInfo", "(Ljava/io/FileDescriptor;)V",
+ (void*)android_os_Debug_dumpNativeMallocInfo},
+ {"getBinderSentTransactions", "()I", (void*)android_os_Debug_getBinderSentTransactions},
+ {"getBinderReceivedTransactions", "()I", (void*)android_os_getBinderReceivedTransactions},
+ {"getBinderLocalObjectCount", "()I", (void*)android_os_Debug_getLocalObjectCount},
+ {"getBinderProxyObjectCount", "()I", (void*)android_os_Debug_getProxyObjectCount},
+ {"getBinderDeathObjectCount", "()I", (void*)android_os_Debug_getDeathObjectCount},
+ {"dumpJavaBacktraceToFileTimeout", "(ILjava/lang/String;I)Z",
+ (void*)android_os_Debug_dumpJavaBacktraceToFileTimeout},
+ {"dumpNativeBacktraceToFileTimeout", "(ILjava/lang/String;I)Z",
+ (void*)android_os_Debug_dumpNativeBacktraceToFileTimeout},
+ {"getUnreachableMemory", "(IZ)Ljava/lang/String;",
+ (void*)android_os_Debug_getUnreachableMemory},
+ {"getZramFreeKb", "()J", (void*)android_os_Debug_getFreeZramKb},
+ {"getIonHeapsSizeKb", "()J", (void*)android_os_Debug_getIonHeapsSizeKb},
+ {"getDmabufTotalExportedKb", "()J", (void*)android_os_Debug_getDmabufTotalExportedKb},
+ {"getGpuPrivateMemoryKb", "()J", (void*)android_os_Debug_getGpuPrivateMemoryKb},
+ {"getDmabufHeapTotalExportedKb", "()J",
+ (void*)android_os_Debug_getDmabufHeapTotalExportedKb},
+ {"getIonPoolsSizeKb", "()J", (void*)android_os_Debug_getIonPoolsSizeKb},
+ {"getDmabufMappedSizeKb", "()J", (void*)android_os_Debug_getDmabufMappedSizeKb},
+ {"getDmabufHeapPoolsSizeKb", "()J", (void*)android_os_Debug_getDmabufHeapPoolsSizeKb},
+ {"getGpuTotalUsageKb", "()J", (void*)android_os_Debug_getGpuTotalUsageKb},
+ {"isVmapStack", "()Z", (void*)android_os_Debug_isVmapStack},
};
int register_android_os_Debug(JNIEnv *env)
diff --git a/core/jni/android_view_KeyCharacterMap.cpp b/core/jni/android_view_KeyCharacterMap.cpp
index ddaeb5a..7f69e22 100644
--- a/core/jni/android_view_KeyCharacterMap.cpp
+++ b/core/jni/android_view_KeyCharacterMap.cpp
@@ -240,6 +240,36 @@
return static_cast<jboolean>(*map1 == *map2);
}
+static void nativeApplyOverlay(JNIEnv* env, jobject clazz, jlong ptr, jstring nameObj,
+ jstring overlayObj) {
+ NativeKeyCharacterMap* map = reinterpret_cast<NativeKeyCharacterMap*>(ptr);
+ if (!map || !map->getMap()) {
+ return;
+ }
+ ScopedUtfChars nameChars(env, nameObj);
+ ScopedUtfChars overlayChars(env, overlayObj);
+ base::Result<std::shared_ptr<KeyCharacterMap>> ret =
+ KeyCharacterMap::loadContents(nameChars.c_str(), overlayChars.c_str(),
+ KeyCharacterMap::Format::OVERLAY);
+ if (ret.ok()) {
+ std::shared_ptr<KeyCharacterMap> overlay = *ret;
+ map->getMap()->combine(*overlay);
+ }
+}
+
+static jint nativeGetMappedKey(JNIEnv* env, jobject clazz, jlong ptr, jint scanCode) {
+ NativeKeyCharacterMap* map = reinterpret_cast<NativeKeyCharacterMap*>(ptr);
+ if (!map || !map->getMap()) {
+ return 0;
+ }
+ int32_t outKeyCode;
+ status_t mapKeyRes = map->getMap()->mapKey(scanCode, /*usageCode=*/0, &outKeyCode);
+ if (mapKeyRes != OK) {
+ return 0;
+ }
+ return static_cast<jint>(outKeyCode);
+}
+
/*
* JNI registration.
*/
@@ -260,7 +290,9 @@
{"nativeObtainEmptyKeyCharacterMap", "(I)Landroid/view/KeyCharacterMap;",
(void*)nativeObtainEmptyKeyCharacterMap},
{"nativeEquals", "(JJ)Z", (void*)nativeEquals},
-};
+ {"nativeApplyOverlay", "(JLjava/lang/String;Ljava/lang/String;)V",
+ (void*)nativeApplyOverlay},
+ {"nativeGetMappedKey", "(JI)I", (void*)nativeGetMappedKey}};
int register_android_view_KeyCharacterMap(JNIEnv* env)
{
diff --git a/core/jni/android_window_ScreenCapture.cpp b/core/jni/android_window_ScreenCapture.cpp
index bdf7eaa..beb8c9b 100644
--- a/core/jni/android_window_ScreenCapture.cpp
+++ b/core/jni/android_window_ScreenCapture.cpp
@@ -50,13 +50,6 @@
} gCaptureArgsClassInfo;
static struct {
- jfieldID displayToken;
- jfieldID width;
- jfieldID height;
- jfieldID useIdentityTransform;
-} gDisplayCaptureArgsClassInfo;
-
-static struct {
jfieldID layer;
jfieldID childrenOnly;
} gLayerCaptureArgsClassInfo;
@@ -181,39 +174,6 @@
gCaptureArgsClassInfo.hintForSeamlessTransition);
}
-static DisplayCaptureArgs displayCaptureArgsFromObject(JNIEnv* env,
- jobject displayCaptureArgsObject) {
- DisplayCaptureArgs captureArgs;
- getCaptureArgs(env, displayCaptureArgsObject, captureArgs);
-
- captureArgs.displayToken =
- ibinderForJavaObject(env,
- env->GetObjectField(displayCaptureArgsObject,
- gDisplayCaptureArgsClassInfo.displayToken));
- captureArgs.width =
- env->GetIntField(displayCaptureArgsObject, gDisplayCaptureArgsClassInfo.width);
- captureArgs.height =
- env->GetIntField(displayCaptureArgsObject, gDisplayCaptureArgsClassInfo.height);
- captureArgs.useIdentityTransform =
- env->GetBooleanField(displayCaptureArgsObject,
- gDisplayCaptureArgsClassInfo.useIdentityTransform);
- return captureArgs;
-}
-
-static jint nativeCaptureDisplay(JNIEnv* env, jclass clazz, jobject displayCaptureArgsObject,
- jlong screenCaptureListenerObject) {
- const DisplayCaptureArgs captureArgs =
- displayCaptureArgsFromObject(env, displayCaptureArgsObject);
-
- if (captureArgs.displayToken == nullptr) {
- return BAD_VALUE;
- }
-
- sp<gui::IScreenCaptureListener> captureListener =
- reinterpret_cast<gui::IScreenCaptureListener*>(screenCaptureListenerObject);
- return ScreenshotClient::captureDisplay(captureArgs, captureListener);
-}
-
static jint nativeCaptureLayers(JNIEnv* env, jclass clazz, jobject layerCaptureArgsObject,
jlong screenCaptureListenerObject) {
LayerCaptureArgs captureArgs;
@@ -283,8 +243,6 @@
static const JNINativeMethod sScreenCaptureMethods[] = {
// clang-format off
- {"nativeCaptureDisplay", "(Landroid/window/ScreenCapture$DisplayCaptureArgs;J)I",
- (void*)nativeCaptureDisplay },
{"nativeCaptureLayers", "(Landroid/window/ScreenCapture$LayerCaptureArgs;J)I",
(void*)nativeCaptureLayers },
{"nativeCreateScreenCaptureListener", "(Ljava/util/function/ObjIntConsumer;)J",
@@ -317,17 +275,6 @@
gCaptureArgsClassInfo.hintForSeamlessTransition =
GetFieldIDOrDie(env, captureArgsClazz, "mHintForSeamlessTransition", "Z");
- jclass displayCaptureArgsClazz =
- FindClassOrDie(env, "android/window/ScreenCapture$DisplayCaptureArgs");
- gDisplayCaptureArgsClassInfo.displayToken =
- GetFieldIDOrDie(env, displayCaptureArgsClazz, "mDisplayToken", "Landroid/os/IBinder;");
- gDisplayCaptureArgsClassInfo.width =
- GetFieldIDOrDie(env, displayCaptureArgsClazz, "mWidth", "I");
- gDisplayCaptureArgsClassInfo.height =
- GetFieldIDOrDie(env, displayCaptureArgsClazz, "mHeight", "I");
- gDisplayCaptureArgsClassInfo.useIdentityTransform =
- GetFieldIDOrDie(env, displayCaptureArgsClazz, "mUseIdentityTransform", "Z");
-
jclass layerCaptureArgsClazz =
FindClassOrDie(env, "android/window/ScreenCapture$LayerCaptureArgs");
gLayerCaptureArgsClassInfo.layer =
diff --git a/core/proto/android/content/package_item_info.proto b/core/proto/android/content/package_item_info.proto
index 279a5d0..b9905e8 100644
--- a/core/proto/android/content/package_item_info.proto
+++ b/core/proto/android/content/package_item_info.proto
@@ -30,6 +30,7 @@
optional string non_localized_label = 4;
optional int32 icon = 5;
optional int32 banner = 6;
+ optional bool is_archived = 7;
}
// Proto of android.content.pm.ApplicationInfo which extends PackageItemInfo
diff --git a/core/proto/android/inputmethodservice/softinputwindow.proto b/core/proto/android/inputmethodservice/softinputwindow.proto
index e0ba6bf..32d14f0 100644
--- a/core/proto/android/inputmethodservice/softinputwindow.proto
+++ b/core/proto/android/inputmethodservice/softinputwindow.proto
@@ -27,6 +27,6 @@
reserved 2; // window_type
reserved 3; // gravity
reserved 4; // takes_focus
- optional .android.graphics.RectProto bounds = 5;
+ reserved 5; // bounds
optional int32 window_state = 6;
}
\ No newline at end of file
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index d09bf44..2e2ec5b 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2814,7 +2814,7 @@
<!-- Base "handwriting slop" value used by ViewConfiguration as a
movement threshold where stylus handwriting should begin. -->
- <dimen name="config_viewConfigurationHandwritingSlop">4dp</dimen>
+ <dimen name="config_viewConfigurationHandwritingSlop">2dp</dimen>
<!-- Base "hover slop" value used by ViewConfiguration as a
movement threshold under which hover is considered "stationary". -->
@@ -5451,6 +5451,10 @@
to enroll the other eligible biometric. -->
<fraction name="config_biometricNotificationFrrThreshold">25%</fraction>
+ <!-- Whether to enable the biometric notification for dual-modality device that enrolled a
+ single biometric and experiences high FRR. -->
+ <bool name="config_biometricFrrNotificationEnabled">false</bool>
+
<!-- The component name for the default profile supervisor, which can be set as a profile owner
even after user setup is complete. The defined component should be used for supervision purposes
only. The component must be part of a system app. -->
diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml
index 5821537..9bf3ce4 100644
--- a/core/res/res/values/config_telephony.xml
+++ b/core/res/res/values/config_telephony.xml
@@ -225,8 +225,4 @@
<bool name="telephony_analytics_switch">true</bool>
<java-symbol type="bool" name="telephony_analytics_switch" />
- <!-- Whether to enable modem on boot if behavior is not defined -->
- <bool name="config_enable_cellular_on_boot_default">true</bool>
- <java-symbol type="bool" name="config_enable_cellular_on_boot_default" />
-
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 7f1a6f9..fe11449 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2604,6 +2604,7 @@
<!-- Biometric FRR config -->
<java-symbol type="fraction" name="config_biometricNotificationFrrThreshold" />
+ <java-symbol type="bool" name="config_biometricFrrNotificationEnabled" />
<!-- Biometric FRR notification messages -->
<java-symbol type="string" name="device_unlock_notification_name" />
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 7f56eb7..9cde296 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -61,11 +61,11 @@
"testng",
"servicestests-utils",
"device-time-shell-utils",
+ "testables",
],
libs: [
"android.test.runner",
- "testables",
"org.apache.http.legacy",
"android.test.base",
"android.test.mock",
diff --git a/core/tests/coretests/src/android/content/BroadcastReceiverTests.java b/core/tests/coretests/src/android/content/BroadcastReceiverTests.java
index 5dbeac2..407c6c3 100644
--- a/core/tests/coretests/src/android/content/BroadcastReceiverTests.java
+++ b/core/tests/coretests/src/android/content/BroadcastReceiverTests.java
@@ -26,6 +26,9 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.ArrayList;
+import java.util.List;
+
@RunWith(AndroidJUnit4.class)
@SmallTest
public class BroadcastReceiverTests {
@@ -47,15 +50,22 @@
@Test
public void testReceiverLimit() {
final IntentFilter mockFilter = new IntentFilter("android.content.tests.TestAction");
+ final List<EmptyReceiver> receivers = new ArrayList<>(RECEIVER_LIMIT_PER_APP);
try {
for (int i = 0; i < RECEIVER_LIMIT_PER_APP + 1; i++) {
- mContext.registerReceiver(new EmptyReceiver(), mockFilter,
+ final EmptyReceiver receiver = new EmptyReceiver();
+ mContext.registerReceiver(receiver, mockFilter,
Context.RECEIVER_EXPORTED_UNAUDITED);
+ receivers.add(receiver);
}
fail("No exception thrown when registering "
+ (RECEIVER_LIMIT_PER_APP + 1) + " receivers");
} catch (IllegalStateException ise) {
// Expected
+ } finally {
+ for (int i = receivers.size() - 1; i >= 0; i--) {
+ mContext.unregisterReceiver(receivers.remove(i));
+ }
}
}
}
diff --git a/core/tests/coretests/src/android/provider/DeviceConfigTest.java b/core/tests/coretests/src/android/provider/DeviceConfigTest.java
index 0e1a6b7..7c4136d 100644
--- a/core/tests/coretests/src/android/provider/DeviceConfigTest.java
+++ b/core/tests/coretests/src/android/provider/DeviceConfigTest.java
@@ -35,6 +35,7 @@
import org.junit.After;
import org.junit.Assert;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -881,6 +882,7 @@
}
@Test
+ @Ignore("b/300174188")
public void syncDisabling() throws Exception {
Properties properties1 = new Properties.Builder(NAMESPACE)
.setString(KEY, VALUE)
diff --git a/core/tests/coretests/src/android/service/TEST_MAPPING b/core/tests/coretests/src/android/service/TEST_MAPPING
index fbf8a92..7ebda00 100644
--- a/core/tests/coretests/src/android/service/TEST_MAPPING
+++ b/core/tests/coretests/src/android/service/TEST_MAPPING
@@ -1,5 +1,5 @@
{
- "postsubmit": [
+ "presubmit": [
{
"name": "FrameworksCoreTests",
"options": [
diff --git a/core/tests/coretests/src/android/service/quicksettings/OWNERS b/core/tests/coretests/src/android/service/quicksettings/OWNERS
new file mode 100644
index 0000000..5665490
--- /dev/null
+++ b/core/tests/coretests/src/android/service/quicksettings/OWNERS
@@ -0,0 +1 @@
+include platform/frameworks/base:/core/java/android/service/quicksettings/OWNERS
diff --git a/core/tests/coretests/src/android/service/quicksettings/TileServiceTest.java b/core/tests/coretests/src/android/service/quicksettings/TileServiceTest.java
index d28eeff..04af5d7 100644
--- a/core/tests/coretests/src/android/service/quicksettings/TileServiceTest.java
+++ b/core/tests/coretests/src/android/service/quicksettings/TileServiceTest.java
@@ -28,10 +28,10 @@
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
+import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
@@ -40,13 +40,15 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class TileServiceTest {
@Mock
private IQSService.Stub mIQSService;
+ private TestableLooper mTestableLooper;
+
private IBinder mTileToken;
private TileService mTileService;
private Tile mTile;
@@ -55,6 +57,8 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
+ mTestableLooper = TestableLooper.get(this);
+
mTileToken = new Binder();
when(mIQSService.asBinder()).thenCallRealMethod();
when(mIQSService.queryLocalInterface(anyString())).thenReturn(mIQSService);
@@ -72,6 +76,7 @@
when(mIQSService.getTile(mTileToken)).thenThrow(new RemoteException());
IBinder result = mTileService.onBind(intent);
+ mTestableLooper.processAllMessages();
assertNull(result);
}
@@ -83,6 +88,7 @@
when(mIQSService.getTile(mTileToken)).thenReturn(null);
IBinder result = mTileService.onBind(intent);
+ mTestableLooper.processAllMessages();
assertNotNull(result);
verify(mIQSService, never()).onStartSuccessful(any());
@@ -96,6 +102,7 @@
when(mIQSService.getTile(mTileToken)).thenReturn(mTile);
IBinder result = mTileService.onBind(intent);
+ mTestableLooper.processAllMessages();
assertNotNull(result);
verify(mIQSService).onStartSuccessful(mTileToken);
diff --git a/core/tests/coretests/src/android/util/PatternsTest.java b/core/tests/coretests/src/android/util/PatternsTest.java
index 6cea2f3..dd8f73f 100644
--- a/core/tests/coretests/src/android/util/PatternsTest.java
+++ b/core/tests/coretests/src/android/util/PatternsTest.java
@@ -399,7 +399,7 @@
@SmallTest
public void testAutoLinkWebUrl_doesNotMatchUrlsWithoutProtocolAndWithUnknownTld()
throws Exception {
- String url = "thank.you";
+ String url = "thank.unknowntld";
assertFalse("Should not match URL that does not start with a protocol and " +
"does not contain a known TLD",
Patterns.AUTOLINK_WEB_URL.matcher(url).matches());
@@ -422,7 +422,7 @@
@SmallTest
public void testAutoLinkWebUrl_doesNotMatchUrlsWithEmojiWithoutProtocolAndWithoutKnownTld()
throws Exception {
- String url = "Thank\u263A.you";
+ String url = "Thank\u263A.unknowntld";
assertFalse("Should not match URLs containing emoji and with unknown TLD",
Patterns.AUTOLINK_WEB_URL.matcher(url).matches());
}
diff --git a/core/tests/coretests/src/android/view/ViewGroupGetChildLocalHitRegionTest.java b/core/tests/coretests/src/android/view/ViewGroupGetChildLocalHitRegionTest.java
index 60a0a2a..c210fd6 100644
--- a/core/tests/coretests/src/android/view/ViewGroupGetChildLocalHitRegionTest.java
+++ b/core/tests/coretests/src/android/view/ViewGroupGetChildLocalHitRegionTest.java
@@ -90,22 +90,73 @@
assertGetChildLocalHitRegionEmpty(R.id.view_cover_top, R.id.view_cover_bottom);
}
+ @Test
+ public void testGetChildLocalHitRegion_topViewIsNotBlockedByBottomView() {
+ // In this case, two views overlap with each other and the MotionEvent is injected to the
+ // bottom view. It verifies that the hit region of the top view won't be blocked by the
+ // bottom view.
+ testGetChildLocalHitRegion_topViewIsNotBlockedByBottomView(/* isHover= */ true);
+ testGetChildLocalHitRegion_topViewIsNotBlockedByBottomView(/* isHover= */ false);
+ }
+
+ private void testGetChildLocalHitRegion_topViewIsNotBlockedByBottomView(boolean isHover) {
+ // In this case, two views overlap with each other and the MotionEvent is injected to the
+ // bottom view. It verifies that the hit region of the top view won't be blocked by the
+ // bottom view.
+ mScenarioRule.getScenario().onActivity(activity -> {
+ View viewTop = activity.findViewById(R.id.view_overlap_top);
+ View viewBottom = activity.findViewById(R.id.view_overlap_bottom);
+
+ // The viewTop covers the left side of the viewBottom. To avoid the MotionEvent gets
+ // blocked by viewTop, we inject MotionEvents into viewBottom's right bottom corner.
+ float x = viewBottom.getWidth() - 1;
+ float y = viewBottom.getHeight() - 1;
+ injectMotionEvent(viewBottom, x, y, isHover);
+
+ Matrix actualMatrix = new Matrix();
+ Region actualRegion = new Region(0, 0, viewTop.getWidth(), viewTop.getHeight());
+ boolean actualNotEmpty = viewTop.getParent()
+ .getChildLocalHitRegion(viewTop, actualRegion, actualMatrix, isHover);
+
+ int[] windowLocation = new int[2];
+ viewTop.getLocationInWindow(windowLocation);
+ Matrix expectMatrix = new Matrix();
+ expectMatrix.preTranslate(-windowLocation[0], -windowLocation[1]);
+ // Though viewTop and viewBottom overlaps, viewTop's hit region won't be blocked by
+ // viewBottom.
+ Region expectRegion = new Region(0, 0, viewTop.getWidth(), viewTop.getHeight());
+
+ assertThat(actualNotEmpty).isTrue();
+ assertThat(actualMatrix).isEqualTo(expectMatrix);
+ assertThat(actualRegion).isEqualTo(expectRegion);
+ });
+ }
+
private void injectMotionEvent(View view, boolean isHover) {
+ float x = view.getWidth() / 2f;
+ float y = view.getHeight() / 2f;
+ injectMotionEvent(view, x, y, isHover);
+ }
+
+ /**
+ * Inject MotionEvent into the given view, at the given location specified in the view's
+ * coordinates.
+ */
+ private void injectMotionEvent(View view, float x, float y, boolean isHover) {
int[] location = new int[2];
view.getLocationInWindow(location);
- float x = location[0] + view.getWidth() / 2f;
- float y = location[1] + view.getHeight() / 2f;
+ float globalX = location[0] + x;
+ float globalY = location[1] + y;
int action = isHover ? MotionEvent.ACTION_HOVER_ENTER : MotionEvent.ACTION_DOWN;
MotionEvent motionEvent = MotionEvent.obtain(/* downtime= */ 0, /* eventTime= */ 0, action,
- x, y, /* pressure= */ 0, /* size= */ 0, /* metaState= */ 0,
+ globalX, globalY, /* pressure= */ 0, /* size= */ 0, /* metaState= */ 0,
/* xPrecision= */ 1, /* yPrecision= */ 1, /* deviceId= */0, /* edgeFlags= */0);
View rootView = view.getRootView();
rootView.dispatchPointerEvent(motionEvent);
}
-
private void assertGetChildLocalHitRegion(int viewId) {
assertGetChildLocalHitRegion(viewId, /* isHover= */ true);
assertGetChildLocalHitRegion(viewId, /* isHover= */ false);
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserListAdapterTest.kt b/core/tests/coretests/src/com/android/internal/app/ChooserListAdapterTest.kt
index 2eeaf53..1c9f04a 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserListAdapterTest.kt
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserListAdapterTest.kt
@@ -19,8 +19,12 @@
import android.content.ComponentName
import android.content.Context
import android.content.Intent
+import android.content.pm.ActivityInfo
+import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.content.pm.ResolveInfo
+import android.content.pm.ShortcutInfo
+import android.graphics.drawable.Icon
import android.os.Bundle
import android.os.UserHandle
import android.service.chooser.ChooserTarget
@@ -32,12 +36,15 @@
import androidx.test.platform.app.InstrumentationRegistry
import com.android.internal.R
import com.android.internal.app.ChooserListAdapter.LoadDirectShareIconTask
+import com.android.internal.app.chooser.DisplayResolveInfo
import com.android.internal.app.chooser.SelectableTargetInfo
import com.android.internal.app.chooser.SelectableTargetInfo.SelectableTargetInfoCommunicator
import com.android.internal.app.chooser.TargetInfo
import com.android.server.testutils.any
import com.android.server.testutils.mock
import com.android.server.testutils.whenever
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.anyInt
@@ -46,22 +53,25 @@
@RunWith(AndroidJUnit4::class)
class ChooserListAdapterTest {
- private val packageManager = mock<PackageManager> {
- whenever(resolveActivity(any(), anyInt())).thenReturn(mock())
- }
+ private val packageManager =
+ mock<PackageManager> { whenever(resolveActivity(any(), anyInt())).thenReturn(mock()) }
private val context = InstrumentationRegistry.getInstrumentation().getContext()
private val resolverListController = mock<ResolverListController>()
- private val chooserListCommunicator = mock<ChooserListAdapter.ChooserListCommunicator> {
- whenever(maxRankedTargets).thenReturn(0)
- }
- private val selectableTargetInfoCommunicator =
- mock<SelectableTargetInfoCommunicator> {
- whenever(targetIntent).thenReturn(mock())
+ private val chooserListCommunicator =
+ mock<ChooserListAdapter.ChooserListCommunicator> {
+ whenever(maxRankedTargets).thenReturn(0)
}
+ private val selectableTargetInfoCommunicator =
+ mock<SelectableTargetInfoCommunicator> { whenever(targetIntent).thenReturn(mock()) }
private val chooserActivityLogger = mock<ChooserActivityLogger>()
+ @Before
+ fun setUp() {
+ whenever(resolverListController.userHandle).thenReturn(UserHandle.CURRENT)
+ }
+
private fun createChooserListAdapter(
- taskProvider: (SelectableTargetInfo?) -> LoadDirectShareIconTask
+ taskProvider: (SelectableTargetInfo?) -> LoadDirectShareIconTask = createTaskProvider()
) =
ChooserListAdapterOverride(
context,
@@ -98,9 +108,8 @@
view.tag = viewHolderOne
val targetInfo = createSelectableTargetInfo()
val iconTaskOne = mock<LoadDirectShareIconTask>()
- val testTaskProvider = mock<() -> LoadDirectShareIconTask> {
- whenever(invoke()).thenReturn(iconTaskOne)
- }
+ val testTaskProvider =
+ mock<() -> LoadDirectShareIconTask> { whenever(invoke()).thenReturn(iconTaskOne) }
val testSubject = createChooserListAdapter { testTaskProvider.invoke() }
testSubject.testViewBind(view, targetInfo, 0)
@@ -114,6 +123,65 @@
verify(testTaskProvider, times(1)).invoke()
}
+ @Test
+ fun getServiceTargetCount_shouldNotShowServiceTargets_returnsZero() {
+ whenever(chooserListCommunicator.shouldShowServiceTargets()).thenReturn(false)
+ val adapter = createChooserListAdapter()
+ whenever(chooserListCommunicator.maxRankedTargets).thenReturn(10)
+ addServiceTargets(adapter, targetCount = 50)
+
+ assertThat(adapter.serviceTargetCount).isEqualTo(0)
+ }
+
+ private fun createTaskProvider(): (SelectableTargetInfo?) -> LoadDirectShareIconTask {
+ val iconTaskOne = mock<LoadDirectShareIconTask>()
+ val testTaskProvider =
+ mock<() -> LoadDirectShareIconTask> { whenever(invoke()).thenReturn(iconTaskOne) }
+ return { testTaskProvider.invoke() }
+ }
+
+ private fun addServiceTargets(adapter: ChooserListAdapter, targetCount: Int) {
+ val origTarget =
+ DisplayResolveInfo(
+ Intent(),
+ createResolveInfo(),
+ Intent(),
+ ResolverListAdapter.ResolveInfoPresentationGetter(context, 200, createResolveInfo())
+ )
+ val targets = mutableListOf<ChooserTarget>()
+ for (i in 1..targetCount) {
+ val score = 1f
+ val componentName = ComponentName("chooser.list.adapter", "Test$i")
+ val extras = Bundle()
+ val icon: Icon? = null
+ targets += ChooserTarget("Title $i", icon, score, componentName, extras)
+ }
+ val directShareToShortcutInfos = mapOf<ChooserTarget, ShortcutInfo>()
+ adapter.addServiceResults(
+ origTarget,
+ targets,
+ ChooserActivity.TARGET_TYPE_DEFAULT,
+ directShareToShortcutInfos
+ )
+ }
+
+ private fun createResolveInfo(): ResolveInfo {
+ val applicationInfo =
+ ApplicationInfo().apply {
+ packageName = "chooser.list.adapter"
+ name = "ChooserListAdapterTestApplication"
+ }
+ val activityInfo =
+ ActivityInfo().apply {
+ packageName = applicationInfo.packageName
+ name = "ChooserListAdapterTest"
+ }
+ activityInfo.applicationInfo = applicationInfo
+ val resolveInfo = ResolveInfo()
+ resolveInfo.activityInfo = activityInfo
+ return resolveInfo
+ }
+
private fun createSelectableTargetInfo(): SelectableTargetInfo =
SelectableTargetInfo(
context,
@@ -125,13 +193,7 @@
)
private fun createChooserTarget(): ChooserTarget =
- ChooserTarget(
- "Title",
- null,
- 1f,
- ComponentName("package", "package.Class"),
- Bundle()
- )
+ ChooserTarget("Title", null, 1f, ComponentName("package", "package.Class"), Bundle())
private fun createView(): View {
val view = FrameLayout(context)
@@ -164,23 +226,23 @@
chooserActivityLogger: ChooserActivityLogger?,
initialIntentsUserHandle: UserHandle?,
private val taskProvider: (SelectableTargetInfo?) -> LoadDirectShareIconTask
-) : ChooserListAdapter(
- context,
- payloadIntents,
- initialIntents,
- rList,
- filterLastUsed,
- resolverListController,
- chooserListCommunicator,
- selectableTargetInfoCommunicator,
- packageManager,
- chooserActivityLogger,
- initialIntentsUserHandle,
-) {
+) :
+ ChooserListAdapter(
+ context,
+ payloadIntents,
+ initialIntents,
+ rList,
+ filterLastUsed,
+ resolverListController,
+ chooserListCommunicator,
+ selectableTargetInfoCommunicator,
+ packageManager,
+ chooserActivityLogger,
+ initialIntentsUserHandle,
+ ) {
override fun createLoadDirectShareIconTask(
info: SelectableTargetInfo?
- ): LoadDirectShareIconTask =
- taskProvider.invoke(info)
+ ): LoadDirectShareIconTask = taskProvider.invoke(info)
fun testViewBind(view: View?, info: TargetInfo?, position: Int) {
onBindView(view, info, position)
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryInputSuspendTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryInputSuspendTest.java
index e870d60..8d825e4 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryInputSuspendTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryInputSuspendTest.java
@@ -99,6 +99,7 @@
if (isCharging(intent) == mExpectedChargingState) {
mReady.open();
}
+ context.unregisterReceiver(this);
}
}, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
}
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index ee2eacf..e46ba9a 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -253,12 +253,6 @@
"group": "WM_DEBUG_BOOT",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "-1883484959": {
- "message": "Content Recording: Display %d state is now (%d), so update recording?",
- "level": "VERBOSE",
- "group": "WM_DEBUG_CONTENT_RECORDING",
- "at": "com\/android\/server\/wm\/DisplayContent.java"
- },
"-1872288685": {
"message": "applyAnimation: anim=%s nextAppTransition=%s transit=%s isEntrance=%b Callers=%s",
"level": "VERBOSE",
@@ -445,6 +439,12 @@
"group": "WM_DEBUG_TASKS",
"at": "com\/android\/server\/wm\/ResetTargetTaskHelper.java"
},
+ "-1700778361": {
+ "message": "Content Recording: Going ahead with updating recording for display %d to new bounds %s and\/or orientation %d and\/or surface size %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_CONTENT_RECORDING",
+ "at": "com\/android\/server\/wm\/ContentRecorder.java"
+ },
"-1699018375": {
"message": "Adding activity %s to task %s callers: %s",
"level": "INFO",
@@ -649,12 +649,6 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/DisplayContent.java"
},
- "-1480264178": {
- "message": "Content Recording: Unable to update recording for display %d to new bounds %s and\/or orientation %d, since the surface is not available.",
- "level": "VERBOSE",
- "group": "WM_DEBUG_CONTENT_RECORDING",
- "at": "com\/android\/server\/wm\/ContentRecorder.java"
- },
"-1478175541": {
"message": "No longer animating wallpaper targets!",
"level": "VERBOSE",
@@ -1855,12 +1849,6 @@
"group": "WM_DEBUG_ADD_REMOVE",
"at": "com\/android\/server\/wm\/Task.java"
},
- "-452750194": {
- "message": "Content Recording: Going ahead with updating recording for display %d to new bounds %s and\/or orientation %d.",
- "level": "VERBOSE",
- "group": "WM_DEBUG_CONTENT_RECORDING",
- "at": "com\/android\/server\/wm\/ContentRecorder.java"
- },
"-451552570": {
"message": "Current focused window being animated by recents. Overriding back callback to recents controller callback.",
"level": "DEBUG",
@@ -2071,12 +2059,6 @@
"group": "WM_DEBUG_WINDOW_TRANSITIONS_MIN",
"at": "com\/android\/server\/wm\/TransitionController.java"
},
- "-262984451": {
- "message": "Relaunch failed %s",
- "level": "INFO",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/ActivityRecord.java"
- },
"-251259736": {
"message": "No longer freezing: %s",
"level": "VERBOSE",
@@ -2377,6 +2359,12 @@
"group": "WM_DEBUG_RECENTS_ANIMATIONS",
"at": "com\/android\/server\/wm\/RecentsAnimationController.java"
},
+ "34106798": {
+ "message": "Content Recording: Display %d state was (%d), is now (%d), so update recording?",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_CONTENT_RECORDING",
+ "at": "com\/android\/server\/wm\/DisplayContent.java"
+ },
"34682671": {
"message": "Not moving display (displayId=%d) to top. Top focused displayId=%d. Reason: FLAG_STEAL_TOP_FOCUS_DISABLED",
"level": "INFO",
@@ -2413,6 +2401,12 @@
"group": "WM_DEBUG_TASKS",
"at": "com\/android\/server\/wm\/RootWindowContainer.java"
},
+ "61363198": {
+ "message": "Auto-PIP allowed, requesting PIP mode via requestStartTransition(): %s, willAutoPip: %b",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
"74885950": {
"message": "Waiting for top state to be released by %s",
"level": "VERBOSE",
@@ -2425,12 +2419,6 @@
"group": "WM_DEBUG_RECENTS_ANIMATIONS",
"at": "com\/android\/server\/wm\/RecentsAnimationController.java"
},
- "90764070": {
- "message": "Could not report token removal to the window token client.",
- "level": "WARN",
- "group": "WM_ERROR",
- "at": "com\/android\/server\/wm\/WindowContextListenerController.java"
- },
"95216706": {
"message": "hideIme target: %s ",
"level": "DEBUG",
@@ -3079,12 +3067,6 @@
"group": "WM_DEBUG_REMOTE_ANIMATIONS",
"at": "com\/android\/server\/wm\/RemoteAnimationController.java"
},
- "643263584": {
- "message": "Content Recording: Apply transformations of shift %d x %d, scale %f, crop %d x %d for display %d",
- "level": "VERBOSE",
- "group": "WM_DEBUG_CONTENT_RECORDING",
- "at": "com\/android\/server\/wm\/ContentRecorder.java"
- },
"644675193": {
"message": "Real start recents",
"level": "DEBUG",
@@ -4105,6 +4087,12 @@
"group": "WM_DEBUG_CONFIGURATION",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "1687944543": {
+ "message": "Content Recording: Unable to update recording for display %d to new bounds %s and\/or orientation %d and\/or surface size %s, since the surface is not available.",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_CONTENT_RECORDING",
+ "at": "com\/android\/server\/wm\/ContentRecorder.java"
+ },
"1699269281": {
"message": "Don't organize or trigger events for untrusted displayId=%d",
"level": "WARN",
@@ -4333,6 +4321,12 @@
"group": "WM_DEBUG_REMOTE_ANIMATIONS",
"at": "com\/android\/server\/wm\/RemoteAnimationController.java"
},
+ "1936800105": {
+ "message": "Content Recording: Apply transformations of shift %d x %d, scale %f, crop (aka recorded content size) %d x %d for display %d; display has size %d x %d; surface has size %d x %d",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_CONTENT_RECORDING",
+ "at": "com\/android\/server\/wm\/ContentRecorder.java"
+ },
"1945495497": {
"message": "Focused window didn't have a valid surface drawn.",
"level": "DEBUG",
@@ -4351,12 +4345,6 @@
"group": "WM_DEBUG_TASKS",
"at": "com\/android\/server\/wm\/RootWindowContainer.java"
},
- "1948483534": {
- "message": "Could not report config changes to the window token client.",
- "level": "WARN",
- "group": "WM_ERROR",
- "at": "com\/android\/server\/wm\/WindowContextListenerController.java"
- },
"1964565370": {
"message": "Starting remote animation",
"level": "INFO",
diff --git a/keystore/java/android/security/AndroidKeyStoreMaintenance.java b/keystore/java/android/security/AndroidKeyStoreMaintenance.java
index 919a93b..0f3488b 100644
--- a/keystore/java/android/security/AndroidKeyStoreMaintenance.java
+++ b/keystore/java/android/security/AndroidKeyStoreMaintenance.java
@@ -20,6 +20,7 @@
import android.annotation.Nullable;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
+import android.os.StrictMode;
import android.security.maintenance.IKeystoreMaintenance;
import android.system.keystore2.Domain;
import android.system.keystore2.KeyDescriptor;
@@ -51,6 +52,7 @@
* @hide
*/
public static int onUserAdded(@NonNull int userId) {
+ StrictMode.noteDiskWrite();
try {
getService().onUserAdded(userId);
return 0;
@@ -71,6 +73,7 @@
* @hide
*/
public static int onUserRemoved(int userId) {
+ StrictMode.noteDiskWrite();
try {
getService().onUserRemoved(userId);
return 0;
@@ -93,6 +96,7 @@
* @hide
*/
public static int onUserPasswordChanged(int userId, @Nullable byte[] password) {
+ StrictMode.noteDiskWrite();
try {
getService().onUserPasswordChanged(userId, password);
return 0;
@@ -110,6 +114,7 @@
* be cleared.
*/
public static int clearNamespace(@Domain int domain, long namespace) {
+ StrictMode.noteDiskWrite();
try {
getService().clearNamespace(domain, namespace);
return 0;
@@ -129,6 +134,7 @@
* @return UserState enum variant as integer if successful or an error
*/
public static int getState(int userId) {
+ StrictMode.noteDiskRead();
try {
return getService().getState(userId);
} catch (ServiceSpecificException e) {
@@ -144,6 +150,7 @@
* Informs Keystore 2.0 that an off body event was detected.
*/
public static void onDeviceOffBody() {
+ StrictMode.noteDiskWrite();
try {
getService().onDeviceOffBody();
} catch (Exception e) {
@@ -172,6 +179,7 @@
* * SYSTEM_ERROR if an unexpected error occurred.
*/
public static int migrateKeyNamespace(KeyDescriptor source, KeyDescriptor destination) {
+ StrictMode.noteDiskWrite();
try {
getService().migrateKeyNamespace(source, destination);
return 0;
diff --git a/keystore/java/android/security/Authorization.java b/keystore/java/android/security/Authorization.java
index 00219e7..2d2dd24 100644
--- a/keystore/java/android/security/Authorization.java
+++ b/keystore/java/android/security/Authorization.java
@@ -22,6 +22,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
+import android.os.StrictMode;
import android.security.authorization.IKeystoreAuthorization;
import android.security.authorization.LockScreenEvent;
import android.system.keystore2.ResponseCode;
@@ -48,6 +49,7 @@
* @return 0 if successful or {@code ResponseCode.SYSTEM_ERROR}.
*/
public static int addAuthToken(@NonNull HardwareAuthToken authToken) {
+ StrictMode.noteSlowCall("addAuthToken");
try {
getService().addAuthToken(authToken);
return 0;
@@ -81,6 +83,7 @@
*/
public static int onLockScreenEvent(@NonNull boolean locked, @NonNull int userId,
@Nullable byte[] syntheticPassword, @Nullable long[] unlockingSids) {
+ StrictMode.noteDiskWrite();
try {
if (locked) {
getService().onLockScreenEvent(LockScreenEvent.LOCK, userId, null, unlockingSids);
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 8811a7f..8045f55 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -18,6 +18,7 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
+import android.os.StrictMode;
import android.os.UserHandle;
import android.security.maintenance.UserState;
@@ -126,6 +127,8 @@
* a {@code KeymasterDefs.KM_ERROR_} value or {@code KeyStore} ResponseCode.
*/
public int addAuthToken(byte[] authToken) {
+ StrictMode.noteDiskWrite();
+
return Authorization.addAuthToken(authToken);
}
diff --git a/keystore/java/android/security/KeyStore2.java b/keystore/java/android/security/KeyStore2.java
index c83f298..5e16bce 100644
--- a/keystore/java/android/security/KeyStore2.java
+++ b/keystore/java/android/security/KeyStore2.java
@@ -23,6 +23,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
+import android.os.StrictMode;
import android.security.keymaster.KeymasterDefs;
import android.system.keystore2.Domain;
import android.system.keystore2.IKeystoreService;
@@ -148,6 +149,8 @@
}
void delete(KeyDescriptor descriptor) throws KeyStoreException {
+ StrictMode.noteDiskWrite();
+
handleRemoteExceptionWithRetry((service) -> {
service.deleteKey(descriptor);
return 0;
@@ -158,6 +161,8 @@
* List all entries in the keystore for in the given namespace.
*/
public KeyDescriptor[] list(int domain, long namespace) throws KeyStoreException {
+ StrictMode.noteDiskRead();
+
return handleRemoteExceptionWithRetry((service) -> service.listEntries(domain, namespace));
}
@@ -166,6 +171,8 @@
*/
public KeyDescriptor[] listBatch(int domain, long namespace, String startPastAlias)
throws KeyStoreException {
+ StrictMode.noteDiskRead();
+
return handleRemoteExceptionWithRetry(
(service) -> service.listEntriesBatched(domain, namespace, startPastAlias));
}
@@ -228,6 +235,8 @@
*/
public KeyDescriptor grant(KeyDescriptor descriptor, int granteeUid, int accessVector)
throws KeyStoreException {
+ StrictMode.noteDiskWrite();
+
return handleRemoteExceptionWithRetry(
(service) -> service.grant(descriptor, granteeUid, accessVector)
);
@@ -243,6 +252,8 @@
*/
public void ungrant(KeyDescriptor descriptor, int granteeUid)
throws KeyStoreException {
+ StrictMode.noteDiskWrite();
+
handleRemoteExceptionWithRetry((service) -> {
service.ungrant(descriptor, granteeUid);
return 0;
@@ -259,6 +270,8 @@
*/
public KeyEntryResponse getKeyEntry(@NonNull KeyDescriptor descriptor)
throws KeyStoreException {
+ StrictMode.noteDiskRead();
+
return handleRemoteExceptionWithRetry((service) -> service.getKeyEntry(descriptor));
}
@@ -290,6 +303,8 @@
*/
public void updateSubcomponents(@NonNull KeyDescriptor key, byte[] publicCert,
byte[] publicCertChain) throws KeyStoreException {
+ StrictMode.noteDiskWrite();
+
handleRemoteExceptionWithRetry((service) -> {
service.updateSubcomponent(key, publicCert, publicCertChain);
return 0;
@@ -305,6 +320,8 @@
*/
public void deleteKey(@NonNull KeyDescriptor descriptor)
throws KeyStoreException {
+ StrictMode.noteDiskWrite();
+
handleRemoteExceptionWithRetry((service) -> {
service.deleteKey(descriptor);
return 0;
@@ -315,6 +332,8 @@
* Returns the number of Keystore entries for a given domain and namespace.
*/
public int getNumberOfEntries(int domain, long namespace) throws KeyStoreException {
+ StrictMode.noteDiskRead();
+
return handleRemoteExceptionWithRetry((service)
-> service.getNumberOfEntries(domain, namespace));
}
diff --git a/keystore/java/android/security/KeyStoreOperation.java b/keystore/java/android/security/KeyStoreOperation.java
index 737ff2b..7c9b8eb 100644
--- a/keystore/java/android/security/KeyStoreOperation.java
+++ b/keystore/java/android/security/KeyStoreOperation.java
@@ -21,6 +21,7 @@
import android.os.Binder;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
+import android.os.StrictMode;
import android.security.keymaster.KeymasterDefs;
import android.system.keystore2.IKeystoreOperation;
import android.system.keystore2.ResponseCode;
@@ -97,6 +98,7 @@
* @throws KeyStoreException
*/
public void updateAad(@NonNull byte[] input) throws KeyStoreException {
+ StrictMode.noteSlowCall("updateAad");
handleExceptions(() -> {
mOperation.updateAad(input);
return 0;
@@ -112,6 +114,7 @@
* @hide
*/
public byte[] update(@NonNull byte[] input) throws KeyStoreException {
+ StrictMode.noteSlowCall("update");
return handleExceptions(() -> mOperation.update(input));
}
@@ -125,6 +128,7 @@
* @hide
*/
public byte[] finish(byte[] input, byte[] signature) throws KeyStoreException {
+ StrictMode.noteSlowCall("finish");
return handleExceptions(() -> mOperation.finish(input, signature));
}
@@ -135,6 +139,7 @@
* @hide
*/
public void abort() throws KeyStoreException {
+ StrictMode.noteSlowCall("abort");
handleExceptions(() -> {
mOperation.abort();
return 0;
diff --git a/keystore/java/android/security/KeyStoreSecurityLevel.java b/keystore/java/android/security/KeyStoreSecurityLevel.java
index 9c0b46c..6ab148a 100644
--- a/keystore/java/android/security/KeyStoreSecurityLevel.java
+++ b/keystore/java/android/security/KeyStoreSecurityLevel.java
@@ -22,6 +22,7 @@
import android.os.Binder;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
+import android.os.StrictMode;
import android.security.keystore.BackendBusyException;
import android.security.keystore.KeyStoreConnectException;
import android.system.keystore2.AuthenticatorSpec;
@@ -75,6 +76,7 @@
*/
public KeyStoreOperation createOperation(@NonNull KeyDescriptor keyDescriptor,
Collection<KeyParameter> args) throws KeyStoreException {
+ StrictMode.noteDiskWrite();
while (true) {
try {
CreateOperationResponse createOperationResponse =
@@ -142,6 +144,8 @@
public KeyMetadata generateKey(@NonNull KeyDescriptor descriptor, KeyDescriptor attestationKey,
Collection<KeyParameter> args, int flags, byte[] entropy)
throws KeyStoreException {
+ StrictMode.noteDiskWrite();
+
return handleExceptions(() -> mSecurityLevel.generateKey(
descriptor, attestationKey, args.toArray(new KeyParameter[args.size()]),
flags, entropy));
@@ -163,6 +167,8 @@
public KeyMetadata importKey(KeyDescriptor descriptor, KeyDescriptor attestationKey,
Collection<KeyParameter> args, int flags, byte[] keyData)
throws KeyStoreException {
+ StrictMode.noteDiskWrite();
+
return handleExceptions(() -> mSecurityLevel.importKey(descriptor, attestationKey,
args.toArray(new KeyParameter[args.size()]), flags, keyData));
}
@@ -186,6 +192,7 @@
@NonNull byte[] wrappedKey, byte[] maskingKey,
Collection<KeyParameter> args, @NonNull AuthenticatorSpec[] authenticatorSpecs)
throws KeyStoreException {
+ StrictMode.noteDiskWrite();
KeyDescriptor keyDescriptor = new KeyDescriptor();
keyDescriptor.alias = wrappedKeyDescriptor.alias;
keyDescriptor.nspace = wrappedKeyDescriptor.nspace;
diff --git a/keystore/java/android/security/LegacyVpnProfileStore.java b/keystore/java/android/security/LegacyVpnProfileStore.java
index c85b6b1..0cc4dfa 100644
--- a/keystore/java/android/security/LegacyVpnProfileStore.java
+++ b/keystore/java/android/security/LegacyVpnProfileStore.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
+import android.os.StrictMode;
import android.security.legacykeystore.ILegacyKeystore;
import android.util.Log;
@@ -51,6 +52,7 @@
* @hide
*/
public static boolean put(@NonNull String alias, @NonNull byte[] profile) {
+ StrictMode.noteDiskWrite();
try {
getService().put(alias, ILegacyKeystore.UID_SELF, profile);
return true;
@@ -70,6 +72,7 @@
* @hide
*/
public static byte[] get(@NonNull String alias) {
+ StrictMode.noteDiskRead();
try {
return getService().get(alias, ILegacyKeystore.UID_SELF);
} catch (ServiceSpecificException e) {
@@ -89,6 +92,7 @@
* @hide
*/
public static boolean remove(@NonNull String alias) {
+ StrictMode.noteDiskWrite();
try {
getService().remove(alias, ILegacyKeystore.UID_SELF);
return true;
@@ -109,6 +113,7 @@
* @hide
*/
public static @NonNull String[] list(@NonNull String prefix) {
+ StrictMode.noteDiskRead();
try {
final String[] aliases = getService().list(prefix, ILegacyKeystore.UID_SELF);
for (int i = 0; i < aliases.length; ++i) {
diff --git a/keystore/java/android/security/SystemKeyStore.java b/keystore/java/android/security/SystemKeyStore.java
index e07eaa2..d481a07 100644
--- a/keystore/java/android/security/SystemKeyStore.java
+++ b/keystore/java/android/security/SystemKeyStore.java
@@ -18,6 +18,9 @@
import android.os.Environment;
import android.os.FileUtils;
+import android.os.StrictMode;
+
+import libcore.io.IoUtils;
import java.io.File;
import java.io.FileOutputStream;
@@ -28,8 +31,6 @@
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
-import libcore.io.IoUtils;
-
/**
*@hide
*/
@@ -69,6 +70,7 @@
public byte[] generateNewKey(int numBits, String algName, String keyName)
throws NoSuchAlgorithmException {
+ StrictMode.noteDiskWrite();
// Check if key with similar name exists. If so, return null.
File keyFile = getKeyFile(keyName);
@@ -103,6 +105,7 @@
}
private File getKeyFile(String keyName) {
+ StrictMode.noteDiskWrite();
File sysKeystoreDir = new File(Environment.getDataDirectory(),
SYSTEM_KEYSTORE_DIRECTORY);
File keyFile = new File(sysKeystoreDir, keyName + KEY_FILE_EXTENSION);
@@ -114,6 +117,7 @@
}
public byte[] retrieveKey(String keyName) throws IOException {
+ StrictMode.noteDiskRead();
File keyFile = getKeyFile(keyName);
if (!keyFile.exists()) {
return null;
@@ -122,6 +126,7 @@
}
public void deleteKey(String keyName) {
+ StrictMode.noteDiskWrite();
// Get the file first.
File keyFile = getKeyFile(keyName);
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java b/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java
index d129891..9ac0f6d 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.hardware.security.keymint.KeyParameter;
+import android.os.StrictMode;
import android.security.KeyStoreException;
import android.security.KeyStoreOperation;
import android.security.keymaster.KeymasterDefs;
@@ -137,6 +138,7 @@
if (!(key instanceof AndroidKeyStorePrivateKey)
&& (key instanceof PrivateKey || key instanceof PublicKey)) {
try {
+ StrictMode.noteSlowCall("engineInit");
mCipher = Cipher.getInstance(getTransform());
String transform = getTransform();
@@ -203,6 +205,7 @@
if (!(key instanceof AndroidKeyStorePrivateKey)
&& (key instanceof PrivateKey || key instanceof PublicKey)) {
try {
+ StrictMode.noteSlowCall("engineInit");
mCipher = Cipher.getInstance(getTransform());
mCipher.init(opmode, key, params, random);
return;
@@ -233,6 +236,7 @@
if (!(key instanceof AndroidKeyStorePrivateKey)
&& (key instanceof PrivateKey || key instanceof PublicKey)) {
try {
+ StrictMode.noteSlowCall("engineInit");
mCipher = Cipher.getInstance(getTransform());
mCipher.init(opmode, key, params, random);
return;
@@ -346,6 +350,7 @@
parameters.add(KeyStore2ParameterUtils.makeEnum(KeymasterDefs.KM_TAG_PURPOSE, purpose));
try {
+ StrictMode.noteDiskRead();
mOperation = mKey.getSecurityLevel().createOperation(
mKey.getKeyIdDescriptor(),
parameters
@@ -521,6 +526,7 @@
@Override
protected final void engineUpdateAAD(byte[] input, int inputOffset, int inputLen) {
if (mCipher != null) {
+ StrictMode.noteSlowCall("engineUpdateAAD");
mCipher.updateAAD(input, inputOffset, inputLen);
return;
}
@@ -562,6 +568,7 @@
@Override
protected final void engineUpdateAAD(ByteBuffer src) {
if (mCipher != null) {
+ StrictMode.noteSlowCall("engineUpdateAAD");
mCipher.updateAAD(src);
return;
}
@@ -715,6 +722,7 @@
throw new NullPointerException("key == null");
}
byte[] encoded = null;
+ StrictMode.noteSlowCall("engineWrap");
if (key instanceof SecretKey) {
if ("RAW".equalsIgnoreCase(key.getFormat())) {
encoded = key.getEncoded();
@@ -807,6 +815,7 @@
throw new InvalidKeyException("Failed to unwrap key", e);
}
+ StrictMode.noteSlowCall("engineUnwrap");
switch (wrappedKeyType) {
case Cipher.SECRET_KEY:
{
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyAgreementSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyAgreementSpi.java
index 7292cd3..66e9f71 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyAgreementSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyAgreementSpi.java
@@ -20,6 +20,7 @@
import android.hardware.security.keymint.KeyParameter;
import android.hardware.security.keymint.KeyPurpose;
import android.hardware.security.keymint.Tag;
+import android.os.StrictMode;
import android.security.KeyStoreException;
import android.security.KeyStoreOperation;
import android.security.keystore.KeyStoreCryptoOperation;
@@ -174,6 +175,7 @@
}
byte[] otherPartyKeyEncoded = mOtherPartyKey.getEncoded();
+ StrictMode.noteSlowCall("engineGenerateSecret");
try {
return mOperation.finish(otherPartyKeyEncoded, null);
} catch (KeyStoreException e) {
@@ -245,6 +247,7 @@
Tag.PURPOSE, KeyPurpose.AGREE_KEY
));
+ StrictMode.noteDiskWrite();
try {
mOperation =
mKey.getSecurityLevel().createOperation(mKey.getKeyIdDescriptor(), parameters);
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java
index f1681ec..d283b05 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java
@@ -18,6 +18,7 @@
import android.hardware.security.keymint.KeyParameter;
import android.hardware.security.keymint.SecurityLevel;
+import android.os.StrictMode;
import android.security.KeyStore2;
import android.security.KeyStoreSecurityLevel;
import android.security.keymaster.KeymasterDefs;
@@ -281,6 +282,7 @@
@Override
protected SecretKey engineGenerateKey() {
+ StrictMode.noteSlowCall("engineGenerateKey");
KeyGenParameterSpec spec = mSpec;
if (spec == null) {
throw new IllegalStateException("Not initialized");
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
index 474b7ea..1398da3 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -27,6 +27,7 @@
import android.hardware.security.keymint.SecurityLevel;
import android.hardware.security.keymint.Tag;
import android.os.Build;
+import android.os.StrictMode;
import android.security.KeyPairGeneratorSpec;
import android.security.KeyStore2;
import android.security.KeyStoreException;
@@ -617,6 +618,7 @@
@Override
public KeyPair generateKeyPair() {
+ StrictMode.noteSlowCall("generateKeyPair");
if (mKeyStore == null || mSpec == null) {
throw new IllegalStateException("Not initialized");
}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
index b4d8def..273dff1 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
@@ -24,6 +24,7 @@
import android.hardware.security.keymint.HardwareAuthenticatorType;
import android.hardware.security.keymint.KeyParameter;
import android.hardware.security.keymint.SecurityLevel;
+import android.os.StrictMode;
import android.security.GateKeeper;
import android.security.KeyStore2;
import android.security.KeyStoreParameter;
@@ -164,6 +165,7 @@
KeyDescriptor descriptor = makeKeyDescriptor(alias);
try {
+ StrictMode.noteDiskRead();
return mKeyStore.getKeyEntry(descriptor);
} catch (android.security.KeyStoreException e) {
if (e.getErrorCode() != ResponseCode.KEY_NOT_FOUND) {
@@ -447,6 +449,7 @@
assertCanReplace(alias, targetDomain, mNamespace, descriptor);
try {
+ StrictMode.noteDiskWrite();
mKeyStore.updateSubcomponents(
((AndroidKeyStorePrivateKey) key).getKeyIdDescriptor(),
userCertBytes, chainBytes);
@@ -597,6 +600,7 @@
importArgs, flags, pkcs8EncodedPrivateKeyBytes);
try {
+ StrictMode.noteDiskWrite();
mKeyStore.updateSubcomponents(metadata.key, userCertBytes, chainBytes);
} catch (android.security.KeyStoreException e) {
mKeyStore.deleteKey(metadata.key);
@@ -938,6 +942,7 @@
KeyEntryResponse response = null;
try {
+ StrictMode.noteDiskRead();
response = mKeyStore.getKeyEntry(wrappingkey);
} catch (android.security.KeyStoreException e) {
throw new KeyStoreException("Failed to import wrapped key. Keystore error code: "
@@ -994,6 +999,7 @@
}
try {
+ StrictMode.noteDiskWrite();
securityLevel.importWrappedKey(
wrappedKey, wrappingkey,
entry.getWrappedKeyBytes(),
@@ -1054,6 +1060,7 @@
}
try {
+ StrictMode.noteDiskWrite();
mKeyStore.updateSubcomponents(makeKeyDescriptor(alias),
null /* publicCert - unused when used as pure certificate store. */,
encoded);
@@ -1066,6 +1073,7 @@
public void engineDeleteEntry(String alias) throws KeyStoreException {
KeyDescriptor descriptor = makeKeyDescriptor(alias);
try {
+ StrictMode.noteDiskWrite();
mKeyStore.deleteKey(descriptor);
} catch (android.security.KeyStoreException e) {
if (e.getErrorCode() != ResponseCode.KEY_NOT_FOUND) {
@@ -1076,6 +1084,7 @@
private KeyDescriptor[] getAliasesBatch(String startPastAlias) {
try {
+ StrictMode.noteDiskRead();
return mKeyStore.listBatch(
getTargetDomain(),
mNamespace,
@@ -1103,6 +1112,7 @@
@Override
public int engineSize() {
try {
+ StrictMode.noteDiskRead();
return mKeyStore.getNumberOfEntries(
getTargetDomain(),
mNamespace
@@ -1166,6 +1176,7 @@
KeyDescriptor[] keyDescriptors = null;
try {
+ StrictMode.noteDiskRead();
keyDescriptors = mKeyStore.list(
getTargetDomain(),
mNamespace
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index a5ee19e..cdfc4c8 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -879,14 +879,12 @@
// Skip resolving if the activity is on a pinned TaskFragmentContainer.
// TODO(b/243518738): skip resolving for overlay container.
- if (container != null) {
- final TaskContainer taskContainer = container.getTaskContainer();
- if (taskContainer.isTaskFragmentContainerPinned(container)) {
- return true;
- }
+ final TaskContainer taskContainer = container != null ? container.getTaskContainer() : null;
+ if (container != null && taskContainer != null
+ && taskContainer.isTaskFragmentContainerPinned(container)) {
+ return true;
}
- final TaskContainer taskContainer = container != null ? container.getTaskContainer() : null;
if (!isOnReparent && taskContainer != null
&& taskContainer.getTopNonFinishingTaskFragmentContainer(false /* includePin */)
!= container) {
@@ -895,6 +893,28 @@
return true;
}
+ // Ensure the top TaskFragments are updated to the right config if activity is resolved
+ // to a new TaskFragment while pin TF exists.
+ final boolean handled = resolveActivityToContainerByRule(wct, activity, container,
+ isOnReparent);
+ if (handled && taskContainer != null) {
+ final SplitPinContainer splitPinContainer = taskContainer.getSplitPinContainer();
+ if (splitPinContainer != null) {
+ final TaskFragmentContainer resolvedContainer = getContainerWithActivity(activity);
+ if (resolvedContainer != null && resolvedContainer.getRunningActivityCount() <= 1) {
+ updateContainer(wct, splitPinContainer.getSecondaryContainer());
+ }
+ }
+ }
+ return handled;
+ }
+
+ /**
+ * Resolves the activity to a {@link TaskFragmentContainer} according to the Split-rules.
+ */
+ boolean resolveActivityToContainerByRule(@NonNull WindowContainerTransaction wct,
+ @NonNull Activity activity, @Nullable TaskFragmentContainer container,
+ boolean isOnReparent) {
/*
* We will check the following to see if there is any embedding rule matched:
* 1. Whether the new launched activity should always expand.
@@ -1301,6 +1321,26 @@
}
}
+ // Ensure the top TaskFragments are updated to the right config if the intent is resolved
+ // to a new TaskFragment while pin TF exists.
+ final TaskFragmentContainer launchingContainer = resolveStartActivityIntentByRule(wct,
+ taskId, intent, launchingActivity);
+ if (launchingContainer != null && launchingContainer.getRunningActivityCount() == 0) {
+ final SplitPinContainer splitPinContainer =
+ launchingContainer.getTaskContainer().getSplitPinContainer();
+ if (splitPinContainer != null) {
+ updateContainer(wct, splitPinContainer.getSecondaryContainer());
+ }
+ }
+ return launchingContainer;
+ }
+
+ /**
+ * Resolves the intent to a {@link TaskFragmentContainer} according to the Split-rules.
+ */
+ @Nullable
+ TaskFragmentContainer resolveStartActivityIntentByRule(@NonNull WindowContainerTransaction wct,
+ int taskId, @NonNull Intent intent, @Nullable Activity launchingActivity) {
/*
* We will check the following to see if there is any embedding rule matched:
* 1. Whether the new activity intent should always expand.
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml
index d93e9ba..7638132 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml
@@ -26,8 +26,8 @@
<ImageButton
android:id="@+id/caption_handle"
android:layout_width="128dp"
- android:layout_height="42dp"
- android:paddingVertical="19dp"
+ android:layout_height="@dimen/desktop_mode_fullscreen_decor_caption_height"
+ android:paddingVertical="16dp"
android:contentDescription="@string/handle_text"
android:src="@drawable/decor_handle_dark"
tools:tint="@color/desktop_mode_caption_handle_bar_dark"
diff --git a/libs/WindowManager/Shell/res/values-television/config.xml b/libs/WindowManager/Shell/res/values-television/config.xml
index 5f9dbdb..da8abde 100644
--- a/libs/WindowManager/Shell/res/values-television/config.xml
+++ b/libs/WindowManager/Shell/res/values-television/config.xml
@@ -44,6 +44,12 @@
if a custom action is present before closing it. -->
<integer name="config_pipForceCloseDelay">5000</integer>
+ <!-- Animation duration when exit starting window: fade out icon -->
+ <integer name="starting_window_app_reveal_icon_fade_out_duration">500</integer>
+
+ <!-- Animation delay when exit starting window: reveal app -->
+ <integer name="starting_window_app_reveal_anim_delay">0</integer>
+
<!-- Animation duration when exit starting window: reveal app -->
<integer name="starting_window_app_reveal_anim_duration">500</integer>
diff --git a/libs/WindowManager/Shell/res/values-watch/config.xml b/libs/WindowManager/Shell/res/values-watch/config.xml
new file mode 100644
index 0000000..03736ed
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-watch/config.xml
@@ -0,0 +1,35 @@
+<?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.
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for watch products. Do not translate. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Animation duration when exit starting window: fade out icon -->
+ <integer name="starting_window_app_reveal_icon_fade_out_duration">50</integer>
+
+ <!-- Animation delay when exit starting window: reveal app -->
+ <integer name="starting_window_app_reveal_anim_delay">50</integer>
+
+ <!-- Animation duration when exit starting window: reveal app -->
+ <integer name="starting_window_app_reveal_anim_duration">200</integer>
+
+ <!-- Default animation type when hiding the starting window. The possible values are:
+ - 0 for radial vanish + slide up
+ - 1 for fade out -->
+ <integer name="starting_window_exit_animation_type">1</integer>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index cba86c8..7faf380 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -401,6 +401,12 @@
<!-- Height of button (32dp) + 2 * margin (5dp each). -->
<dimen name="freeform_decor_caption_height">42dp</dimen>
+ <!-- Height of desktop mode caption for freeform tasks. -->
+ <dimen name="desktop_mode_freeform_decor_caption_height">42dp</dimen>
+
+ <!-- Height of desktop mode caption for fullscreen tasks. -->
+ <dimen name="desktop_mode_fullscreen_decor_caption_height">36dp</dimen>
+
<!-- The width of the maximize menu in desktop mode. -->
<dimen name="desktop_mode_maximize_menu_width">287dp</dimen>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
index 66e6930..39e3180 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
@@ -257,8 +257,16 @@
return false;
}
- // Badged bubble image
- Drawable bubbleDrawable = iconFactory.getBubbleDrawable(c, info.shortcutInfo, b.getIcon());
+ Drawable bubbleDrawable = null;
+ try {
+ // Badged bubble image
+ bubbleDrawable = iconFactory.getBubbleDrawable(c, info.shortcutInfo,
+ b.getIcon());
+ } catch (Exception e) {
+ // If we can't create the icon we'll default to the app icon
+ Log.w(TAG, "Exception creating icon for the bubble: " + b.getKey());
+ }
+
if (bubbleDrawable == null) {
// Default to app icon
bubbleDrawable = appIcon;
@@ -268,7 +276,7 @@
b.isImportantConversation());
info.badgeBitmap = badgeBitmapInfo.icon;
// Raw badge bitmap never includes the important conversation ring
- info.rawBadgeBitmap = b.isImportantConversation() // is this needed for bar?
+ info.rawBadgeBitmap = b.isImportantConversation()
? iconFactory.getBadgeBitmap(badgedIcon, false).icon
: badgeBitmapInfo.icon;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index fd23d14..9f9854e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -205,6 +205,7 @@
SyncTransactionQueue syncQueue,
Transitions transitions,
Optional<DesktopTasksController> desktopTasksController,
+ RecentsTransitionHandler recentsTransitionHandler,
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
if (DesktopModeStatus.isEnabled()) {
return new DesktopModeWindowDecorViewModel(
@@ -218,6 +219,7 @@
syncQueue,
transitions,
desktopTasksController,
+ recentsTransitionHandler,
rootTaskDisplayAreaOrganizer);
}
return new CaptionWindowDecorViewModel(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 9ce0118..f8dd208 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -17,7 +17,6 @@
package com.android.wm.shell.desktopmode
import android.app.ActivityManager.RunningTaskInfo
-import android.app.WindowConfiguration
import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME
import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
@@ -61,6 +60,7 @@
import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.TO_DESKTOP_INDICATOR
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
import com.android.wm.shell.splitscreen.SplitScreenController
+import com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_ENTER_DESKTOP
import com.android.wm.shell.sysui.ShellCommandHandler
import com.android.wm.shell.sysui.ShellController
import com.android.wm.shell.sysui.ShellInit
@@ -213,6 +213,7 @@
"DesktopTasksController: moveToDesktop taskId=%d",
task.taskId
)
+ exitSplitIfApplicable(wct, task)
// Bring other apps to front first
bringDesktopAppsToFront(task.displayId, wct)
addMoveToDesktopChanges(wct, task)
@@ -240,6 +241,7 @@
taskInfo.taskId
)
val wct = WindowContainerTransaction()
+ exitSplitIfApplicable(wct, taskInfo)
moveHomeTaskToFront(wct)
addMoveToDesktopChanges(wct, taskInfo)
wct.setBounds(taskInfo.token, startBounds)
@@ -319,7 +321,7 @@
task.taskId
)
val wct = WindowContainerTransaction()
- wct.setWindowingMode(task.token, WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW)
+ wct.setWindowingMode(task.token, WINDOWING_MODE_MULTI_WINDOW)
wct.setBounds(task.token, Rect())
wct.setDensityDpi(task.token, getDefaultDensityDpi())
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
@@ -329,6 +331,13 @@
}
}
+ private fun exitSplitIfApplicable(wct: WindowContainerTransaction, taskInfo: RunningTaskInfo) {
+ if (taskInfo.windowingMode == WINDOWING_MODE_MULTI_WINDOW) {
+ splitScreenController.prepareExitSplitScreen(wct,
+ splitScreenController.getStageOfTask(taskInfo.taskId), EXIT_REASON_ENTER_DESKTOP)
+ }
+ }
+
/**
* The second part of the animated move to desktop transition, called after
* {@link startMoveToDesktop}. Move a task to fullscreen after being dragged from fullscreen
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java
index 069066e..2616b8b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java
@@ -34,4 +34,10 @@
default void getRecentTasks(int maxNum, int flags, int userId, Executor callbackExecutor,
Consumer<List<GroupedRecentTaskInfo>> callback) {
}
+
+ /**
+ * Adds the listener to be notified of whether the recent task animation is running.
+ */
+ default void addAnimationStateListener(Executor listenerExecutor, Consumer<Boolean> listener) {
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index 94e1b33..ccc3438 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -428,6 +428,21 @@
executor.execute(() -> callback.accept(tasks));
});
}
+
+ @Override
+ public void addAnimationStateListener(Executor executor, Consumer<Boolean> listener) {
+ mMainExecutor.execute(() -> {
+ if (mTransitionHandler == null) {
+ return;
+ }
+ mTransitionHandler.addTransitionStateListener(new RecentsTransitionStateListener() {
+ @Override
+ public void onAnimationStateChanged(boolean running) {
+ executor.execute(() -> listener.accept(running));
+ }
+ });
+ });
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
index e916a14..ead2f9c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
@@ -76,6 +76,7 @@
private final RecentTasksController mRecentTasksController;
private IApplicationThread mAnimApp = null;
private final ArrayList<RecentsController> mControllers = new ArrayList<>();
+ private final ArrayList<RecentsTransitionStateListener> mStateListeners = new ArrayList<>();
/**
* List of other handlers which might need to mix recents with other things. These are checked
@@ -106,6 +107,11 @@
mMixers.remove(mixer);
}
+ /** Adds the callback for receiving the state change of transition. */
+ public void addTransitionStateListener(RecentsTransitionStateListener listener) {
+ mStateListeners.add(listener);
+ }
+
@VisibleForTesting
public IBinder startRecentsTransition(PendingIntent intent, Intent fillIn, Bundle options,
IApplicationThread appThread, IRecentsAnimationRunner listener) {
@@ -128,6 +134,9 @@
}
final IBinder transition = mTransitions.startTransition(TRANSIT_TO_FRONT, wct,
mixedHandler == null ? this : mixedHandler);
+ for (int i = 0; i < mStateListeners.size(); i++) {
+ mStateListeners.get(i).onTransitionStarted(transition);
+ }
if (mixer != null) {
mixer.setRecentsTransition(transition);
}
@@ -389,6 +398,9 @@
mTransition = null;
mPendingPauseSnapshotsForCancel = null;
mControllers.remove(this);
+ for (int i = 0; i < mStateListeners.size(); i++) {
+ mStateListeners.get(i).onAnimationStateChanged(false);
+ }
}
boolean start(TransitionInfo info, SurfaceControl.Transaction t,
@@ -528,6 +540,9 @@
apps.toArray(new RemoteAnimationTarget[apps.size()]),
wallpapers.toArray(new RemoteAnimationTarget[wallpapers.size()]),
new Rect(0, 0, 0, 0), new Rect(), b);
+ for (int i = 0; i < mStateListeners.size(); i++) {
+ mStateListeners.get(i).onAnimationStateChanged(true);
+ }
} catch (RemoteException e) {
Slog.e(TAG, "Error starting recents animation", e);
cancel("onAnimationStart() failed");
@@ -750,8 +765,17 @@
final boolean wasClosing = closingIdx >= 0;
t.reparent(target.leash, mInfo.getRoot(rootIdx).getLeash());
t.setLayer(target.leash, layer);
- // Hide the animation leash if not already visible, let listener show it
- t.setVisibility(target.leash, !wasClosing);
+ if (wasClosing) {
+ // App was previously visible and is closing
+ t.show(target.leash);
+ t.setAlpha(target.leash, 1f);
+ // Also override the task alpha as it was set earlier when dispatching
+ // the transition and setting up the leash to hide the
+ t.setAlpha(change.getLeash(), 1f);
+ } else {
+ // Hide the animation leash, let the listener show it
+ t.hide(target.leash);
+ }
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
" opening new leaf taskId=%d wasClosing=%b",
target.taskId, wasClosing);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionStateListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionStateListener.java
new file mode 100644
index 0000000..e8733eb
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionStateListener.java
@@ -0,0 +1,30 @@
+/*
+ * 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.recents;
+
+import android.os.IBinder;
+
+/** The listener for the events from {@link RecentsTransitionHandler}. */
+public interface RecentsTransitionStateListener {
+
+ /** Notifies whether the recents animation is running. */
+ default void onAnimationStateChanged(boolean running) {
+ }
+
+ /** Notifies that a recents shell transition has started. */
+ default void onTransitionStarted(IBinder transition) {}
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 991b699..f70b0fc5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -23,7 +23,6 @@
import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.RemoteAnimationTarget.MODE_OPENING;
-
import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
@@ -131,6 +130,7 @@
public static final int EXIT_REASON_CHILD_TASK_ENTER_PIP = 9;
public static final int EXIT_REASON_RECREATE_SPLIT = 10;
public static final int EXIT_REASON_FULLSCREEN_SHORTCUT = 11;
+ public static final int EXIT_REASON_ENTER_DESKTOP = 12;
@IntDef(value = {
EXIT_REASON_UNKNOWN,
EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW,
@@ -144,6 +144,7 @@
EXIT_REASON_CHILD_TASK_ENTER_PIP,
EXIT_REASON_RECREATE_SPLIT,
EXIT_REASON_FULLSCREEN_SHORTCUT,
+ EXIT_REASON_ENTER_DESKTOP
})
@Retention(RetentionPolicy.SOURCE)
@interface ExitReason{}
@@ -1011,6 +1012,8 @@
return "CHILD_TASK_ENTER_PIP";
case EXIT_REASON_RECREATE_SPLIT:
return "RECREATE_SPLIT";
+ case EXIT_REASON_ENTER_DESKTOP:
+ return "ENTER_DESKTOP";
default:
return "unknown reason, reason int = " + exitReason;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java
index 5483fa5..f4ab226 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java
@@ -25,6 +25,7 @@
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__DEVICE_FOLDED;
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__DRAG_DIVIDER;
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__FULLSCREEN_SHORTCUT;
+import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__ENTER_DESKTOP;
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__RECREATE_SPLIT;
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME;
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__ROOT_TASK_VANISHED;
@@ -42,6 +43,7 @@
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_CHILD_TASK_ENTER_PIP;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DEVICE_FOLDED;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER;
+import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_ENTER_DESKTOP;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_FULLSCREEN_SHORTCUT;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_RECREATE_SPLIT;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_RETURN_HOME;
@@ -192,6 +194,8 @@
return SPLITSCREEN_UICHANGED__EXIT_REASON__RECREATE_SPLIT;
case EXIT_REASON_FULLSCREEN_SHORTCUT:
return SPLITSCREEN_UICHANGED__EXIT_REASON__FULLSCREEN_SHORTCUT;
+ case EXIT_REASON_ENTER_DESKTOP:
+ return SPLITSCREEN_UICHANGED__EXIT_REASON__ENTER_DESKTOP;
case EXIT_REASON_UNKNOWN:
// Fall through
default:
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java
index cb76044..edb5aba 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java
@@ -20,7 +20,6 @@
import static com.android.internal.jank.InteractionJankMonitor.CUJ_SPLASHSCREEN_EXIT_ANIM;
import android.animation.Animator;
-import android.annotation.IntDef;
import android.content.Context;
import android.graphics.Rect;
import android.util.Slog;
@@ -47,7 +46,7 @@
private final int mIconFadeOutDuration;
private final int mAppRevealDelay;
private final int mAppRevealDuration;
- @ExitAnimationType
+ @SplashScreenExitAnimationUtils.ExitAnimationType
private final int mAnimationType;
private final int mAnimationDuration;
private final float mIconStartAlpha;
@@ -58,24 +57,6 @@
private Runnable mFinishCallback;
- /**
- * This splash screen exit animation type uses a radial vanish to hide
- * the starting window and slides up the main window content.
- */
- private static final int TYPE_RADIAL_VANISH_SLIDE_UP = 0;
-
- /**
- * This splash screen exit animation type fades out the starting window
- * to reveal the main window content.
- */
- private static final int TYPE_FADE_OUT = 1;
-
- @IntDef(prefix = { "TYPE_" }, value = {
- TYPE_RADIAL_VANISH_SLIDE_UP,
- TYPE_FADE_OUT,
- })
- private @interface ExitAnimationType {}
-
SplashScreenExitAnimation(Context context, SplashScreenView view, SurfaceControl leash,
Rect frame, int mainWindowShiftLength, TransactionPool pool, Runnable handleFinish,
float roundedCornerRadius) {
@@ -121,15 +102,10 @@
}
void startAnimations() {
- if (mAnimationType == TYPE_FADE_OUT) {
- SplashScreenExitAnimationUtils.startFadeOutAnimation(mSplashScreenView,
- mAnimationDuration, this);
- } else {
- SplashScreenExitAnimationUtils.startAnimations(mSplashScreenView, mFirstWindowSurface,
- mMainWindowShiftLength, mTransactionPool, mFirstWindowFrame, mAnimationDuration,
- mIconFadeOutDuration, mIconStartAlpha, mBrandingStartAlpha, mAppRevealDelay,
- mAppRevealDuration, this, mRoundedCornerRadius);
- }
+ SplashScreenExitAnimationUtils.startAnimations(mAnimationType, mSplashScreenView,
+ mFirstWindowSurface, mMainWindowShiftLength, mTransactionPool, mFirstWindowFrame,
+ mAnimationDuration, mIconFadeOutDuration, mIconStartAlpha, mBrandingStartAlpha,
+ mAppRevealDelay, mAppRevealDuration, this, mRoundedCornerRadius);
}
private void reset() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimationUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimationUtils.java
index 64f09a7..e86b62d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimationUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimationUtils.java
@@ -20,6 +20,7 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
+import android.annotation.IntDef;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Configuration;
@@ -54,6 +55,7 @@
public class SplashScreenExitAnimationUtils {
private static final boolean DEBUG_EXIT_ANIMATION = false;
private static final boolean DEBUG_EXIT_ANIMATION_BLEND = false;
+ private static final boolean DEBUG_EXIT_FADE_ANIMATION = false;
private static final String TAG = "SplashScreenExitAnimationUtils";
private static final Interpolator ICON_INTERPOLATOR = new PathInterpolator(0.15f, 0f, 1f, 1f);
@@ -62,19 +64,47 @@
private static final Interpolator SHIFT_UP_INTERPOLATOR = new PathInterpolator(0f, 0f, 0f, 1f);
/**
+ * This splash screen exit animation type uses a radial vanish to hide
+ * the starting window and slides up the main window content.
+ * @hide
+ */
+ public static final int TYPE_RADIAL_VANISH_SLIDE_UP = 0;
+
+ /**
+ * This splash screen exit animation type fades out the starting window
+ * to reveal the main window content.
+ * @hide
+ */
+ public static final int TYPE_FADE_OUT = 1;
+
+ /** @hide */
+ @IntDef(prefix = { "TYPE_" }, value = {
+ TYPE_RADIAL_VANISH_SLIDE_UP,
+ TYPE_FADE_OUT,
+ })
+ public @interface ExitAnimationType {}
+
+ /**
* Creates and starts the animator to fade out the icon, reveal the app, and shift up main
* window with rounded corner radius.
*/
- static void startAnimations(ViewGroup splashScreenView,
- SurfaceControl firstWindowSurface, int mainWindowShiftLength,
- TransactionPool transactionPool, Rect firstWindowFrame, int animationDuration,
- int iconFadeOutDuration, float iconStartAlpha, float brandingStartAlpha,
- int appRevealDelay, int appRevealDuration, Animator.AnimatorListener animatorListener,
- float roundedCornerRadius) {
- ValueAnimator animator = createRadialVanishSlideUpAnimator(splashScreenView,
- firstWindowSurface, mainWindowShiftLength, transactionPool, firstWindowFrame,
- animationDuration, iconFadeOutDuration, iconStartAlpha, brandingStartAlpha,
- appRevealDelay, appRevealDuration, animatorListener, roundedCornerRadius);
+ static void startAnimations(@ExitAnimationType int animationType,
+ ViewGroup splashScreenView, SurfaceControl firstWindowSurface,
+ int mainWindowShiftLength, TransactionPool transactionPool, Rect firstWindowFrame,
+ int animationDuration, int iconFadeOutDuration, float iconStartAlpha,
+ float brandingStartAlpha, int appRevealDelay, int appRevealDuration,
+ Animator.AnimatorListener animatorListener, float roundedCornerRadius) {
+ ValueAnimator animator;
+ if (animationType == TYPE_FADE_OUT) {
+ animator = createFadeOutAnimation(splashScreenView, animationDuration,
+ iconFadeOutDuration, iconStartAlpha, brandingStartAlpha, appRevealDelay,
+ appRevealDuration, animatorListener);
+ } else {
+ animator = createRadialVanishSlideUpAnimator(splashScreenView,
+ firstWindowSurface, mainWindowShiftLength, transactionPool, firstWindowFrame,
+ animationDuration, iconFadeOutDuration, iconStartAlpha, brandingStartAlpha,
+ appRevealDelay, appRevealDuration, animatorListener, roundedCornerRadius);
+ }
animator.start();
}
@@ -88,10 +118,11 @@
TransactionPool transactionPool, Rect firstWindowFrame, int animationDuration,
int iconFadeOutDuration, float iconStartAlpha, float brandingStartAlpha,
int appRevealDelay, int appRevealDuration, Animator.AnimatorListener animatorListener) {
- startAnimations(splashScreenView, firstWindowSurface, mainWindowShiftLength,
- transactionPool, firstWindowFrame, animationDuration, iconFadeOutDuration,
- iconStartAlpha, brandingStartAlpha, appRevealDelay, appRevealDuration,
- animatorListener, 0f /* roundedCornerRadius */);
+ // Start the default 'reveal' animation.
+ startAnimations(TYPE_RADIAL_VANISH_SLIDE_UP, splashScreenView,
+ firstWindowSurface, mainWindowShiftLength, transactionPool, firstWindowFrame,
+ animationDuration, iconFadeOutDuration, iconStartAlpha, brandingStartAlpha,
+ appRevealDelay, appRevealDuration, animatorListener, 0f /* roundedCornerRadius */);
}
/**
@@ -209,18 +240,57 @@
return nightMode == Configuration.UI_MODE_NIGHT_YES;
}
- static void startFadeOutAnimation(ViewGroup splashScreenView,
- int animationDuration, Animator.AnimatorListener animatorListener) {
- final ValueAnimator animator = ValueAnimator.ofFloat(1f, 0f);
+ private static ValueAnimator createFadeOutAnimation(ViewGroup splashScreenView,
+ int animationDuration, int iconFadeOutDuration, float iconStartAlpha,
+ float brandingStartAlpha, int appRevealDelay, int appRevealDuration,
+ Animator.AnimatorListener animatorListener) {
+
+ if (DEBUG_EXIT_FADE_ANIMATION) {
+ splashScreenView.setBackgroundColor(Color.BLUE);
+ }
+
+ final ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f);
animator.setDuration(animationDuration);
animator.setInterpolator(Interpolators.LINEAR);
animator.addUpdateListener(animation -> {
- splashScreenView.setAlpha((float) animation.getAnimatedValue());
+
+ float linearProgress = (float) animation.getAnimatedValue();
+
+ // Icon fade out progress (always starts immediately)
+ final float iconFadeProgress = ICON_INTERPOLATOR.getInterpolation(getProgress(
+ linearProgress, 0 /* delay */, iconFadeOutDuration, animationDuration));
+ View iconView = null;
+ View brandingView = null;
+
+ if (splashScreenView instanceof SplashScreenView) {
+ iconView = ((SplashScreenView) splashScreenView).getIconView();
+ brandingView = ((SplashScreenView) splashScreenView).getBrandingView();
+ }
+ if (iconView != null) {
+ iconView.setAlpha(iconStartAlpha * (1f - iconFadeProgress));
+ }
+ if (brandingView != null) {
+ brandingView.setAlpha(brandingStartAlpha * (1f - iconFadeProgress));
+ }
+
+ // Splash screen fade out progress (possibly delayed)
+ final float splashFadeProgress = Interpolators.ALPHA_OUT.getInterpolation(
+ getProgress(linearProgress, appRevealDelay,
+ appRevealDuration, animationDuration));
+
+ splashScreenView.setAlpha(1f - splashFadeProgress);
+
+ if (DEBUG_EXIT_FADE_ANIMATION) {
+ Slog.d(TAG, "progress -> animation: " + linearProgress
+ + "\t icon alpha: " + ((iconView != null) ? iconView.getAlpha() : "n/a")
+ + "\t splash alpha: " + splashScreenView.getAlpha()
+ );
+ }
});
if (animatorListener != null) {
animator.addListener(animatorListener);
}
- animator.start();
+ return animator;
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java
index 72fc8686..6ea6516 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java
@@ -30,7 +30,7 @@
import static android.window.StartingWindowInfo.TYPE_PARAMETER_NEW_TASK;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_PROCESS_RUNNING;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_TASK_SWITCH;
-import static android.window.StartingWindowInfo.TYPE_PARAMETER_USE_SOLID_COLOR_SPLASH_SCREEN;
+import static android.window.StartingWindowInfo.TYPE_PARAMETER_ALLOW_ICON;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_WINDOWLESS;
import android.window.StartingWindowInfo;
@@ -52,8 +52,7 @@
final boolean processRunning = (parameter & TYPE_PARAMETER_PROCESS_RUNNING) != 0;
final boolean allowTaskSnapshot = (parameter & TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT) != 0;
final boolean activityCreated = (parameter & TYPE_PARAMETER_ACTIVITY_CREATED) != 0;
- final boolean isSolidColorSplashScreen =
- (parameter & TYPE_PARAMETER_USE_SOLID_COLOR_SPLASH_SCREEN) != 0;
+ final boolean allowIcon = (parameter & TYPE_PARAMETER_ALLOW_ICON) != 0;
final boolean legacySplashScreen =
((parameter & TYPE_PARAMETER_LEGACY_SPLASH_SCREEN) != 0);
final boolean activityDrawn = (parameter & TYPE_PARAMETER_ACTIVITY_DRAWN) != 0;
@@ -67,13 +66,13 @@
+ "processRunning=%b, "
+ "allowTaskSnapshot=%b, "
+ "activityCreated=%b, "
- + "isSolidColorSplashScreen=%b, "
+ + "allowIcon=%b, "
+ "legacySplashScreen=%b, "
+ "activityDrawn=%b, "
+ "windowless=%b, "
+ "topIsHome=%b",
newTask, taskSwitch, processRunning, allowTaskSnapshot, activityCreated,
- isSolidColorSplashScreen, legacySplashScreen, activityDrawn, windowlessSurface,
+ allowIcon, legacySplashScreen, activityDrawn, windowlessSurface,
topIsHome);
if (windowlessSurface) {
@@ -81,7 +80,7 @@
}
if (!topIsHome) {
if (!processRunning || newTask || (taskSwitch && !activityCreated)) {
- return getSplashscreenType(isSolidColorSplashScreen, legacySplashScreen);
+ return getSplashscreenType(allowIcon, legacySplashScreen);
}
}
@@ -95,18 +94,18 @@
}
}
if (!activityDrawn && !topIsHome) {
- return getSplashscreenType(isSolidColorSplashScreen, legacySplashScreen);
+ return getSplashscreenType(allowIcon, legacySplashScreen);
}
}
return STARTING_WINDOW_TYPE_NONE;
}
- private static int getSplashscreenType(boolean solidColorSplashScreen,
- boolean legacySplashScreen) {
- return solidColorSplashScreen
- ? STARTING_WINDOW_TYPE_SOLID_COLOR_SPLASH_SCREEN
- : legacySplashScreen
- ? STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN
+ private static int getSplashscreenType(boolean allowIcon, boolean legacySplashScreen) {
+ if (allowIcon) {
+ return legacySplashScreen ? STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN
: STARTING_WINDOW_TYPE_SPLASH_SCREEN;
+ } else {
+ return STARTING_WINDOW_TYPE_SOLID_COLOR_SPLASH_SCREEN;
+ }
}
}
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 c189731..82fc0f4 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
@@ -18,6 +18,7 @@
import android.app.ActivityManager.RunningTaskInfo;
import android.app.WindowConfiguration;
+import android.app.WindowConfiguration.WindowingMode;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.Color;
@@ -114,7 +115,7 @@
mRelayoutParams.reset();
mRelayoutParams.mRunningTaskInfo = taskInfo;
mRelayoutParams.mLayoutResId = R.layout.caption_window_decor;
- mRelayoutParams.mCaptionHeightId = getCaptionHeightId();
+ mRelayoutParams.mCaptionHeightId = getCaptionHeightId(taskInfo.getWindowingMode());
mRelayoutParams.mShadowRadiusId = shadowRadiusID;
mRelayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw;
@@ -227,7 +228,7 @@
}
@Override
- int getCaptionHeightId() {
+ int getCaptionHeightId(@WindowingMode int windowingMode) {
return R.dimen.freeform_decor_caption_height;
}
}
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 026d0cd..afa2754 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
@@ -21,6 +21,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
import static com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler.FINAL_FREEFORM_SCALE;
@@ -68,10 +69,13 @@
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
import com.android.wm.shell.desktopmode.DesktopModeStatus;
import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition;
import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
+import com.android.wm.shell.recents.RecentsTransitionHandler;
+import com.android.wm.shell.recents.RecentsTransitionStateListener;
import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.sysui.KeyguardChangeListener;
@@ -101,6 +105,7 @@
private final DisplayController mDisplayController;
private final SyncTransactionQueue mSyncQueue;
private final Optional<DesktopTasksController> mDesktopTasksController;
+ private final RecentsTransitionHandler mRecentsTransitionHandler;
private boolean mTransitionDragActive;
private SparseArray<EventReceiver> mEventReceiversByDisplay = new SparseArray<>();
@@ -120,7 +125,8 @@
private MoveToDesktopAnimator mMoveToDesktopAnimator;
private final Rect mDragToDesktopAnimationStartBounds = new Rect();
- private final DesktopModeKeyguardChangeListener mDesktopModeKeyguardChangeListener;
+ private final DesktopModeKeyguardChangeListener mDesktopModeKeyguardChangeListener =
+ new DesktopModeKeyguardChangeListener();
private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
public DesktopModeWindowDecorViewModel(
@@ -134,6 +140,7 @@
SyncTransactionQueue syncQueue,
Transitions transitions,
Optional<DesktopTasksController> desktopTasksController,
+ RecentsTransitionHandler recentsTransitionHandler,
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer
) {
this(
@@ -147,10 +154,10 @@
syncQueue,
transitions,
desktopTasksController,
+ recentsTransitionHandler,
new DesktopModeWindowDecoration.Factory(),
new InputMonitorFactory(),
SurfaceControl.Transaction::new,
- new DesktopModeKeyguardChangeListener(),
rootTaskDisplayAreaOrganizer);
}
@@ -166,10 +173,10 @@
SyncTransactionQueue syncQueue,
Transitions transitions,
Optional<DesktopTasksController> desktopTasksController,
+ RecentsTransitionHandler recentsTransitionHandler,
DesktopModeWindowDecoration.Factory desktopModeWindowDecorFactory,
InputMonitorFactory inputMonitorFactory,
Supplier<SurfaceControl.Transaction> transactionFactory,
- DesktopModeKeyguardChangeListener desktopModeKeyguardChangeListener,
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
mContext = context;
mMainHandler = mainHandler;
@@ -181,11 +188,11 @@
mSyncQueue = syncQueue;
mTransitions = transitions;
mDesktopTasksController = desktopTasksController;
+ mRecentsTransitionHandler = recentsTransitionHandler;
mDesktopModeWindowDecorFactory = desktopModeWindowDecorFactory;
mInputMonitorFactory = inputMonitorFactory;
mTransactionFactory = transactionFactory;
- mDesktopModeKeyguardChangeListener = desktopModeKeyguardChangeListener;
mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer;
shellInit.addInitCallback(this::onInit, this);
@@ -193,6 +200,12 @@
private void onInit() {
mShellController.addKeyguardChangeListener(mDesktopModeKeyguardChangeListener);
+ mRecentsTransitionHandler.addTransitionStateListener(new RecentsTransitionStateListener() {
+ @Override
+ public void onTransitionStarted(IBinder transition) {
+ onRecentsTransitionStarted(transition);
+ }
+ });
}
@Override
@@ -318,6 +331,16 @@
}
}
+ private void onRecentsTransitionStarted(IBinder transition) {
+ // Block relayout on window decorations originating from #onTaskInfoChanges until the
+ // animation completes to avoid interfering with the transition animation.
+ for (int i = 0; i < mWindowDecorByTaskId.size(); i++) {
+ final DesktopModeWindowDecoration decor = mWindowDecorByTaskId.valueAt(i);
+ decor.incrementRelayoutBlock();
+ decor.addTransitionPausingRelayout(transition);
+ }
+ }
+
private class DesktopModeTouchEventListener extends GestureDetector.SimpleOnGestureListener
implements View.OnClickListener, View.OnTouchListener, View.OnLongClickListener,
DragDetector.MotionEventHandler{
@@ -348,13 +371,8 @@
final int id = v.getId();
if (id == R.id.close_window || id == R.id.close_button) {
mTaskOperations.closeTask(mTaskToken);
- if (mSplitScreenController != null
- && mSplitScreenController.isSplitScreenVisible()) {
- int remainingTaskPosition = mTaskId == mSplitScreenController
- .getTaskInfo(SPLIT_POSITION_TOP_OR_LEFT).taskId
- ? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT;
- ActivityManager.RunningTaskInfo remainingTask = mSplitScreenController
- .getTaskInfo(remainingTaskPosition);
+ if (isTaskInSplitScreen(mTaskId)) {
+ RunningTaskInfo remainingTask = getOtherSplitTask(mTaskId);
mSplitScreenController.moveTaskToFullscreen(remainingTask.taskId);
}
decoration.closeMaximizeMenu();
@@ -376,6 +394,7 @@
mWindowDecorByTaskId.get(mTaskId).addCaptionInset(wct);
decoration.incrementRelayoutBlock();
mDesktopTasksController.get().moveToDesktop(decoration, mTaskId, wct);
+ closeOtherSplitTask(mTaskId);
}
decoration.closeHandleMenu();
} else if (id == R.id.fullscreen_button) {
@@ -720,6 +739,7 @@
relevantDecor.mTaskInfo.displayId);
if (ev.getY() > statusBarHeight) {
if (mMoveToDesktopAnimator == null) {
+ closeOtherSplitTask(relevantDecor.mTaskInfo.taskId);
mMoveToDesktopAnimator = new MoveToDesktopAnimator(
mDragToDesktopAnimationStartBounds, relevantDecor.mTaskInfo,
relevantDecor.mTaskSurface);
@@ -810,7 +830,8 @@
private DesktopModeWindowDecoration getRelevantWindowDecor(MotionEvent ev) {
if (mSplitScreenController != null && mSplitScreenController.isSplitScreenVisible()) {
// We can't look at focused task here as only one task will have focus.
- return getSplitScreenDecor(ev);
+ DesktopModeWindowDecoration splitTaskDecor = getSplitScreenDecor(ev);
+ return splitTaskDecor == null ? getFocusedDecor() : splitTaskDecor;
} else {
return getFocusedDecor();
}
@@ -942,6 +963,24 @@
}
}
+ private RunningTaskInfo getOtherSplitTask(int taskId) {
+ @SplitPosition int remainingTaskPosition = mSplitScreenController
+ .getSplitPosition(taskId) == SPLIT_POSITION_BOTTOM_OR_RIGHT
+ ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT;
+ return mSplitScreenController.getTaskInfo(remainingTaskPosition);
+ }
+
+ private void closeOtherSplitTask(int taskId) {
+ if (isTaskInSplitScreen(taskId)) {
+ mTaskOperations.closeTask(getOtherSplitTask(taskId).token);
+ }
+ }
+
+ private boolean isTaskInSplitScreen(int taskId) {
+ return mSplitScreenController != null
+ && mSplitScreenController.isTaskInSplitScreen(taskId);
+ }
+
private class DragStartListenerImpl
implements DragPositioningCallbackUtility.DragStartListener {
@Override
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 dbff20e..84ec6b3 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
@@ -17,8 +17,10 @@
package com.android.wm.shell.windowdecor;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import android.app.ActivityManager;
+import android.app.WindowConfiguration.WindowingMode;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
@@ -56,6 +58,7 @@
import java.util.HashSet;
import java.util.Set;
+import java.util.function.Supplier;
/**
* Defines visuals and behaviors of a window decoration of a caption bar and shadows. It works with
@@ -109,7 +112,30 @@
Choreographer choreographer,
SyncTransactionQueue syncQueue,
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
- super(context, displayController, taskOrganizer, taskInfo, taskSurface, windowDecorConfig);
+ this (context, displayController, taskOrganizer, taskInfo, taskSurface, windowDecorConfig,
+ handler, choreographer, syncQueue, rootTaskDisplayAreaOrganizer,
+ SurfaceControl.Builder::new, SurfaceControl.Transaction::new,
+ WindowContainerTransaction::new, new SurfaceControlViewHostFactory() {});
+ }
+
+ DesktopModeWindowDecoration(
+ Context context,
+ DisplayController displayController,
+ ShellTaskOrganizer taskOrganizer,
+ ActivityManager.RunningTaskInfo taskInfo,
+ SurfaceControl taskSurface,
+ Configuration windowDecorConfig,
+ Handler handler,
+ Choreographer choreographer,
+ SyncTransactionQueue syncQueue,
+ RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
+ Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier,
+ Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier,
+ Supplier<WindowContainerTransaction> windowContainerTransactionSupplier,
+ SurfaceControlViewHostFactory surfaceControlViewHostFactory) {
+ super(context, displayController, taskOrganizer, taskInfo, taskSurface, windowDecorConfig,
+ surfaceControlBuilderSupplier, surfaceControlTransactionSupplier,
+ windowContainerTransactionSupplier, surfaceControlViewHostFactory);
mHandler = handler;
mChoreographer = choreographer;
@@ -150,7 +176,7 @@
return;
}
- final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ final SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get();
// Use |applyStartTransactionOnDraw| so that the transaction (that applies task crop) is
// synced with the buffer transaction (that draws the View). Both will be shown on screen
// at the same, whereas applying them independently causes flickering. See b/270202228.
@@ -180,13 +206,23 @@
mRelayoutParams.reset();
mRelayoutParams.mRunningTaskInfo = taskInfo;
mRelayoutParams.mLayoutResId = windowDecorLayoutId;
- mRelayoutParams.mCaptionHeightId = getCaptionHeightId();
+ mRelayoutParams.mCaptionHeightId = getCaptionHeightId(taskInfo.getWindowingMode());
mRelayoutParams.mShadowRadiusId = shadowRadiusID;
mRelayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw;
-
- mRelayoutParams.mWindowDecorConfig = DesktopTasksController.isDesktopDensityOverrideSet()
- ? mContext.getResources().getConfiguration() // Use system context
- : mTaskInfo.configuration; // Use task configuration
+ // The configuration used to lay out the window decoration. The system context's config is
+ // used when the task density has been overridden to a custom density so that the resources
+ // and views of the decoration aren't affected and match the rest of the System UI, if not
+ // then just use the task's configuration. A copy is made instead of using the original
+ // reference so that the configuration isn't mutated on config changes and diff checks can
+ // be made in WindowDecoration#relayout using the pre/post-relayout configuration.
+ // See b/301119301.
+ // TODO(b/301119301): consider moving the config data needed for diffs to relayout params
+ // instead of using a whole Configuration as a parameter.
+ final Configuration windowDecorConfig = new Configuration();
+ windowDecorConfig.setTo(DesktopTasksController.isDesktopDensityOverrideSet()
+ ? mContext.getResources().getConfiguration() // Use system context.
+ : mTaskInfo.configuration); // Use task configuration.
+ mRelayoutParams.mWindowDecorConfig = windowDecorConfig;
mRelayoutParams.mCornerRadius =
(int) ScreenDecorationsUtils.getWindowCornerRadius(mContext);
@@ -286,7 +322,7 @@
final int displayWidth = displayLayout.width();
final int displayHeight = displayLayout.height();
- final int captionHeight = getCaptionHeight();
+ final int captionHeight = getCaptionHeight(mTaskInfo.getWindowingMode());
final ImageButton maximizeWindowButton =
mResult.mRootView.findViewById(R.id.maximize_window);
@@ -442,6 +478,7 @@
@Override
void releaseViews() {
closeHandleMenu();
+ closeMaximizeMenu();
super.releaseViews();
}
@@ -545,7 +582,7 @@
super.close();
}
- private int getDesktopModeWindowDecorLayoutId(int windowingMode) {
+ private int getDesktopModeWindowDecorLayoutId(@WindowingMode int windowingMode) {
return windowingMode == WINDOWING_MODE_FREEFORM
? R.layout.desktop_mode_app_controls_window_decor
: R.layout.desktop_mode_focused_window_decor;
@@ -574,7 +611,7 @@
exclusionRegion = new Region();
}
exclusionRegion.union(new Rect(0, 0, mResult.mWidth,
- getCaptionHeight()));
+ getCaptionHeight(mTaskInfo.getWindowingMode())));
exclusionRegion.translate(mPositionInParent.x, mPositionInParent.y);
return exclusionRegion;
}
@@ -590,12 +627,14 @@
}
@Override
- int getCaptionHeightId() {
- return R.dimen.freeform_decor_caption_height;
+ int getCaptionHeightId(@WindowingMode int windowingMode) {
+ return windowingMode == WINDOWING_MODE_FULLSCREEN
+ ? R.dimen.desktop_mode_fullscreen_decor_caption_height
+ : R.dimen.desktop_mode_freeform_decor_caption_height;
}
- private int getCaptionHeight() {
- return loadDimensionPixelSize(mContext.getResources(), getCaptionHeightId());
+ private int getCaptionHeight(@WindowingMode int windowingMode) {
+ return loadDimensionPixelSize(mContext.getResources(), getCaptionHeightId(windowingMode));
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index a269275..3c853f1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -20,6 +20,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import android.app.ActivityManager.RunningTaskInfo;
+import android.app.WindowConfiguration.WindowingMode;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -196,7 +197,8 @@
final int oldDensityDpi = mWindowDecorConfig.densityDpi;
mWindowDecorConfig = params.mWindowDecorConfig != null ? params.mWindowDecorConfig
: mTaskInfo.getConfiguration();
- if (oldDensityDpi != mWindowDecorConfig.densityDpi
+ final int newDensityDpi = mWindowDecorConfig.densityDpi;
+ if (oldDensityDpi != newDensityDpi
|| mDisplay == null
|| mDisplay.getDisplayId() != mTaskInfo.displayId
|| oldLayoutResId != mLayoutResId) {
@@ -336,7 +338,7 @@
}
}
- int getCaptionHeightId() {
+ int getCaptionHeightId(@WindowingMode int windowingMode) {
return Resources.ID_NULL;
}
@@ -458,7 +460,7 @@
* Adds caption inset source to a WCT
*/
public void addCaptionInset(WindowContainerTransaction wct) {
- final int captionHeightId = getCaptionHeightId();
+ final int captionHeightId = getCaptionHeightId(mTaskInfo.getWindowingMode());
if (!ViewRootImpl.CAPTION_ON_SHELL || captionHeightId == Resources.ID_NULL) {
return;
}
diff --git a/libs/WindowManager/Shell/tests/flicker/Android.bp b/libs/WindowManager/Shell/tests/flicker/Android.bp
index 6fd3354..0058d11 100644
--- a/libs/WindowManager/Shell/tests/flicker/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/Android.bp
@@ -119,6 +119,20 @@
],
}
+java_library {
+ name: "wm-shell-flicker-platinum-tests",
+ platform_apis: true,
+ optimize: {
+ enabled: false,
+ },
+ srcs: [
+ ":WMShellFlickerServicePlatinumTests-src",
+ ],
+ static_libs: [
+ "wm-shell-flicker-utils",
+ ],
+}
+
java_defaults {
name: "WMShellFlickerTestsDefault",
manifest: "manifests/AndroidManifest.xml",
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt
index c335d3d..03c438f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt
@@ -23,6 +23,7 @@
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import android.tools.common.flicker.subject.region.RegionTraceSubject
import android.tools.device.helpers.WindowUtils
import android.tools.device.traces.parsers.toFlickerComponent
import androidx.test.filters.RequiresDevice
@@ -80,7 +81,9 @@
secondAppForSplitScreen.launchViaIntent(wmHelper)
pipApp.launchViaIntent(wmHelper)
tapl.goHome()
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, pipApp, secondAppForSplitScreen)
+ SplitScreenUtils.enterSplit(
+ wmHelper, tapl, device, pipApp, secondAppForSplitScreen,
+ flicker.scenario.startRotation)
pipApp.enableAutoEnterForPipActivity()
}
teardown {
@@ -126,9 +129,20 @@
if (tapl.isTablet) {
flicker.assertWmVisibleRegion(pipApp) { coversAtMost(displayBounds) }
} else {
- // on phones home does not rotate in landscape, PiP enters back to portrait
- // orientation so use display bounds from that orientation for assertion
- flicker.assertWmVisibleRegion(pipApp) { coversAtMost(portraitDisplayBounds) }
+ // on phones home screen does not rotate in landscape, PiP enters back to portrait
+ // orientation - if we go from landscape to portrait it should switch between the bounds
+ // otherwise it should be the same as tablet (i.e. portrait to portrait)
+ if (flicker.scenario.isLandscapeOrSeascapeAtStart) {
+ flicker.assertWmVisibleRegion(pipApp) {
+ // first check against landscape bounds then against portrait bounds
+ (coversAtMost(displayBounds).then() as RegionTraceSubject).coversAtMost(
+ portraitDisplayBounds
+ )
+ }
+ } else {
+ // always check against the display bounds which do not change during transition
+ flicker.assertWmVisibleRegion(pipApp) { coversAtMost(displayBounds) }
+ }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
index aa35237..a8f4d0a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
@@ -31,7 +31,7 @@
class DismissSplitScreenByGoHomeGesturalNavLandscape :
DismissSplitScreenByGoHome(Rotation.ROTATION_90) {
- @ExpectedScenarios(["SPLIT_SCREEN_EXIT"])
+ @ExpectedScenarios(["APP_CLOSE_TO_HOME"]) // SPLIT_SCREEN_EXIT not yet tagged here (b/301222449)
@Test
override fun dismissSplitScreenByGoHome() = super.dismissSplitScreenByGoHome()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
index e195360..cee9bbf 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
@@ -31,7 +31,7 @@
class DismissSplitScreenByGoHomeGesturalNavPortrait :
DismissSplitScreenByGoHome(Rotation.ROTATION_0) {
- @ExpectedScenarios(["SPLIT_SCREEN_EXIT"])
+ @ExpectedScenarios(["APP_CLOSE_TO_HOME"]) // SPLIT_SCREEN_EXIT not yet tagged here (b/301222449)
@Test
override fun dismissSplitScreenByGoHome() = super.dismissSplitScreenByGoHome()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
index 5f771c7..169b5cf 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
@@ -31,7 +31,7 @@
class EnterSplitScreenByDragFromAllAppsGesturalNavLandscape :
EnterSplitScreenByDragFromAllApps(Rotation.ROTATION_90) {
- @ExpectedScenarios(["SPLIT_SCREEN_ENTER"])
+ @ExpectedScenarios(["ENTIRE_TRACE"]) // missing SPLIT_SCREEN_ENTER tag (b/301093332)
@Test
override fun enterSplitScreenByDragFromAllApps() = super.enterSplitScreenByDragFromAllApps()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
index 729a401..412c011 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
@@ -31,7 +31,7 @@
class EnterSplitScreenByDragFromAllAppsGesturalNavPortrait :
EnterSplitScreenByDragFromAllApps(Rotation.ROTATION_0) {
- @ExpectedScenarios(["SPLIT_SCREEN_ENTER"])
+ @ExpectedScenarios(["ENTIRE_TRACE"]) // missing SPLIT_SCREEN_ENTER tag (b/301093332)
@Test
override fun enterSplitScreenByDragFromAllApps() = super.enterSplitScreenByDragFromAllApps()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavLandscape.kt
index e37d806..b444bad 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavLandscape.kt
@@ -19,10 +19,15 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.CopyContentInSplit
+import org.junit.Rule
import org.junit.Test
open class CopyContentInSplitGesturalNavLandscape : CopyContentInSplit(Rotation.ROTATION_90) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavPortrait.kt
index 2a50912..e2ab989 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavPortrait.kt
@@ -19,10 +19,15 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.CopyContentInSplit
+import org.junit.Rule
import org.junit.Test
open class CopyContentInSplitGesturalNavPortrait : CopyContentInSplit(Rotation.ROTATION_0) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavLandscape.kt
index d5da1a8..22b8102 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavLandscape.kt
@@ -19,11 +19,16 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByDivider
+import org.junit.Rule
import org.junit.Test
open class DismissSplitScreenByDividerGesturalNavLandscape :
DismissSplitScreenByDivider(Rotation.ROTATION_90) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavPortrait.kt
index 7fdcb9b..3fb014f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavPortrait.kt
@@ -19,11 +19,16 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByDivider
+import org.junit.Rule
import org.junit.Test
open class DismissSplitScreenByDividerGesturalNavPortrait :
DismissSplitScreenByDivider(Rotation.ROTATION_0) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
index 308e954..ea1f942 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
@@ -19,11 +19,16 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByGoHome
+import org.junit.Rule
import org.junit.Test
open class DismissSplitScreenByGoHomeGesturalNavLandscape :
DismissSplitScreenByGoHome(Rotation.ROTATION_90) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
index 39e75bd..8f23a79 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
@@ -19,11 +19,16 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByGoHome
+import org.junit.Rule
import org.junit.Test
open class DismissSplitScreenByGoHomeGesturalNavPortrait :
DismissSplitScreenByGoHome(Rotation.ROTATION_0) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavLandscape.kt
index e18da17..b0f39e5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavLandscape.kt
@@ -19,10 +19,15 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.DragDividerToResize
+import org.junit.Rule
import org.junit.Test
open class DragDividerToResizeGesturalNavLandscape : DragDividerToResize(Rotation.ROTATION_90) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavPortrait.kt
index 00d60e7..6ce8746 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavPortrait.kt
@@ -19,10 +19,15 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.DragDividerToResize
+import org.junit.Rule
import org.junit.Test
open class DragDividerToResizeGesturalNavPortrait : DragDividerToResize(Rotation.ROTATION_0) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
index d7efbc8..9f74edf 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
@@ -19,11 +19,16 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromAllApps
+import org.junit.Rule
import org.junit.Test
open class EnterSplitScreenByDragFromAllAppsGesturalNavLandscape :
EnterSplitScreenByDragFromAllApps(Rotation.ROTATION_90) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
index 4eece3f..1e4055e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
@@ -19,11 +19,16 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromAllApps
+import org.junit.Rule
import org.junit.Test
open class EnterSplitScreenByDragFromAllAppsGesturalNavPortrait :
EnterSplitScreenByDragFromAllApps(Rotation.ROTATION_0) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
index d96b056..c3b8132 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
@@ -19,11 +19,16 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromNotification
+import org.junit.Rule
import org.junit.Test
open class EnterSplitScreenByDragFromNotificationGesturalNavLandscape :
EnterSplitScreenByDragFromNotification(Rotation.ROTATION_90) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
index 809b690..7756d04 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
@@ -19,11 +19,16 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromNotification
+import org.junit.Rule
import org.junit.Test
open class EnterSplitScreenByDragFromNotificationGesturalNavPortrait :
EnterSplitScreenByDragFromNotification(Rotation.ROTATION_0) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
index bbdf2d7..c72aa5a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
@@ -19,11 +19,16 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromShortcut
+import org.junit.Rule
import org.junit.Test
open class EnterSplitScreenByDragFromShortcutGesturalNavLandscape :
EnterSplitScreenByDragFromShortcut(Rotation.ROTATION_90) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
index 5c29fd8..cc88f27 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
@@ -19,11 +19,16 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromShortcut
+import org.junit.Rule
import org.junit.Test
open class EnterSplitScreenByDragFromShortcutGesturalNavPortrait :
EnterSplitScreenByDragFromShortcut(Rotation.ROTATION_0) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
index a7398eb..87b38b1 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
@@ -19,11 +19,16 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromTaskbar
+import org.junit.Rule
import org.junit.Test
open class EnterSplitScreenByDragFromTaskbarGesturalNavLandscape :
EnterSplitScreenByDragFromTaskbar(Rotation.ROTATION_90) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
index eae88ad..ca347ed 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
@@ -19,11 +19,16 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromTaskbar
+import org.junit.Rule
import org.junit.Test
open class EnterSplitScreenByDragFromTaskbarGesturalNavPortrait :
EnterSplitScreenByDragFromTaskbar(Rotation.ROTATION_0) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
index 7e8ee04..6597819 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
@@ -19,11 +19,16 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenFromOverview
+import org.junit.Rule
import org.junit.Test
open class EnterSplitScreenFromOverviewGesturalNavLandscape :
EnterSplitScreenFromOverview(Rotation.ROTATION_90) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
index 9295c33..6df31fc 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
@@ -19,11 +19,16 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenFromOverview
+import org.junit.Rule
import org.junit.Test
open class EnterSplitScreenFromOverviewGesturalNavPortrait :
EnterSplitScreenFromOverview(Rotation.ROTATION_0) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
index 4b59e9f..02596c5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
@@ -19,11 +19,16 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchAppByDoubleTapDivider
+import org.junit.Rule
import org.junit.Test
open class SwitchAppByDoubleTapDividerGesturalNavLandscape :
SwitchAppByDoubleTapDivider(Rotation.ROTATION_90) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
index 5ff36d4..9d579f6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
@@ -19,11 +19,16 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchAppByDoubleTapDivider
+import org.junit.Rule
import org.junit.Test
open class SwitchAppByDoubleTapDividerGesturalNavPortrait :
SwitchAppByDoubleTapDivider(Rotation.ROTATION_0) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
index c0cb721..da85342 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
@@ -19,11 +19,16 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromAnotherApp
+import org.junit.Rule
import org.junit.Test
open class SwitchBackToSplitFromAnotherAppGesturalNavLandscape :
SwitchBackToSplitFromAnotherApp(Rotation.ROTATION_90) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
index 8c14088..1ae2c9e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
@@ -19,11 +19,16 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromAnotherApp
+import org.junit.Rule
import org.junit.Test
open class SwitchBackToSplitFromAnotherAppGesturalNavPortrait :
SwitchBackToSplitFromAnotherApp(Rotation.ROTATION_0) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
index 7b6614b..b1b56257 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
@@ -19,11 +19,16 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromHome
+import org.junit.Rule
import org.junit.Test
open class SwitchBackToSplitFromHomeGesturalNavLandscape :
SwitchBackToSplitFromHome(Rotation.ROTATION_90) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
index 5df5be9..08c437e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
@@ -19,11 +19,16 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromHome
+import org.junit.Rule
import org.junit.Test
open class SwitchBackToSplitFromHomeGesturalNavPortrait :
SwitchBackToSplitFromHome(Rotation.ROTATION_0) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
index 9d63003..efbf86d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
@@ -19,11 +19,16 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromRecent
+import org.junit.Rule
import org.junit.Test
open class SwitchBackToSplitFromRecentGesturalNavLandscape :
SwitchBackToSplitFromRecent(Rotation.ROTATION_90) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
index 9fa04b2..f7072fa 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
@@ -19,11 +19,16 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromRecent
+import org.junit.Rule
import org.junit.Test
open class SwitchBackToSplitFromRecentGesturalNavPortrait :
SwitchBackToSplitFromRecent(Rotation.ROTATION_0) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavLandscape.kt
index 9386aa2..d80d112 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavLandscape.kt
@@ -19,11 +19,16 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBetweenSplitPairs
+import org.junit.Rule
import org.junit.Test
open class SwitchBetweenSplitPairsGesturalNavLandscape :
SwitchBetweenSplitPairs(Rotation.ROTATION_90) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavPortrait.kt
index 5ef2167..30ec37a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavPortrait.kt
@@ -19,11 +19,16 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBetweenSplitPairs
+import org.junit.Rule
import org.junit.Test
open class SwitchBetweenSplitPairsGesturalNavPortrait :
SwitchBetweenSplitPairs(Rotation.ROTATION_0) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
index 9caab9b..1e086d2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
@@ -18,13 +18,18 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.UnlockKeyguardToSplitScreen
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.BlockJUnit4ClassRunner
@RunWith(BlockJUnit4ClassRunner::class)
open class UnlockKeyguardToSplitScreenGesturalNavLandscape : UnlockKeyguardToSplitScreen() {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
index bf484e5..932f892 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
@@ -18,13 +18,18 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.UnlockKeyguardToSplitScreen
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.BlockJUnit4ClassRunner
@RunWith(BlockJUnit4ClassRunner::class)
open class UnlockKeyguardToSplitScreenGesturalNavPortrait : UnlockKeyguardToSplitScreen() {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/CopyContentInSplit.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/CopyContentInSplit.kt
index 245184c..80ab24d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/CopyContentInSplit.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/CopyContentInSplit.kt
@@ -51,7 +51,7 @@
tapl.setEnableRotation(true)
tapl.setExpectedRotation(rotation.value)
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, textEditApp)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, textEditApp, rotation)
}
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByDivider.kt
index 1f2f1ec..4c39104 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByDivider.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByDivider.kt
@@ -49,7 +49,7 @@
tapl.setEnableRotation(true)
tapl.setExpectedRotation(rotation.value)
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, rotation)
}
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByGoHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByGoHome.kt
index ebbf7c5..f6d1afc 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByGoHome.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByGoHome.kt
@@ -49,7 +49,7 @@
tapl.setEnableRotation(true)
tapl.setExpectedRotation(rotation.value)
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, rotation)
}
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DragDividerToResize.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DragDividerToResize.kt
index 71e701c..db5a32a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DragDividerToResize.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DragDividerToResize.kt
@@ -49,7 +49,7 @@
tapl.setEnableRotation(true)
tapl.setExpectedRotation(rotation.value)
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, rotation)
}
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromAllApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromAllApps.kt
index c433b21..d7b306c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromAllApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromAllApps.kt
@@ -19,6 +19,7 @@
import android.app.Instrumentation
import android.tools.common.NavBar
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.ChangeDisplayOrientationRule
import android.tools.device.traces.parsers.WindowManagerStateHelper
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
@@ -49,11 +50,13 @@
fun setup() {
Assume.assumeTrue(tapl.isTablet)
+ tapl.goHome()
+
+ primaryApp.launchViaIntent(wmHelper)
+ ChangeDisplayOrientationRule.setRotation(rotation)
+
tapl.setEnableRotation(true)
tapl.setExpectedRotation(rotation.value)
-
- tapl.goHome()
- primaryApp.launchViaIntent(wmHelper)
}
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt
index 3f087a5..cc982d1 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt
@@ -19,6 +19,7 @@
import android.app.Instrumentation
import android.tools.common.NavBar
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.ChangeDisplayOrientationRule
import android.tools.device.traces.parsers.WindowManagerStateHelper
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
@@ -50,14 +51,16 @@
fun setup() {
Assume.assumeTrue(tapl.isTablet)
- tapl.setEnableRotation(true)
- tapl.setExpectedRotation(rotation.value)
-
// Send a notification
sendNotificationApp.launchViaIntent(wmHelper)
sendNotificationApp.postNotification(wmHelper)
tapl.goHome()
+
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+
primaryApp.launchViaIntent(wmHelper)
+ ChangeDisplayOrientationRule.setRotation(rotation)
}
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromShortcut.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromShortcut.kt
index 767e7b5..fa12bb8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromShortcut.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromShortcut.kt
@@ -19,6 +19,7 @@
import android.app.Instrumentation
import android.tools.common.NavBar
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.ChangeDisplayOrientationRule
import android.tools.device.traces.parsers.WindowManagerStateHelper
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
@@ -49,12 +50,13 @@
fun setup() {
Assume.assumeTrue(tapl.isTablet)
- tapl.setEnableRotation(true)
- tapl.setExpectedRotation(rotation.value)
-
tapl.goHome()
SplitScreenUtils.createShortcutOnHotseatIfNotExist(tapl, secondaryApp.appName)
primaryApp.launchViaIntent(wmHelper)
+ ChangeDisplayOrientationRule.setRotation(rotation)
+
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
}
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenFromOverview.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenFromOverview.kt
index f2cbf24..983653b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenFromOverview.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenFromOverview.kt
@@ -46,9 +46,6 @@
@Before
fun setup() {
- tapl.setEnableRotation(true)
- tapl.setExpectedRotation(rotation.value)
-
primaryApp.launchViaIntent(wmHelper)
secondaryApp.launchViaIntent(wmHelper)
tapl.goHome()
@@ -57,11 +54,14 @@
.withAppTransitionIdle()
.withHomeActivityVisible()
.waitForAndVerify()
+
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
}
@Test
open fun enterSplitScreenFromOverview() {
- SplitScreenUtils.splitFromOverview(tapl, device)
+ SplitScreenUtils.splitFromOverview(tapl, device, rotation)
SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt
index 538ed96..068171d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt
@@ -48,11 +48,12 @@
@Before
fun setup() {
- tapl.setEnableRotation(true)
- tapl.setExpectedRotation(rotation.value)
tapl.workspace.switchToOverview().dismissAllTasks()
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, rotation)
}
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromAnotherApp.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromAnotherApp.kt
index 0dab5ad..64b75c5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromAnotherApp.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromAnotherApp.kt
@@ -50,7 +50,7 @@
tapl.setEnableRotation(true)
tapl.setExpectedRotation(rotation.value)
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, rotation)
thirdApp.launchViaIntent(wmHelper)
wmHelper.StateSyncBuilder().withWindowSurfaceAppeared(thirdApp).waitForAndVerify()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromHome.kt
index ad3a2d4..1795010 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromHome.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromHome.kt
@@ -49,7 +49,7 @@
tapl.setEnableRotation(true)
tapl.setExpectedRotation(rotation.value)
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, rotation)
tapl.goHome()
wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt
index b780a16..7065846 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt
@@ -46,11 +46,12 @@
@Before
fun setup() {
- tapl.setEnableRotation(true)
- tapl.setExpectedRotation(rotation.value)
tapl.workspace.switchToOverview().dismissAllTasks()
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, rotation)
tapl.goHome()
wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBetweenSplitPairs.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBetweenSplitPairs.kt
index 329d61d..251cb50 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBetweenSplitPairs.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBetweenSplitPairs.kt
@@ -51,8 +51,8 @@
tapl.setEnableRotation(true)
tapl.setExpectedRotation(rotation.value)
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, thirdApp, fourthApp)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, rotation)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, thirdApp, fourthApp, rotation)
SplitScreenUtils.waitForSplitComplete(wmHelper, thirdApp, fourthApp)
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairsNoPip.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairsNoPip.kt
index e59ed64..1387536 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairsNoPip.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairsNoPip.kt
@@ -62,8 +62,10 @@
get() = {
setup {
tapl.goHome()
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, thirdApp, pipApp)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp,
+ secondaryApp, flicker.scenario.startRotation)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, thirdApp, pipApp,
+ flicker.scenario.startRotation)
pipApp.enableAutoEnterForPipActivity()
SplitScreenUtils.waitForSplitComplete(wmHelper, thirdApp, pipApp)
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt
index 4d90070..3b9e53f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt
@@ -39,7 +39,8 @@
protected val popupWindowLayer = ComponentNameMatcher("", "PopupWindow:")
protected val thisTransition: FlickerBuilder.() -> Unit
get() = {
- setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, textEditApp) }
+ setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp,
+ textEditApp, flicker.scenario.startRotation) }
transitions {
SplitScreenUtils.copyContentInSplit(
instrumentation,
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt
index 8360e94..5fdde3a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt
@@ -35,7 +35,8 @@
SplitScreenBase(flicker) {
protected val thisTransition: FlickerBuilder.() -> Unit
get() = {
- setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) }
+ setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp,
+ secondaryApp, flicker.scenario.startRotation) }
transitions {
if (tapl.isTablet) {
SplitScreenUtils.dragDividerToDismissSplit(
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt
index e745878..b7f6bfe 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt
@@ -35,7 +35,10 @@
SplitScreenBase(flicker) {
protected val thisTransition: FlickerBuilder.() -> Unit
get() = {
- setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) }
+ setup {
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp,
+ flicker.scenario.startRotation)
+ }
transitions {
tapl.goHome()
wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt
index c3beb36..bb2a7aa 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt
@@ -37,7 +37,8 @@
SplitScreenBase(flicker) {
protected val thisTransition: FlickerBuilder.() -> Unit
get() = {
- setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) }
+ setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp,
+ secondaryApp, flicker.scenario.startRotation) }
transitions { SplitScreenUtils.dragDividerToResizeAndWait(device, wmHelper) }
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt
index be507d8..5e5e7d7 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt
@@ -46,7 +46,7 @@
.waitForAndVerify()
}
transitions {
- SplitScreenUtils.splitFromOverview(tapl, device)
+ SplitScreenUtils.splitFromOverview(tapl, device, flicker.scenario.startRotation)
SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
index ed0debd..46b0bd2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
@@ -39,7 +39,8 @@
SplitScreenBase(flicker) {
protected val thisTransition: FlickerBuilder.() -> Unit
get() = {
- setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) }
+ setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp,
+ secondaryApp, flicker.scenario.startRotation) }
transitions {
SplitScreenUtils.doubleTapDividerToSwitch(device)
wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt
index 9b7939a..baf7693 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt
@@ -39,7 +39,8 @@
protected val thisTransition: FlickerBuilder.() -> Unit
get() = {
setup {
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp,
+ secondaryApp, flicker.scenario.startRotation)
thirdApp.launchViaIntent(wmHelper)
wmHelper.StateSyncBuilder().withWindowSurfaceAppeared(thirdApp).waitForAndVerify()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt
index 9326ef3..33b55f1 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt
@@ -37,7 +37,8 @@
protected val thisTransition: FlickerBuilder.() -> Unit
get() = {
setup {
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp,
+ secondaryApp, flicker.scenario.startRotation)
tapl.goHome()
wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt
index b928e40..b79dfb5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt
@@ -37,7 +37,8 @@
protected val thisTransition: FlickerBuilder.() -> Unit
get() = {
setup {
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp,
+ secondaryApp, flicker.scenario.startRotation)
tapl.goHome()
wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt
index f314995..0204d75 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt
@@ -39,8 +39,10 @@
protected val thisTransition: FlickerBuilder.() -> Unit
get() = {
setup {
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, thirdApp, fourthApp)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp,
+ secondaryApp, flicker.scenario.startRotation)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, thirdApp, fourthApp,
+ flicker.scenario.startRotation)
SplitScreenUtils.waitForSplitComplete(wmHelper, thirdApp, fourthApp)
}
transitions {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt
index 3f8a1ae..d53adc0 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt
@@ -19,6 +19,7 @@
import android.app.Instrumentation
import android.graphics.Point
import android.os.SystemClock
+import android.tools.common.Rotation
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.common.traces.component.IComponentMatcher
import android.tools.common.traces.component.IComponentNameMatcher
@@ -41,6 +42,7 @@
import com.android.server.wm.flicker.testapp.ActivityOptions
import com.android.server.wm.flicker.testapp.ActivityOptions.SplitScreen.Primary
import org.junit.Assert.assertNotNull
+import android.tools.device.flicker.rules.ChangeDisplayOrientationRule
object SplitScreenUtils {
private const val TIMEOUT_MS = 3_000L
@@ -101,13 +103,15 @@
tapl: LauncherInstrumentation,
device: UiDevice,
primaryApp: StandardAppHelper,
- secondaryApp: StandardAppHelper
+ secondaryApp: StandardAppHelper,
+ rotation: Rotation
) {
primaryApp.launchViaIntent(wmHelper)
secondaryApp.launchViaIntent(wmHelper)
+ ChangeDisplayOrientationRule.setRotation(rotation)
tapl.goHome()
wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
- splitFromOverview(tapl, device)
+ splitFromOverview(tapl, device, rotation)
waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
}
@@ -121,7 +125,7 @@
waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
}
- fun splitFromOverview(tapl: LauncherInstrumentation, device: UiDevice) {
+ fun splitFromOverview(tapl: LauncherInstrumentation, device: UiDevice, rotation: Rotation) {
// Note: The initial split position in landscape is different between tablet and phone.
// In landscape, tablet will let the first app split to right side, and phone will
// split to left side.
@@ -129,7 +133,9 @@
// TAPL's currentTask on tablet is sometimes not what we expected if the overview
// contains more than 3 task views. We need to use uiautomator directly to find the
// second task to split.
- tapl.workspace.switchToOverview().overviewActions.clickSplit()
+ val home = tapl.workspace.switchToOverview()
+ ChangeDisplayOrientationRule.setRotation(rotation)
+ home.overviewActions.clickSplit()
val snapshots = device.wait(Until.findObjects(overviewSnapshotSelector), TIMEOUT_MS)
if (snapshots == null || snapshots.size < 1) {
error("Fail to find a overview snapshot to split.")
@@ -145,9 +151,10 @@
}
snapshots[0].click()
} else {
- tapl.workspace
+ val home = tapl.workspace
.switchToOverview()
- .currentTask
+ ChangeDisplayOrientationRule.setRotation(rotation)
+ home.currentTask
.tapMenu()
.tapSplitMenuItem()
.currentTask
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt
index 9f0d89b..5237585 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt
@@ -27,15 +27,13 @@
import junit.framework.Assert.assertEquals
import org.junit.Before
import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.any
-import org.mockito.ArgumentMatchers.anyInt
-import org.mockito.ArgumentMatchers.anyString
-import org.mockito.ArgumentMatchers.eq
-import org.mockito.Mockito
-import org.mockito.Mockito.mock
-import org.mockito.Mockito.never
-import org.mockito.Mockito.reset
-import org.mockito.Mockito.verify
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.reset
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.verifyNoMoreInteractions
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -66,7 +64,7 @@
@Before
fun setup() {
- launcherApps = mock(LauncherApps::class.java)
+ launcherApps = mock<LauncherApps>()
repository = BubbleVolatileRepository(launcherApps)
}
@@ -98,7 +96,7 @@
repository.addBubbles(user11.identifier, listOf(bubble12))
assertEquals(listOf(bubble11, bubble12), repository.getEntities(user11.identifier))
- Mockito.verifyNoMoreInteractions(launcherApps)
+ verifyNoMoreInteractions(launcherApps)
}
@Test
@@ -167,9 +165,8 @@
assertThat(ret).isTrue() // bubbles were removed
assertThat(repository.getEntities(user0.identifier).toList()).isEmpty()
- verify(launcherApps, never()).uncacheShortcuts(anyString(),
- any(),
- any(UserHandle::class.java), anyInt())
+ verify(launcherApps, never())
+ .uncacheShortcuts(any<String>(), any(), any<UserHandle>(), any<Int>())
}
@Test
@@ -184,9 +181,8 @@
assertThat(repository.getEntities(user0.identifier).toList())
.isEqualTo(listOf(bubble1, bubble3))
- verify(launcherApps, never()).uncacheShortcuts(anyString(),
- any(),
- any(UserHandle::class.java), anyInt())
+ verify(launcherApps, never())
+ .uncacheShortcuts(any<String>(), any(), any<UserHandle>(), any<Int>())
}
@Test
@@ -200,9 +196,8 @@
assertThat(repository.getEntities(user0.identifier).toList())
.isEqualTo(listOf(bubble1, bubble2, bubble3))
- verify(launcherApps, never()).uncacheShortcuts(anyString(),
- any(),
- any(UserHandle::class.java), anyInt())
+ verify(launcherApps, never())
+ .uncacheShortcuts(any<String>(), any(), any<UserHandle>(), any<Int>())
}
@Test
@@ -219,9 +214,8 @@
user11.identifier))
assertThat(ret).isFalse() // bubbles were NOT removed
- verify(launcherApps, never()).uncacheShortcuts(anyString(),
- any(),
- any(UserHandle::class.java), anyInt())
+ verify(launcherApps, never())
+ .uncacheShortcuts(any<String>(), any(), any<UserHandle>(), any<Int>())
}
@Test
@@ -237,9 +231,8 @@
assertThat(ret).isTrue() // bubbles were removed
assertThat(repository.getEntities(user0.identifier).toList()).isEmpty()
- verify(launcherApps, never()).uncacheShortcuts(anyString(),
- any(),
- any(UserHandle::class.java), anyInt())
+ verify(launcherApps, never())
+ .uncacheShortcuts(any<String>(), any(), any<UserHandle>(), any<Int>())
// User 11 bubbles should still be here
assertThat(repository.getEntities(user11.identifier).toList())
@@ -261,9 +254,8 @@
// bubble2 is the work profile bubble and should be removed
assertThat(repository.getEntities(user0.identifier).toList())
.isEqualTo(listOf(bubble1, bubble3))
- verify(launcherApps, never()).uncacheShortcuts(anyString(),
- any(),
- any(UserHandle::class.java), anyInt())
+ verify(launcherApps, never())
+ .uncacheShortcuts(any<String>(), any(), any<UserHandle>(), any<Int>())
// User 11 bubbles should still be here
assertThat(repository.getEntities(user11.identifier).toList())
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index be4a287..c6cccc0 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -52,6 +52,8 @@
import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask
import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFullscreenTask
import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createHomeTask
+import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createSplitScreenTask
+import com.android.wm.shell.splitscreen.SplitScreenController
import com.android.wm.shell.sysui.ShellCommandHandler
import com.android.wm.shell.sysui.ShellController
import com.android.wm.shell.sysui.ShellInit
@@ -94,6 +96,7 @@
ToggleResizeDesktopTaskTransitionHandler
@Mock lateinit var launchAdjacentController: LaunchAdjacentController
@Mock lateinit var desktopModeWindowDecoration: DesktopModeWindowDecoration
+ @Mock lateinit var splitScreenController: SplitScreenController
private lateinit var mockitoSession: StaticMockitoSession
private lateinit var controller: DesktopTasksController
@@ -116,6 +119,7 @@
whenever(transitions.startTransition(anyInt(), any(), isNull())).thenAnswer { Binder() }
controller = createController()
+ controller.splitScreenController = splitScreenController
shellInit.init()
}
@@ -341,6 +345,30 @@
}
@Test
+ fun moveToDesktop_splitTaskExitsSplit() {
+ var task = setUpSplitScreenTask()
+ controller.moveToDesktop(desktopModeWindowDecoration, task)
+ val wct = getLatestMoveToDesktopWct()
+ assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FREEFORM)
+ verify(splitScreenController).prepareExitSplitScreen(any(), anyInt(),
+ eq(SplitScreenController.EXIT_REASON_ENTER_DESKTOP)
+ )
+ }
+
+ @Test
+ fun moveToDesktop_fullscreenTaskDoesNotExitSplit() {
+ var task = setUpFullscreenTask()
+ controller.moveToDesktop(desktopModeWindowDecoration, task)
+ val wct = getLatestMoveToDesktopWct()
+ assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FREEFORM)
+ verify(splitScreenController, never()).prepareExitSplitScreen(any(), anyInt(),
+ eq(SplitScreenController.EXIT_REASON_ENTER_DESKTOP)
+ )
+ }
+
+ @Test
fun moveToFullscreen_displayFullscreen_windowingModeSetToUndefined() {
val task = setUpFreeformTask()
task.configuration.windowConfiguration.displayWindowingMode = WINDOWING_MODE_FULLSCREEN
@@ -695,6 +723,13 @@
return task
}
+ private fun setUpSplitScreenTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
+ val task = createSplitScreenTask(displayId)
+ whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
+ runningTasks.add(task)
+ return task
+ }
+
private fun markTaskVisible(task: RunningTaskInfo) {
desktopModeTaskRepository.updateVisibleFreeformTasks(
task.displayId,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt
index 29a757c..2f6f320 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt
@@ -21,6 +21,7 @@
import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
+import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW
import android.view.Display.DEFAULT_DISPLAY
import com.android.wm.shell.MockToken
import com.android.wm.shell.TestRunningTaskInfoBuilder
@@ -45,12 +46,25 @@
@JvmOverloads
fun createFullscreenTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
return TestRunningTaskInfoBuilder()
- .setDisplayId(displayId)
- .setToken(MockToken().token())
- .setActivityType(ACTIVITY_TYPE_STANDARD)
- .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
- .setLastActiveTime(100)
- .build()
+ .setDisplayId(displayId)
+ .setToken(MockToken().token())
+ .setActivityType(ACTIVITY_TYPE_STANDARD)
+ .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
+ .setLastActiveTime(100)
+ .build()
+ }
+
+ /** Create a task that has windowing mode set to [WINDOWING_MODE_MULTI_WINDOW] */
+ @JvmStatic
+ @JvmOverloads
+ fun createSplitScreenTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
+ return TestRunningTaskInfoBuilder()
+ .setDisplayId(displayId)
+ .setToken(MockToken().token())
+ .setActivityType(ACTIVITY_TYPE_STANDARD)
+ .setWindowingMode(WINDOWING_MODE_MULTI_WINDOW)
+ .setLastActiveTime(100)
+ .build()
}
/** Create a new home task */
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java
deleted file mode 100644
index d8afe68..0000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java
+++ /dev/null
@@ -1,320 +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.wm.shell.windowdecor;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.ActivityManager;
-import android.app.WindowConfiguration;
-import android.graphics.Rect;
-import android.hardware.display.DisplayManager;
-import android.hardware.display.VirtualDisplay;
-import android.hardware.input.InputManager;
-import android.os.Handler;
-import android.os.Looper;
-import android.view.Choreographer;
-import android.view.Display;
-import android.view.InputChannel;
-import android.view.InputMonitor;
-import android.view.SurfaceControl;
-import android.view.SurfaceView;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.ShellTestCase;
-import com.android.wm.shell.TestRunningTaskInfoBuilder;
-import com.android.wm.shell.common.DisplayController;
-import com.android.wm.shell.common.DisplayLayout;
-import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.desktopmode.DesktopTasksController;
-import com.android.wm.shell.sysui.ShellController;
-import com.android.wm.shell.sysui.ShellInit;
-import com.android.wm.shell.transition.Transitions;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mock;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Optional;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.function.Supplier;
-
-/** Tests of {@link DesktopModeWindowDecorViewModel} */
-@SmallTest
-public class DesktopModeWindowDecorViewModelTests extends ShellTestCase {
-
- private static final String TAG = "DesktopModeWindowDecorViewModelTests";
- private static final Rect STABLE_INSETS = new Rect(0, 100, 0, 0);
-
- @Mock private DesktopModeWindowDecoration mDesktopModeWindowDecoration;
- @Mock private DesktopModeWindowDecoration.Factory mDesktopModeWindowDecorFactory;
-
- @Mock private Handler mMainHandler;
- @Mock private Choreographer mMainChoreographer;
- @Mock private ShellTaskOrganizer mTaskOrganizer;
- @Mock private DisplayController mDisplayController;
- @Mock private DisplayLayout mDisplayLayout;
- @Mock private SyncTransactionQueue mSyncQueue;
- @Mock private DesktopTasksController mDesktopTasksController;
- @Mock private InputMonitor mInputMonitor;
- @Mock private InputManager mInputManager;
- @Mock private Transitions mTransitions;
- @Mock private DesktopModeWindowDecorViewModel.InputMonitorFactory mMockInputMonitorFactory;
- @Mock private Supplier<SurfaceControl.Transaction> mTransactionFactory;
- @Mock private SurfaceControl.Transaction mTransaction;
- @Mock private Display mDisplay;
- @Mock private ShellController mShellController;
- @Mock private ShellInit mShellInit;
- @Mock private DesktopModeWindowDecorViewModel.DesktopModeKeyguardChangeListener
- mDesktopModeKeyguardChangeListener;
- @Mock private RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
- private final List<InputManager> mMockInputManagers = new ArrayList<>();
-
- private DesktopModeWindowDecorViewModel mDesktopModeWindowDecorViewModel;
-
- @Before
- public void setUp() {
- mMockInputManagers.add(mInputManager);
-
- mDesktopModeWindowDecorViewModel =
- new DesktopModeWindowDecorViewModel(
- mContext,
- mMainHandler,
- mMainChoreographer,
- mShellInit,
- mTaskOrganizer,
- mDisplayController,
- mShellController,
- mSyncQueue,
- mTransitions,
- Optional.of(mDesktopTasksController),
- mDesktopModeWindowDecorFactory,
- mMockInputMonitorFactory,
- mTransactionFactory,
- mDesktopModeKeyguardChangeListener,
- mRootTaskDisplayAreaOrganizer
- );
-
- doReturn(mDesktopModeWindowDecoration)
- .when(mDesktopModeWindowDecorFactory)
- .create(any(), any(), any(), any(), any(), any(), any(), any(), any());
- doReturn(mTransaction).when(mTransactionFactory).get();
- doReturn(mDisplayLayout).when(mDisplayController).getDisplayLayout(anyInt());
- doReturn(STABLE_INSETS).when(mDisplayLayout).stableInsets();
- doNothing().when(mShellController).addKeyguardChangeListener(any());
-
- when(mMockInputMonitorFactory.create(any(), any())).thenReturn(mInputMonitor);
- // InputChannel cannot be mocked because it passes to InputEventReceiver.
- final InputChannel[] inputChannels = InputChannel.openInputChannelPair(TAG);
- inputChannels[0].dispose();
- when(mInputMonitor.getInputChannel()).thenReturn(inputChannels[1]);
-
- mDesktopModeWindowDecoration.mDisplay = mDisplay;
- doReturn(Display.DEFAULT_DISPLAY).when(mDisplay).getDisplayId();
- }
-
- @Test
- public void testDeleteCaptionOnChangeTransitionWhenNecessary() throws Exception {
- final int taskId = 1;
- final ActivityManager.RunningTaskInfo taskInfo =
- createTaskInfo(taskId, Display.DEFAULT_DISPLAY, WINDOWING_MODE_FREEFORM);
- SurfaceControl surfaceControl = mock(SurfaceControl.class);
- runOnMainThread(() -> {
- final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
- final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
-
- mDesktopModeWindowDecorViewModel.onTaskOpening(
- taskInfo, surfaceControl, startT, finishT);
-
- taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_UNDEFINED);
- taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED);
- mDesktopModeWindowDecorViewModel.onTaskChanging(
- taskInfo, surfaceControl, startT, finishT);
- });
- verify(mDesktopModeWindowDecorFactory)
- .create(
- mContext,
- mDisplayController,
- mTaskOrganizer,
- taskInfo,
- surfaceControl,
- mMainHandler,
- mMainChoreographer,
- mSyncQueue,
- mRootTaskDisplayAreaOrganizer);
- verify(mDesktopModeWindowDecoration).close();
- }
-
- @Test
- public void testCreateCaptionOnChangeTransitionWhenNecessary() throws Exception {
- final int taskId = 1;
- final ActivityManager.RunningTaskInfo taskInfo =
- createTaskInfo(taskId, Display.DEFAULT_DISPLAY, WINDOWING_MODE_UNDEFINED);
- SurfaceControl surfaceControl = mock(SurfaceControl.class);
- runOnMainThread(() -> {
- final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
- final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
- taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED);
-
- mDesktopModeWindowDecorViewModel.onTaskChanging(
- taskInfo, surfaceControl, startT, finishT);
-
- taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
- taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_STANDARD);
-
- mDesktopModeWindowDecorViewModel.onTaskChanging(
- taskInfo, surfaceControl, startT, finishT);
- });
- verify(mDesktopModeWindowDecorFactory, times(1))
- .create(
- mContext,
- mDisplayController,
- mTaskOrganizer,
- taskInfo,
- surfaceControl,
- mMainHandler,
- mMainChoreographer,
- mSyncQueue,
- mRootTaskDisplayAreaOrganizer);
- }
-
- @Test
- public void testCreateAndDisposeEventReceiver() throws Exception {
- final int taskId = 1;
- final ActivityManager.RunningTaskInfo taskInfo =
- createTaskInfo(taskId, Display.DEFAULT_DISPLAY, WINDOWING_MODE_FREEFORM);
- taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_STANDARD);
- runOnMainThread(() -> {
- SurfaceControl surfaceControl = mock(SurfaceControl.class);
- final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
- final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
-
- mDesktopModeWindowDecorViewModel.onTaskOpening(
- taskInfo, surfaceControl, startT, finishT);
-
- mDesktopModeWindowDecorViewModel.destroyWindowDecoration(taskInfo);
- });
- verify(mMockInputMonitorFactory).create(any(), any());
- verify(mInputMonitor).dispose();
- }
-
- @Test
- public void testEventReceiversOnMultipleDisplays() throws Exception {
- runOnMainThread(() -> {
- SurfaceView surfaceView = new SurfaceView(mContext);
- final DisplayManager mDm = mContext.getSystemService(DisplayManager.class);
- final VirtualDisplay secondaryDisplay = mDm.createVirtualDisplay(
- "testEventReceiversOnMultipleDisplays", /*width=*/ 400, /*height=*/ 400,
- /*densityDpi=*/ 320, surfaceView.getHolder().getSurface(),
- DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY);
- try {
- int secondaryDisplayId = secondaryDisplay.getDisplay().getDisplayId();
-
- final int taskId = 1;
- final ActivityManager.RunningTaskInfo taskInfo =
- createTaskInfo(taskId, Display.DEFAULT_DISPLAY, WINDOWING_MODE_FREEFORM);
- final ActivityManager.RunningTaskInfo secondTaskInfo =
- createTaskInfo(taskId + 1, secondaryDisplayId, WINDOWING_MODE_FREEFORM);
- final ActivityManager.RunningTaskInfo thirdTaskInfo =
- createTaskInfo(taskId + 2, secondaryDisplayId, WINDOWING_MODE_FREEFORM);
-
- SurfaceControl surfaceControl = mock(SurfaceControl.class);
- final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
- final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
-
- mDesktopModeWindowDecorViewModel.onTaskOpening(taskInfo, surfaceControl, startT,
- finishT);
- mDesktopModeWindowDecorViewModel.onTaskOpening(secondTaskInfo, surfaceControl,
- startT, finishT);
- mDesktopModeWindowDecorViewModel.onTaskOpening(thirdTaskInfo, surfaceControl,
- startT, finishT);
- mDesktopModeWindowDecorViewModel.destroyWindowDecoration(thirdTaskInfo);
- mDesktopModeWindowDecorViewModel.destroyWindowDecoration(taskInfo);
- } finally {
- secondaryDisplay.release();
- }
- });
- verify(mMockInputMonitorFactory, times(2)).create(any(), any());
- verify(mInputMonitor, times(1)).dispose();
- }
-
- @Test
- public void testCaptionIsNotCreatedWhenKeyguardIsVisible() throws Exception {
- doReturn(true).when(
- mDesktopModeKeyguardChangeListener).isKeyguardVisibleAndOccluded();
-
- final int taskId = 1;
- final ActivityManager.RunningTaskInfo taskInfo =
- createTaskInfo(taskId, Display.DEFAULT_DISPLAY, WINDOWING_MODE_FULLSCREEN);
- taskInfo.isFocused = true;
- SurfaceControl surfaceControl = mock(SurfaceControl.class);
- runOnMainThread(() -> {
- final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
- final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
-
- mDesktopModeWindowDecorViewModel.onTaskOpening(
- taskInfo, surfaceControl, startT, finishT);
-
- taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_UNDEFINED);
- taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED);
- mDesktopModeWindowDecorViewModel.onTaskChanging(
- taskInfo, surfaceControl, startT, finishT);
- });
- verify(mDesktopModeWindowDecorFactory, never())
- .create(any(), any(), any(), any(), any(), any(), any(), any(), any());
- }
-
- private void runOnMainThread(Runnable r) throws Exception {
- final Handler mainHandler = new Handler(Looper.getMainLooper());
- final CountDownLatch latch = new CountDownLatch(1);
- mainHandler.post(() -> {
- r.run();
- latch.countDown();
- });
- latch.await(1, TimeUnit.SECONDS);
- }
-
- private static ActivityManager.RunningTaskInfo createTaskInfo(int taskId,
- int displayId, @WindowConfiguration.WindowingMode int windowingMode) {
- ActivityManager.RunningTaskInfo taskInfo =
- new TestRunningTaskInfoBuilder()
- .setDisplayId(displayId)
- .setVisible(true)
- .build();
- taskInfo.taskId = taskId;
- taskInfo.configuration.windowConfiguration.setWindowingMode(windowingMode);
- return taskInfo;
- }
-}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
new file mode 100644
index 0000000..00d70a7
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
@@ -0,0 +1,344 @@
+/*
+ * 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.wm.shell.windowdecor
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.app.WindowConfiguration
+import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
+import android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED
+import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
+import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
+import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
+import android.graphics.Rect
+import android.hardware.display.DisplayManager
+import android.hardware.display.VirtualDisplay
+import android.os.Handler
+import android.os.IBinder
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import android.view.Choreographer
+import android.view.Display.DEFAULT_DISPLAY
+import android.view.InputChannel
+import android.view.InputMonitor
+import android.view.SurfaceControl
+import android.view.SurfaceView
+import androidx.core.content.getSystemService
+import androidx.test.filters.SmallTest
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.TestRunningTaskInfoBuilder
+import com.android.wm.shell.common.DisplayController
+import com.android.wm.shell.common.DisplayLayout
+import com.android.wm.shell.common.ShellExecutor
+import com.android.wm.shell.common.SyncTransactionQueue
+import com.android.wm.shell.desktopmode.DesktopTasksController
+import com.android.wm.shell.recents.RecentsTransitionHandler
+import com.android.wm.shell.recents.RecentsTransitionStateListener
+import com.android.wm.shell.sysui.KeyguardChangeListener
+import com.android.wm.shell.sysui.ShellController
+import com.android.wm.shell.sysui.ShellInit
+import com.android.wm.shell.transition.Transitions
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.whenever
+import java.util.Optional
+import java.util.function.Supplier
+
+/** Tests of [DesktopModeWindowDecorViewModel] */
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
+ @Mock private lateinit var mockDesktopModeWindowDecorFactory:
+ DesktopModeWindowDecoration.Factory
+ @Mock private lateinit var mockMainHandler: Handler
+ @Mock private lateinit var mockMainChoreographer: Choreographer
+ @Mock private lateinit var mockTaskOrganizer: ShellTaskOrganizer
+ @Mock private lateinit var mockDisplayController: DisplayController
+ @Mock private lateinit var mockDisplayLayout: DisplayLayout
+ @Mock private lateinit var mockSyncQueue: SyncTransactionQueue
+ @Mock private lateinit var mockDesktopTasksController: DesktopTasksController
+ @Mock private lateinit var mockInputMonitor: InputMonitor
+ @Mock private lateinit var mockTransitions: Transitions
+ @Mock private lateinit var mockInputMonitorFactory:
+ DesktopModeWindowDecorViewModel.InputMonitorFactory
+ @Mock private lateinit var mockShellController: ShellController
+ @Mock private lateinit var mockShellExecutor: ShellExecutor
+ @Mock private lateinit var mockRootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
+ @Mock private lateinit var mockRecentsTransitionHandler: RecentsTransitionHandler
+
+ private val transactionFactory = Supplier<SurfaceControl.Transaction> {
+ SurfaceControl.Transaction()
+ }
+
+ private lateinit var shellInit: ShellInit
+ private lateinit var desktopModeWindowDecorViewModel: DesktopModeWindowDecorViewModel
+
+ @Before
+ fun setUp() {
+ shellInit = ShellInit(mockShellExecutor)
+ desktopModeWindowDecorViewModel = DesktopModeWindowDecorViewModel(
+ mContext,
+ mockMainHandler,
+ mockMainChoreographer,
+ shellInit,
+ mockTaskOrganizer,
+ mockDisplayController,
+ mockShellController,
+ mockSyncQueue,
+ mockTransitions,
+ Optional.of(mockDesktopTasksController),
+ mockRecentsTransitionHandler,
+ mockDesktopModeWindowDecorFactory,
+ mockInputMonitorFactory,
+ transactionFactory,
+ mockRootTaskDisplayAreaOrganizer
+ )
+
+ whenever(mockDisplayController.getDisplayLayout(any())).thenReturn(mockDisplayLayout)
+ whenever(mockDisplayLayout.stableInsets()).thenReturn(STABLE_INSETS)
+ whenever(mockInputMonitorFactory.create(any(), any())).thenReturn(mockInputMonitor)
+
+ // InputChannel cannot be mocked because it passes to InputEventReceiver.
+ val inputChannels = InputChannel.openInputChannelPair(TAG)
+ inputChannels.first().dispose()
+ whenever(mockInputMonitor.inputChannel).thenReturn(inputChannels[1])
+
+ shellInit.init()
+ }
+
+ @Test
+ fun testDeleteCaptionOnChangeTransitionWhenNecessary() {
+ val task = createTask(windowingMode = WINDOWING_MODE_FREEFORM)
+ val taskSurface = SurfaceControl()
+ val decoration = setUpMockDecorationForTask(task)
+
+ onTaskOpening(task, taskSurface)
+
+ task.setWindowingMode(WINDOWING_MODE_UNDEFINED)
+ task.setActivityType(ACTIVITY_TYPE_UNDEFINED)
+ onTaskChanging(task, taskSurface)
+
+ verify(mockDesktopModeWindowDecorFactory).create(
+ mContext,
+ mockDisplayController,
+ mockTaskOrganizer,
+ task,
+ taskSurface,
+ mockMainHandler,
+ mockMainChoreographer,
+ mockSyncQueue,
+ mockRootTaskDisplayAreaOrganizer
+ )
+ verify(decoration).close()
+ }
+
+ @Test
+ fun testCreateCaptionOnChangeTransitionWhenNecessary() {
+ val task = createTask(
+ windowingMode = WINDOWING_MODE_UNDEFINED,
+ activityType = ACTIVITY_TYPE_UNDEFINED
+ )
+ val taskSurface = SurfaceControl()
+ setUpMockDecorationForTask(task)
+
+ onTaskChanging(task, taskSurface)
+ verify(mockDesktopModeWindowDecorFactory, never()).create(
+ mContext,
+ mockDisplayController,
+ mockTaskOrganizer,
+ task,
+ taskSurface,
+ mockMainHandler,
+ mockMainChoreographer,
+ mockSyncQueue,
+ mockRootTaskDisplayAreaOrganizer
+ )
+
+ task.setWindowingMode(WINDOWING_MODE_FREEFORM)
+ task.setActivityType(ACTIVITY_TYPE_STANDARD)
+ onTaskChanging(task, taskSurface)
+ verify(mockDesktopModeWindowDecorFactory, times(1)).create(
+ mContext,
+ mockDisplayController,
+ mockTaskOrganizer,
+ task,
+ taskSurface,
+ mockMainHandler,
+ mockMainChoreographer,
+ mockSyncQueue,
+ mockRootTaskDisplayAreaOrganizer
+ )
+ }
+
+ @Test
+ fun testCreateAndDisposeEventReceiver() {
+ val task = createTask(windowingMode = WINDOWING_MODE_FREEFORM)
+ setUpMockDecorationForTask(task)
+
+ onTaskOpening(task)
+ desktopModeWindowDecorViewModel.destroyWindowDecoration(task)
+
+ verify(mockInputMonitorFactory).create(any(), any())
+ verify(mockInputMonitor).dispose()
+ }
+
+ @Test
+ fun testEventReceiversOnMultipleDisplays() {
+ val secondaryDisplay = createVirtualDisplay() ?: return
+ val secondaryDisplayId = secondaryDisplay.display.displayId
+ val task = createTask(displayId = DEFAULT_DISPLAY, windowingMode = WINDOWING_MODE_FREEFORM)
+ val secondTask = createTask(
+ displayId = secondaryDisplayId,
+ windowingMode = WINDOWING_MODE_FREEFORM
+ )
+ val thirdTask = createTask(
+ displayId = secondaryDisplayId,
+ windowingMode = WINDOWING_MODE_FREEFORM
+ )
+ setUpMockDecorationsForTasks(task, secondTask, thirdTask)
+
+ onTaskOpening(task)
+ onTaskOpening(secondTask)
+ onTaskOpening(thirdTask)
+ desktopModeWindowDecorViewModel.destroyWindowDecoration(thirdTask)
+ desktopModeWindowDecorViewModel.destroyWindowDecoration(task)
+ secondaryDisplay.release()
+
+ verify(mockInputMonitorFactory, times(2)).create(any(), any())
+ verify(mockInputMonitor, times(1)).dispose()
+ }
+
+ @Test
+ fun testCaptionIsNotCreatedWhenKeyguardIsVisible() {
+ val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN, focused = true)
+ val keyguardListenerCaptor = argumentCaptor<KeyguardChangeListener>()
+ verify(mockShellController).addKeyguardChangeListener(keyguardListenerCaptor.capture())
+
+ keyguardListenerCaptor.firstValue.onKeyguardVisibilityChanged(
+ true /* visible */,
+ true /* occluded */,
+ false /* animatingDismiss */
+ )
+ onTaskOpening(task)
+
+ task.setWindowingMode(WINDOWING_MODE_UNDEFINED)
+ task.setWindowingMode(ACTIVITY_TYPE_UNDEFINED)
+ onTaskChanging(task)
+
+ verify(mockDesktopModeWindowDecorFactory, never())
+ .create(any(), any(), any(), eq(task), any(), any(), any(), any(), any())
+ }
+
+ @Test
+ fun testRelayoutBlockedDuringRecentsTransition() {
+ val recentsCaptor = argumentCaptor<RecentsTransitionStateListener>()
+ verify(mockRecentsTransitionHandler).addTransitionStateListener(recentsCaptor.capture())
+
+ val transition = mock(IBinder::class.java)
+ val task = createTask(windowingMode = WINDOWING_MODE_FREEFORM)
+ val decoration = setUpMockDecorationForTask(task)
+
+ // Make sure a window decorations exists first by launching a freeform task.
+ onTaskOpening(task)
+ // Now call back when a Recents transition starts.
+ recentsCaptor.firstValue.onTransitionStarted(transition)
+
+ verify(decoration).incrementRelayoutBlock()
+ verify(decoration).addTransitionPausingRelayout(transition)
+ }
+
+ private fun onTaskOpening(task: RunningTaskInfo, leash: SurfaceControl = SurfaceControl()) {
+ desktopModeWindowDecorViewModel.onTaskOpening(
+ task,
+ leash,
+ SurfaceControl.Transaction(),
+ SurfaceControl.Transaction()
+ )
+ }
+
+ private fun onTaskChanging(task: RunningTaskInfo, leash: SurfaceControl = SurfaceControl()) {
+ desktopModeWindowDecorViewModel.onTaskChanging(
+ task,
+ leash,
+ SurfaceControl.Transaction(),
+ SurfaceControl.Transaction()
+ )
+ }
+
+ private fun createTask(
+ displayId: Int = DEFAULT_DISPLAY,
+ @WindowConfiguration.WindowingMode windowingMode: Int,
+ activityType: Int = ACTIVITY_TYPE_STANDARD,
+ focused: Boolean = true
+ ): RunningTaskInfo {
+ return TestRunningTaskInfoBuilder()
+ .setDisplayId(displayId)
+ .setWindowingMode(windowingMode)
+ .setVisible(true)
+ .setActivityType(activityType)
+ .build().apply {
+ isFocused = focused
+ }
+ }
+
+ private fun setUpMockDecorationForTask(task: RunningTaskInfo): DesktopModeWindowDecoration {
+ val decoration = mock(DesktopModeWindowDecoration::class.java)
+ whenever(mockDesktopModeWindowDecorFactory.create(
+ any(), any(), any(), eq(task), any(), any(), any(), any(), any())
+ ).thenReturn(decoration)
+ return decoration
+ }
+
+ private fun setUpMockDecorationsForTasks(vararg tasks: RunningTaskInfo) {
+ tasks.forEach { setUpMockDecorationForTask(it) }
+ }
+
+ private fun createVirtualDisplay(): VirtualDisplay? {
+ val surfaceView = SurfaceView(mContext)
+ return mContext.getSystemService<DisplayManager>()?.createVirtualDisplay(
+ "testEventReceiversOnMultipleDisplays",
+ /*width=*/ 400,
+ /*height=*/ 400,
+ /*densityDpi=*/320,
+ surfaceView.holder.surface,
+ DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
+ )
+ }
+
+ private fun RunningTaskInfo.setWindowingMode(@WindowConfiguration.WindowingMode mode: Int) {
+ configuration.windowConfiguration.windowingMode = mode
+ }
+
+ private fun RunningTaskInfo.setActivityType(type: Int) {
+ configuration.windowConfiguration.activityType = type
+ }
+
+ companion object {
+ private const val TAG = "DesktopModeWindowDecorViewModelTests"
+ private val STABLE_INSETS = Rect(0, 100, 0, 0)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
new file mode 100644
index 0000000..a2dbab1
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
@@ -0,0 +1,132 @@
+/*
+ * 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 static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.res.Configuration;
+import android.os.Handler;
+import android.testing.AndroidTestingRunner;
+import android.view.Choreographer;
+import android.view.Display;
+import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
+import android.window.WindowContainerTransaction;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.TestRunningTaskInfoBuilder;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.SyncTransactionQueue;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+import java.util.function.Supplier;
+
+/**
+ * Tests for {@link DesktopModeWindowDecoration}.
+ *
+ * Build/Install/Run:
+ * atest WMShellUnitTests:DesktopModeWindowDecorationTests
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class DesktopModeWindowDecorationTests extends ShellTestCase {
+ @Mock
+ private DisplayController mMockDisplayController;
+ @Mock
+ private ShellTaskOrganizer mMockShellTaskOrganizer;
+ @Mock
+ private Handler mMockHandler;
+ @Mock
+ private Choreographer mMockChoreographer;
+ @Mock
+ private SyncTransactionQueue mMockSyncQueue;
+ @Mock
+ private RootTaskDisplayAreaOrganizer mMockRootTaskDisplayAreaOrganizer;
+ @Mock
+ private Supplier<SurfaceControl.Transaction> mMockTransactionSupplier;
+ @Mock
+ private SurfaceControl.Transaction mMockTransaction;
+ @Mock
+ private SurfaceControl mMockSurfaceControl;
+ @Mock
+ private SurfaceControlViewHost mMockSurfaceControlViewHost;
+ @Mock
+ private WindowDecoration.SurfaceControlViewHostFactory mMockSurfaceControlViewHostFactory;
+
+ private final Configuration mConfiguration = new Configuration();
+
+ @Before
+ public void setUp() {
+ doReturn(mMockSurfaceControlViewHost).when(mMockSurfaceControlViewHostFactory).create(
+ any(), any(), any());
+ doReturn(mMockTransaction).when(mMockTransactionSupplier).get();
+ }
+
+ @Test
+ public void testMenusClosedWhenTaskIsInvisible() {
+ doReturn(mMockTransaction).when(mMockTransaction).hide(any());
+
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(false /* visible */);
+ final DesktopModeWindowDecoration spyWindowDecor =
+ spy(createWindowDecoration(taskInfo));
+
+ spyWindowDecor.relayout(taskInfo);
+
+ // Menus should close if open before the task being invisible causes relayout to return.
+ verify(spyWindowDecor).closeHandleMenu();
+ verify(spyWindowDecor).closeMaximizeMenu();
+
+ }
+
+ private DesktopModeWindowDecoration createWindowDecoration(
+ ActivityManager.RunningTaskInfo taskInfo) {
+ return new DesktopModeWindowDecoration(mContext, mMockDisplayController,
+ mMockShellTaskOrganizer, taskInfo, mMockSurfaceControl, mConfiguration,
+ mMockHandler, mMockChoreographer, mMockSyncQueue, mMockRootTaskDisplayAreaOrganizer,
+ SurfaceControl.Builder::new, mMockTransactionSupplier,
+ WindowContainerTransaction::new, mMockSurfaceControlViewHostFactory);
+ }
+
+ private ActivityManager.RunningTaskInfo createTaskInfo(boolean visible) {
+ final ActivityManager.TaskDescription.Builder taskDescriptionBuilder =
+ new ActivityManager.TaskDescription.Builder();
+ ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
+ .setDisplayId(Display.DEFAULT_DISPLAY)
+ .setTaskDescriptionBuilder(taskDescriptionBuilder)
+ .setVisible(visible)
+ .build();
+ taskInfo.realActivity = new ComponentName("com.android.wm.shell.windowdecor",
+ "DesktopModeWindowDecorationTests");
+ taskInfo.baseActivity = new ComponentName("com.android.wm.shell.windowdecor",
+ "DesktopModeWindowDecorationTests");
+ return taskInfo;
+
+ }
+}
diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp
index 735fc07..30d4612 100644
--- a/libs/hwui/renderthread/CacheManager.cpp
+++ b/libs/hwui/renderthread/CacheManager.cpp
@@ -169,7 +169,8 @@
return;
}
mGrContext->flushAndSubmit();
- mGrContext->purgeResourcesNotUsedInMs(std::chrono::seconds(30));
+ mGrContext->performDeferredCleanup(std::chrono::seconds(30),
+ GrPurgeResourceOptions::kAllResources);
}
void CacheManager::getMemoryUsage(size_t* cpuUsage, size_t* gpuUsage) {
diff --git a/media/java/android/media/tv/tuner/Lnb.java b/media/java/android/media/tv/tuner/Lnb.java
index 3b70890..f9eaabd 100644
--- a/media/java/android/media/tv/tuner/Lnb.java
+++ b/media/java/android/media/tv/tuner/Lnb.java
@@ -25,6 +25,8 @@
import android.hardware.tv.tuner.LnbTone;
import android.hardware.tv.tuner.LnbVoltage;
import android.media.tv.tuner.Tuner.Result;
+import android.media.tv.tunerresourcemanager.TunerResourceManager;
+import android.util.Log;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -147,10 +149,13 @@
public static final int EVENT_TYPE_LNB_OVERLOAD = LnbEventType.LNB_OVERLOAD;
private static final String TAG = "Lnb";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
Map<LnbCallback, Executor> mCallbackMap =
new HashMap<LnbCallback, Executor>();
Tuner mOwner;
+ TunerResourceManager mTunerResourceManager;
+ int mClientId;
private final Object mCallbackLock = new Object();
@@ -174,6 +179,10 @@
}
}
setOwner(tuner);
+ if (mOwner != null) {
+ mTunerResourceManager = mOwner.getTunerResourceManager();
+ mClientId = mOwner.getClientId();
+ }
}
/**
@@ -210,6 +219,8 @@
Objects.requireNonNull(newOwner, "newOwner must not be null");
synchronized (mLock) {
mOwner = newOwner;
+ mTunerResourceManager = newOwner.getTunerResourceManager();
+ mClientId = newOwner.getClientId();
}
}
@@ -317,21 +328,42 @@
* Releases the LNB instance.
*/
public void close() {
- synchronized (mLock) {
- if (mIsClosed) {
- return;
- }
- int res = nativeClose();
- if (res != Tuner.RESULT_SUCCESS) {
- TunerUtils.throwExceptionForResult(res, "Failed to close LNB");
- } else {
- mIsClosed = true;
- if (mOwner != null) {
- mOwner.releaseLnb();
- mOwner = null;
+ acquireTRMSLock("close()");
+ try {
+ synchronized (mLock) {
+ if (mIsClosed) {
+ return;
}
- mCallbackMap.clear();
+ int res = nativeClose();
+ if (res != Tuner.RESULT_SUCCESS) {
+ TunerUtils.throwExceptionForResult(res, "Failed to close LNB");
+ } else {
+ mIsClosed = true;
+ if (mOwner != null) {
+ mOwner.releaseLnb();
+ mOwner = null;
+ }
+ mCallbackMap.clear();
+ }
}
+ } finally {
+ releaseTRMSLock();
}
}
+
+ private void acquireTRMSLock(String functionNameForLog) {
+ if (DEBUG) {
+ Log.d(TAG, "ATTEMPT:acquireLock() in " + functionNameForLog
+ + "for clientId:" + mClientId);
+ }
+ if (!mTunerResourceManager.acquireLock(mClientId)) {
+ Log.e(TAG, "FAILED:acquireLock() in " + functionNameForLog
+ + " for clientId:" + mClientId + " - this can cause deadlock between"
+ + " Tuner API calls and onReclaimResources()");
+ }
+ }
+
+ private void releaseTRMSLock() {
+ mTunerResourceManager.releaseLock(mClientId);
+ }
}
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index f87e47e..9924fae 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -903,6 +903,9 @@
}
}
+ /**
+ * Releases Lnb resource if held. TRMS lock must be acquired prior to calling this function.
+ */
private void closeLnb() {
mLnbLock.lock();
try {
@@ -2806,6 +2809,10 @@
return mClientId;
}
+ /* package */ TunerResourceManager getTunerResourceManager() {
+ return mTunerResourceManager;
+ }
+
private void acquireTRMSLock(String functionNameForLog) {
if (DEBUG) {
Log.d(TAG, "ATTEMPT:acquireLock() in " + functionNameForLog
diff --git a/packages/CredentialManager/Android.bp b/packages/CredentialManager/Android.bp
index 233aee2..fe26dc3 100644
--- a/packages/CredentialManager/Android.bp
+++ b/packages/CredentialManager/Android.bp
@@ -50,46 +50,3 @@
proguard_compatibility: false,
},
}
-
-android_app {
- name: "ClockworkCredentialManager",
- defaults: ["platform_app_defaults"],
- certificate: "platform",
- manifest: "wear/AndroidManifest.xml",
- srcs: ["wear/src/**/*.kt"],
- resource_dirs: ["wear/res"],
-
- dex_preopt: {
- profile_guided: true,
- profile: "wear/profile.txt.prof",
- },
-
- static_libs: [
- "PlatformComposeCore",
- "androidx.activity_activity-compose",
- "androidx.appcompat_appcompat",
- "androidx.compose.foundation_foundation",
- "androidx.compose.foundation_foundation-layout",
- "androidx.compose.material_material-icons-core",
- "androidx.compose.material_material-icons-extended",
- "androidx.compose.ui_ui",
- "androidx.core_core-ktx",
- "androidx.credentials_credentials",
- "androidx.lifecycle_lifecycle-extensions",
- "androidx.lifecycle_lifecycle-livedata",
- "androidx.lifecycle_lifecycle-runtime-ktx",
- "androidx.lifecycle_lifecycle-viewmodel-compose",
- "androidx.wear.compose_compose-foundation",
- "androidx.wear.compose_compose-material",
- "kotlinx-coroutines-core",
- ],
-
- platform_apis: true,
- privileged: true,
-
- kotlincflags: ["-Xjvm-default=all"],
-
- optimize: {
- proguard_compatibility: false,
- },
-}
diff --git a/packages/CredentialManager/AndroidManifest.xml b/packages/CredentialManager/AndroidManifest.xml
index 4161601..a5ccdb6 100644
--- a/packages/CredentialManager/AndroidManifest.xml
+++ b/packages/CredentialManager/AndroidManifest.xml
@@ -17,31 +17,43 @@
*/
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.credentialmanager">
+ package="com.android.credentialmanager">
<uses-permission android:name="android.permission.LAUNCH_CREDENTIAL_SELECTOR"/>
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
<uses-permission android:name="android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS"/>
+ <uses-permission android:name="android.permission.ACCESS_INSTANT_APPS" />
<application
- android:allowBackup="true"
- android:dataExtractionRules="@xml/data_extraction_rules"
- android:fullBackupContent="@xml/backup_rules"
- android:icon="@mipmap/ic_launcher"
- android:label="@string/app_name"
- android:roundIcon="@mipmap/ic_launcher_round"
- android:supportsRtl="true"
- android:theme="@style/Theme.CredentialSelector">
-
- <activity
- android:name=".CredentialSelectorActivity"
- android:exported="true"
- android:permission="android.permission.LAUNCH_CREDENTIAL_SELECTOR"
- android:launchMode="singleTop"
+ android:allowBackup="true"
+ android:dataExtractionRules="@xml/data_extraction_rules"
+ android:fullBackupContent="@xml/backup_rules"
+ android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
- android:excludeFromRecents="true"
+ android:roundIcon="@mipmap/ic_launcher_round"
+ android:supportsRtl="true"
android:theme="@style/Theme.CredentialSelector">
- </activity>
- </application>
+
+ <activity
+ android:name=".CredentialSelectorActivity"
+ android:exported="true"
+ android:permission="android.permission.LAUNCH_CREDENTIAL_SELECTOR"
+ android:launchMode="singleTop"
+ android:label="@string/app_name"
+ android:excludeFromRecents="true"
+ android:theme="@style/Theme.CredentialSelector">
+ </activity>
+ <service
+ android:name=".autofill.CredentialAutofillService"
+ android:exported="false"
+ android:permission="android.permission.BIND_AUTOFILL_SERVICE">
+ <meta-data
+ android:name="android.autofill"
+ android:resource="@xml/autofill_service_configuration"/>
+ <intent-filter>
+ <action android:name="android.service.autofill.AutofillService"/>
+ </intent-filter>
+ </service>
+ </application>
</manifest>
diff --git a/packages/CredentialManager/horologist/Android.bp b/packages/CredentialManager/horologist/Android.bp
new file mode 100644
index 0000000..bb324bb
--- /dev/null
+++ b/packages/CredentialManager/horologist/Android.bp
@@ -0,0 +1,27 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+// TODO: ag/24733147 - Remove this project once it is imported.
+android_library {
+ name: "Horologist",
+ manifest: "AndroidManifest.xml",
+ srcs: ["src/**/*.kt"],
+ static_libs: [
+ "androidx.compose.foundation_foundation",
+ "androidx.compose.runtime_runtime",
+ "androidx.compose.ui_ui",
+ "androidx.navigation_navigation-compose",
+ "androidx.lifecycle_lifecycle-extensions",
+ "androidx.lifecycle_lifecycle-runtime-ktx",
+ "androidx.lifecycle_lifecycle-viewmodel-compose",
+ "androidx.wear.compose_compose-foundation",
+ "androidx.wear.compose_compose-material",
+ "androidx.wear.compose_compose-navigation",
+ ],
+}
diff --git a/packages/CredentialManager/horologist/AndroidManifest.xml b/packages/CredentialManager/horologist/AndroidManifest.xml
new file mode 100644
index 0000000..e386ce2
--- /dev/null
+++ b/packages/CredentialManager/horologist/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (c) 2023 Google Inc.
+ *
+ * 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"
+ package="com.google.android.horologist">
+
+ <uses-feature android:name="android.hardware.type.watch" />
+
+</manifest>
diff --git a/packages/CredentialManager/horologist/src/com/google/android/horologist/annotations/ExperimentalHorologistApi.kt b/packages/CredentialManager/horologist/src/com/google/android/horologist/annotations/ExperimentalHorologistApi.kt
new file mode 100644
index 0000000..ae77605
--- /dev/null
+++ b/packages/CredentialManager/horologist/src/com/google/android/horologist/annotations/ExperimentalHorologistApi.kt
@@ -0,0 +1,23 @@
+/*
+ * 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
+ *
+ * https://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.google.android.horologist.annotations
+
+@RequiresOptIn(
+ message = "Horologist API is experimental. The API may be changed in the future.",
+)
+@Retention(AnnotationRetention.BINARY)
+public annotation class ExperimentalHorologistApi
diff --git a/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/layout/ScalingLazyColumnDefaults.kt b/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/layout/ScalingLazyColumnDefaults.kt
new file mode 100644
index 0000000..c88bbd8
--- /dev/null
+++ b/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/layout/ScalingLazyColumnDefaults.kt
@@ -0,0 +1,131 @@
+/*
+ * Copyright 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
+ *
+ * https://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.
+ */
+
+@file:Suppress("ObjectLiteralToLambda")
+
+package com.google.android.horologist.compose.layout
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.platform.LocalConfiguration
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import androidx.wear.compose.foundation.lazy.AutoCenteringParams
+import androidx.wear.compose.foundation.lazy.ScalingLazyListAnchorType
+import com.google.android.horologist.annotations.ExperimentalHorologistApi
+import com.google.android.horologist.compose.layout.ScalingLazyColumnState.RotaryMode
+
+/**
+ * Default layouts for ScalingLazyColumnState, based on UX guidance.
+ */
+public object ScalingLazyColumnDefaults {
+ /**
+ * Layout the first item, directly under the time text.
+ * This is positioned from the top of the screen instead of the
+ * center.
+ */
+ @ExperimentalHorologistApi
+ public fun belowTimeText(
+ rotaryMode: RotaryMode = RotaryMode.Scroll,
+ firstItemIsFullWidth: Boolean = false,
+ verticalArrangement: Arrangement.Vertical =
+ Arrangement.spacedBy(
+ space = 4.dp,
+ alignment = Alignment.Top,
+ ),
+ horizontalAlignment: Alignment.Horizontal = Alignment.CenterHorizontally,
+ contentPadding: PaddingValues = PaddingValues(horizontal = 10.dp),
+ topPaddingDp: Dp = 32.dp + (if (firstItemIsFullWidth) 20.dp else 0.dp),
+ ): ScalingLazyColumnState.Factory {
+ return object : ScalingLazyColumnState.Factory {
+ @Composable
+ override fun create(): ScalingLazyColumnState {
+ val density = LocalDensity.current
+ val configuration = LocalConfiguration.current
+
+ return remember {
+ val screenHeightPx =
+ with(density) { configuration.screenHeightDp.dp.roundToPx() }
+ val topPaddingPx = with(density) { topPaddingDp.roundToPx() }
+ val topScreenOffsetPx = screenHeightPx / 2 - topPaddingPx
+
+ ScalingLazyColumnState(
+ initialScrollPosition = ScalingLazyColumnState.ScrollPosition(
+ index = 0,
+ offsetPx = topScreenOffsetPx,
+ ),
+ anchorType = ScalingLazyListAnchorType.ItemStart,
+ rotaryMode = rotaryMode,
+ verticalArrangement = verticalArrangement,
+ horizontalAlignment = horizontalAlignment,
+ contentPadding = contentPadding,
+ )
+ }
+ }
+ }
+ }
+
+ /**
+ * Layout the item [initialCenterIndex] at [initialCenterOffset] from the
+ * center of the screen.
+ */
+ @ExperimentalHorologistApi
+ public fun scalingLazyColumnDefaults(
+ rotaryMode: RotaryMode = RotaryMode.Scroll,
+ initialCenterIndex: Int = 1,
+ initialCenterOffset: Int = 0,
+ verticalArrangement: Arrangement.Vertical =
+ Arrangement.spacedBy(
+ space = 4.dp,
+ alignment = Alignment.Top,
+ ),
+ horizontalAlignment: Alignment.Horizontal = Alignment.CenterHorizontally,
+ contentPadding: PaddingValues = PaddingValues(horizontal = 10.dp),
+ autoCentering: AutoCenteringParams? = AutoCenteringParams(
+ initialCenterIndex,
+ initialCenterOffset,
+ ),
+ anchorType: ScalingLazyListAnchorType = ScalingLazyListAnchorType.ItemCenter,
+ hapticsEnabled: Boolean = true,
+ reverseLayout: Boolean = false,
+ ): ScalingLazyColumnState.Factory {
+ return object : ScalingLazyColumnState.Factory {
+ @Composable
+ override fun create(): ScalingLazyColumnState {
+ return remember {
+ ScalingLazyColumnState(
+ initialScrollPosition = ScalingLazyColumnState.ScrollPosition(
+ index = initialCenterIndex,
+ offsetPx = initialCenterOffset,
+ ),
+ rotaryMode = rotaryMode,
+ verticalArrangement = verticalArrangement,
+ horizontalAlignment = horizontalAlignment,
+ contentPadding = contentPadding,
+ autoCentering = autoCentering,
+ anchorType = anchorType,
+ hapticsEnabled = hapticsEnabled,
+ reverseLayout = reverseLayout,
+ )
+ }
+ }
+ }
+ }
+}
diff --git a/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/layout/ScalingLazyColumnState.kt b/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/layout/ScalingLazyColumnState.kt
new file mode 100644
index 0000000..3a12b9f
--- /dev/null
+++ b/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/layout/ScalingLazyColumnState.kt
@@ -0,0 +1,168 @@
+/*
+ * Copyright 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
+ *
+ * https://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.
+ */
+
+@file:Suppress("ObjectLiteralToLambda")
+@file:OptIn(ExperimentalHorologistApi::class, ExperimentalWearFoundationApi::class)
+
+package com.google.android.horologist.compose.layout
+
+import androidx.compose.foundation.gestures.FlingBehavior
+import androidx.compose.foundation.gestures.ScrollableDefaults
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import androidx.wear.compose.foundation.ExperimentalWearFoundationApi
+import androidx.wear.compose.foundation.lazy.AutoCenteringParams
+import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
+import androidx.wear.compose.foundation.lazy.ScalingLazyListAnchorType
+import androidx.wear.compose.foundation.lazy.ScalingLazyListScope
+import androidx.wear.compose.foundation.lazy.ScalingLazyListState
+import androidx.wear.compose.foundation.lazy.ScalingParams
+import androidx.wear.compose.foundation.rememberActiveFocusRequester
+import com.google.android.horologist.annotations.ExperimentalHorologistApi
+import com.google.android.horologist.compose.layout.ScalingLazyColumnState.RotaryMode
+import com.google.android.horologist.compose.rotaryinput.rememberDisabledHaptic
+import com.google.android.horologist.compose.rotaryinput.rememberRotaryHapticHandler
+import com.google.android.horologist.compose.rotaryinput.rotaryWithScroll
+import com.google.android.horologist.compose.rotaryinput.rotaryWithSnap
+import com.google.android.horologist.compose.rotaryinput.toRotaryScrollAdapter
+import androidx.wear.compose.foundation.lazy.ScalingLazyColumnDefaults as WearScalingLazyColumnDefaults
+
+/**
+ * A Config and State object wrapping up all configuration for a [ScalingLazyColumn].
+ * This allows defaults such as [ScalingLazyColumnDefaults.belowTimeText].
+ */
+@ExperimentalHorologistApi
+public class ScalingLazyColumnState(
+ public val initialScrollPosition: ScrollPosition = ScrollPosition(1, 0),
+ public val autoCentering: AutoCenteringParams? = AutoCenteringParams(
+ initialScrollPosition.index,
+ initialScrollPosition.offsetPx,
+ ),
+ public val anchorType: ScalingLazyListAnchorType = ScalingLazyListAnchorType.ItemCenter,
+ public val contentPadding: PaddingValues = PaddingValues(horizontal = 10.dp),
+ public val rotaryMode: RotaryMode = RotaryMode.Scroll,
+ public val reverseLayout: Boolean = false,
+ public val verticalArrangement: Arrangement.Vertical =
+ Arrangement.spacedBy(
+ space = 4.dp,
+ alignment = if (!reverseLayout) Alignment.Top else Alignment.Bottom,
+ ),
+ public val horizontalAlignment: Alignment.Horizontal = Alignment.CenterHorizontally,
+ public val flingBehavior: FlingBehavior? = null,
+ public val userScrollEnabled: Boolean = true,
+ public val scalingParams: ScalingParams = WearScalingLazyColumnDefaults.scalingParams(),
+ public val hapticsEnabled: Boolean = true,
+) {
+ private var _state: ScalingLazyListState? = null
+ public var state: ScalingLazyListState
+ get() {
+ if (_state == null) {
+ _state = ScalingLazyListState(
+ initialScrollPosition.index,
+ initialScrollPosition.offsetPx,
+ )
+ }
+ return _state!!
+ }
+ set(value) {
+ _state = value
+ }
+
+ public sealed interface RotaryMode {
+ public object Snap : RotaryMode
+ public object Scroll : RotaryMode
+
+ @Deprecated(
+ "Use RotaryMode.Scroll instead",
+ replaceWith = ReplaceWith("RotaryMode.Scroll"),
+ )
+ public object Fling : RotaryMode
+ }
+
+ public data class ScrollPosition(
+ val index: Int,
+ val offsetPx: Int,
+ )
+
+ public fun interface Factory {
+ @Composable
+ public fun create(): ScalingLazyColumnState
+ }
+}
+
+@Composable
+public fun rememberColumnState(
+ factory: ScalingLazyColumnState.Factory = ScalingLazyColumnDefaults.belowTimeText(),
+): ScalingLazyColumnState {
+ val columnState = factory.create()
+
+ columnState.state = rememberSaveable(saver = ScalingLazyListState.Saver) {
+ columnState.state
+ }
+
+ return columnState
+}
+
+@ExperimentalHorologistApi
+@Composable
+public fun ScalingLazyColumn(
+ columnState: ScalingLazyColumnState,
+ modifier: Modifier = Modifier,
+ content: ScalingLazyListScope.() -> Unit,
+) {
+ val focusRequester = rememberActiveFocusRequester()
+
+ val rotaryHaptics = if (columnState.hapticsEnabled) {
+ rememberRotaryHapticHandler(columnState.state)
+ } else {
+ rememberDisabledHaptic()
+ }
+ val modifierWithRotary = when (columnState.rotaryMode) {
+ RotaryMode.Snap -> modifier.rotaryWithSnap(
+ focusRequester = focusRequester,
+ rotaryScrollAdapter = columnState.state.toRotaryScrollAdapter(),
+ reverseDirection = columnState.reverseLayout,
+ rotaryHaptics = rotaryHaptics,
+ )
+
+ else -> modifier.rotaryWithScroll(
+ focusRequester = focusRequester,
+ scrollableState = columnState.state,
+ reverseDirection = columnState.reverseLayout,
+ rotaryHaptics = rotaryHaptics,
+ )
+ }
+
+ ScalingLazyColumn(
+ modifier = modifierWithRotary,
+ state = columnState.state,
+ contentPadding = columnState.contentPadding,
+ reverseLayout = columnState.reverseLayout,
+ verticalArrangement = columnState.verticalArrangement,
+ horizontalAlignment = columnState.horizontalAlignment,
+ flingBehavior = columnState.flingBehavior ?: ScrollableDefaults.flingBehavior(),
+ userScrollEnabled = columnState.userScrollEnabled,
+ scalingParams = columnState.scalingParams,
+ anchorType = columnState.anchorType,
+ autoCentering = columnState.autoCentering,
+ content = content,
+ )
+}
diff --git a/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/layout/ScrollAway.kt b/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/layout/ScrollAway.kt
new file mode 100644
index 0000000..623ae1a
--- /dev/null
+++ b/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/layout/ScrollAway.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright 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
+ *
+ * https://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.google.android.horologist.compose.layout
+
+import androidx.compose.foundation.ScrollState
+import androidx.compose.foundation.gestures.ScrollableState
+import androidx.compose.foundation.lazy.LazyListState
+import androidx.compose.runtime.State
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.composed
+import androidx.compose.ui.layout.layout
+import androidx.compose.ui.platform.LocalDensity
+import androidx.wear.compose.foundation.lazy.ScalingLazyListState
+import androidx.wear.compose.material.scrollAway
+import com.google.android.horologist.compose.navscaffold.ScalingLazyColumnScrollableState
+
+internal fun Modifier.scrollAway(
+ scrollState: State<ScrollableState?>,
+): Modifier = composed {
+ when (val state = scrollState.value) {
+ is ScalingLazyColumnScrollableState -> {
+ val offsetDp = with(LocalDensity.current) {
+ state.initialOffsetPx.toDp()
+ }
+ this.scrollAway(state.scalingLazyListState, state.initialIndex, offsetDp)
+ }
+ is ScalingLazyListState -> this.scrollAway(state)
+ is LazyListState -> this.scrollAway(state)
+ is ScrollState -> this.scrollAway(state)
+ // Disabled
+ null -> this.hidden()
+ // Enabled but no scroll state
+ else -> this
+ }
+}
+
+internal fun Modifier.hidden(): Modifier = layout { _, _ -> layout(0, 0) {} }
diff --git a/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/navscaffold/NavScaffoldViewModel.kt b/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/navscaffold/NavScaffoldViewModel.kt
new file mode 100644
index 0000000..14c0ba1
--- /dev/null
+++ b/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/navscaffold/NavScaffoldViewModel.kt
@@ -0,0 +1,297 @@
+/*
+ * Copyright 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
+ *
+ * https://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.
+ */
+
+@file:OptIn(ExperimentalHorologistApi::class, SavedStateHandleSaveableApi::class)
+
+package com.google.android.horologist.compose.navscaffold
+
+import android.os.Bundle
+import androidx.compose.foundation.ScrollState
+import androidx.compose.foundation.gestures.ScrollableState
+import androidx.compose.foundation.lazy.LazyListState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.lifecycle.SavedStateHandle
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewmodel.compose.SavedStateHandleSaveableApi
+import androidx.lifecycle.viewmodel.compose.saveable
+import androidx.navigation.NavBackStackEntry
+import androidx.navigation.NavHostController
+import androidx.wear.compose.foundation.lazy.ScalingLazyListState
+import androidx.wear.compose.material.PositionIndicator
+import androidx.wear.compose.material.Scaffold
+import androidx.wear.compose.material.TimeText
+import androidx.wear.compose.material.Vignette
+import androidx.wear.compose.material.VignettePosition
+import com.google.android.horologist.annotations.ExperimentalHorologistApi
+import com.google.android.horologist.compose.layout.ScalingLazyColumnState
+import com.google.android.horologist.compose.navscaffold.NavScaffoldViewModel.PositionIndicatorMode
+import com.google.android.horologist.compose.navscaffold.NavScaffoldViewModel.TimeTextMode
+import com.google.android.horologist.compose.navscaffold.NavScaffoldViewModel.TimeTextMode.ScrollAway
+import com.google.android.horologist.compose.navscaffold.NavScaffoldViewModel.VignetteMode.Off
+import com.google.android.horologist.compose.navscaffold.NavScaffoldViewModel.VignetteMode.On
+import com.google.android.horologist.compose.navscaffold.NavScaffoldViewModel.VignetteMode.WhenScrollable
+
+/**
+ * A ViewModel that backs the WearNavScaffold to allow each composable to interact and effect
+ * the [Scaffold] positionIndicator, vignette and timeText.
+ *
+ * A ViewModel is used to allow the same current instance to be shared between the WearNavScaffold
+ * and the composable screen via [NavHostController.currentBackStackEntry].
+ */
+public open class NavScaffoldViewModel(
+ private val savedStateHandle: SavedStateHandle,
+) : ViewModel() {
+ internal var initialIndex: Int? = null
+ internal var initialOffsetPx: Int? = null
+ internal var scrollType by mutableStateOf<ScrollType?>(null)
+
+ private lateinit var _scrollableState: ScrollableState
+
+ /**
+ * Returns the scrollable state for this composable or null if the scaffold should
+ * not consider this element to be scrollable.
+ */
+ public val scrollableState: ScrollableState?
+ get() = if (scrollType == null || scrollType == ScrollType.None) {
+ null
+ } else {
+ _scrollableState
+ }
+
+ /**
+ * The configuration of [Vignette], [WhenScrollable], [Off], [On] and if so whether top and
+ * bottom. Defaults to on for scrollable screens.
+ */
+ public var vignettePosition: VignetteMode by mutableStateOf(WhenScrollable)
+
+ /**
+ * The configuration of [TimeText], defaults to [TimeTextMode.ScrollAway] which will move the
+ * time text above the screen to avoid overlapping with the content moving up.
+ */
+ public var timeTextMode: TimeTextMode by mutableStateOf(ScrollAway)
+
+ /**
+ * The configuration of [PositionIndicator]. The default is to show a scroll bar while the
+ * scroll is in progress.
+ */
+ public var positionIndicatorMode: PositionIndicatorMode
+ by mutableStateOf(PositionIndicatorMode.On)
+
+ internal fun initializeScrollState(scrollStateBuilder: () -> ScrollState): ScrollState {
+ check(scrollType == null || scrollType == ScrollType.ScrollState)
+
+ if (scrollType == null) {
+ scrollType = ScrollType.ScrollState
+
+ _scrollableState = savedStateHandle.saveable(
+ key = "navScaffold.ScrollState",
+ saver = ScrollState.Saver,
+ ) {
+ scrollStateBuilder()
+ }
+ }
+
+ return _scrollableState as ScrollState
+ }
+
+ internal fun initializeScalingLazyListState(
+ scrollableStateBuilder: () -> ScalingLazyListState,
+ ): ScalingLazyListState {
+ check(scrollType == null || scrollType == ScrollType.ScalingLazyColumn)
+
+ if (scrollType == null) {
+ scrollType = ScrollType.ScalingLazyColumn
+
+ _scrollableState = savedStateHandle.saveable(
+ key = "navScaffold.ScalingLazyListState",
+ saver = ScalingLazyListState.Saver,
+ ) {
+ scrollableStateBuilder().also {
+ initialIndex = it.centerItemIndex
+ initialOffsetPx = it.centerItemScrollOffset
+ }
+ }
+ }
+
+ return _scrollableState as ScalingLazyListState
+ }
+
+ internal fun initializeScalingLazyListState(
+ columnState: ScalingLazyColumnState,
+ ) {
+ check(scrollType == null || scrollType == ScrollType.ScalingLazyColumn)
+
+ if (scrollType == null) {
+ scrollType = ScrollType.ScalingLazyColumn
+
+ initialIndex = columnState.initialScrollPosition.index
+ initialOffsetPx = columnState.initialScrollPosition.offsetPx
+
+ _scrollableState = savedStateHandle.saveable(
+ key = "navScaffold.ScalingLazyListState",
+ saver = ScalingLazyListState.Saver,
+ ) {
+ columnState.state
+ }
+ }
+
+ columnState.state = _scrollableState as ScalingLazyListState
+ }
+
+ internal fun initializeLazyList(
+ scrollableStateBuilder: () -> LazyListState,
+ ): LazyListState {
+ check(scrollType == null || scrollType == ScrollType.LazyList)
+
+ if (scrollType == null) {
+ scrollType = ScrollType.LazyList
+
+ _scrollableState = savedStateHandle.saveable(
+ key = "navScaffold.LazyListState",
+ saver = LazyListState.Saver,
+ ) {
+ scrollableStateBuilder()
+ }
+ }
+
+ return _scrollableState as LazyListState
+ }
+
+ internal enum class ScrollType {
+ None, ScalingLazyColumn, ScrollState, LazyList
+ }
+
+ /**
+ * The configuration of [TimeText], defaults to [ScrollAway] which will move the time text above the
+ * screen to avoid overlapping with the content moving up.
+ */
+ public enum class TimeTextMode {
+ On, Off, ScrollAway
+ }
+
+ /**
+ * The configuration of [PositionIndicator]. The default is to show a scroll bar while the
+ * scroll is in progress.
+ */
+ public enum class PositionIndicatorMode {
+ On, Off
+ }
+
+ /**
+ * The configuration of [Vignette], [WhenScrollable], [Off], [On] and if so whether top and
+ * bottom. Defaults to on for scrollable screens.
+ */
+ public sealed interface VignetteMode {
+ public object WhenScrollable : VignetteMode
+ public object Off : VignetteMode
+ public data class On(val position: VignettePosition) : VignetteMode
+ }
+
+ internal fun timeTextScrollableState(): ScrollableState? {
+ return when (timeTextMode) {
+ ScrollAway -> {
+ when (this.scrollType) {
+ ScrollType.ScrollState -> {
+ this.scrollableState as ScrollState
+ }
+
+ ScrollType.ScalingLazyColumn -> {
+ val scalingLazyListState =
+ this.scrollableState as ScalingLazyListState
+
+ ScalingLazyColumnScrollableState(scalingLazyListState, initialIndex
+ ?: 1, initialOffsetPx ?: 0)
+ }
+
+ ScrollType.LazyList -> {
+ this.scrollableState as LazyListState
+ }
+
+ else -> {
+ ScrollState(0)
+ }
+ }
+ }
+
+ TimeTextMode.On -> {
+ ScrollState(0)
+ }
+
+ else -> {
+ null
+ }
+ }
+ }
+}
+
+internal class ScalingLazyColumnScrollableState(
+ val scalingLazyListState: ScalingLazyListState,
+ val initialIndex: Int,
+ val initialOffsetPx: Int,
+) : ScrollableState by scalingLazyListState
+
+/**
+ * The context items provided to a navigation composable.
+ *
+ * The [viewModel] can be used to customise the scaffold behaviour.
+ */
+public data class ScaffoldContext<T : ScrollableState>(
+ val backStackEntry: NavBackStackEntry,
+ val scrollableState: T,
+ val viewModel: NavScaffoldViewModel,
+) {
+ var timeTextMode: TimeTextMode by viewModel::timeTextMode
+
+ var positionIndicatorMode: PositionIndicatorMode by viewModel::positionIndicatorMode
+
+ val arguments: Bundle?
+ get() = backStackEntry.arguments
+}
+
+public data class NonScrollableScaffoldContext(
+ val backStackEntry: NavBackStackEntry,
+ val viewModel: NavScaffoldViewModel,
+) {
+ var timeTextMode: TimeTextMode by viewModel::timeTextMode
+
+ var positionIndicatorMode: PositionIndicatorMode by viewModel::positionIndicatorMode
+
+ val arguments: Bundle?
+ get() = backStackEntry.arguments
+}
+
+/**
+ * The context items provided to a navigation composable.
+ *
+ * The [viewModel] can be used to customise the scaffold behaviour.
+ */
+public data class ScrollableScaffoldContext(
+ val backStackEntry: NavBackStackEntry,
+ val columnState: ScalingLazyColumnState,
+ val viewModel: NavScaffoldViewModel,
+) {
+ val scrollableState: ScalingLazyListState
+ get() = columnState.state
+
+ var timeTextMode: TimeTextMode by viewModel::timeTextMode
+
+ var positionIndicatorMode: PositionIndicatorMode by viewModel::positionIndicatorMode
+
+ val arguments: Bundle?
+ get() = backStackEntry.arguments
+}
diff --git a/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/navscaffold/WearNavScaffold.kt b/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/navscaffold/WearNavScaffold.kt
new file mode 100644
index 0000000..315d822
--- /dev/null
+++ b/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/navscaffold/WearNavScaffold.kt
@@ -0,0 +1,321 @@
+/*
+ * Copyright 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
+ *
+ * https://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.
+ */
+
+@file:OptIn(ExperimentalWearFoundationApi::class)
+
+package com.google.android.horologist.compose.navscaffold
+
+import androidx.compose.foundation.ScrollState
+import androidx.compose.foundation.gestures.ScrollableState
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.lazy.LazyListState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.State
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.key
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.platform.LocalLifecycleOwner
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleEventObserver
+import androidx.lifecycle.viewmodel.compose.viewModel
+import androidx.navigation.NamedNavArgument
+import androidx.navigation.NavBackStackEntry
+import androidx.navigation.NavDeepLink
+import androidx.navigation.NavGraphBuilder
+import androidx.navigation.NavHostController
+import androidx.wear.compose.foundation.ExperimentalWearFoundationApi
+import androidx.wear.compose.foundation.HierarchicalFocusCoordinator
+import androidx.wear.compose.foundation.lazy.ScalingLazyListState
+import androidx.wear.compose.material.PositionIndicator
+import androidx.wear.compose.material.Scaffold
+import androidx.wear.compose.material.TimeText
+import androidx.wear.compose.material.Vignette
+import androidx.wear.compose.navigation.SwipeDismissableNavHost
+import androidx.wear.compose.navigation.SwipeDismissableNavHostState
+import androidx.wear.compose.navigation.composable
+import androidx.wear.compose.navigation.currentBackStackEntryAsState
+import androidx.wear.compose.navigation.rememberSwipeDismissableNavHostState
+import com.google.android.horologist.annotations.ExperimentalHorologistApi
+import com.google.android.horologist.compose.layout.ScalingLazyColumnDefaults
+import com.google.android.horologist.compose.layout.ScalingLazyColumnState
+import com.google.android.horologist.compose.layout.scrollAway
+
+/**
+ * A Navigation and Scroll aware [Scaffold].
+ *
+ * In addition to [NavGraphBuilder.scrollable], 3 additional extensions are supported
+ * [scalingLazyColumnComposable], [scrollStateComposable] and
+ * [lazyListComposable].
+ *
+ * These should be used to build the [ScrollableState] or [FocusRequester] as well as
+ * configure the behaviour of [TimeText], [PositionIndicator] or [Vignette].
+ */
+@Composable
+public fun WearNavScaffold(
+ startDestination: String,
+ navController: NavHostController,
+ modifier: Modifier = Modifier,
+ snackbar: @Composable () -> Unit = {},
+ timeText: @Composable (Modifier) -> Unit = {
+ TimeText(
+ modifier = it,
+ )
+ },
+ state: SwipeDismissableNavHostState = rememberSwipeDismissableNavHostState(),
+ builder: NavGraphBuilder.() -> Unit,
+) {
+ val currentBackStackEntry: NavBackStackEntry? by navController.currentBackStackEntryAsState()
+
+ val viewModel: NavScaffoldViewModel? = currentBackStackEntry?.let {
+ viewModel(viewModelStoreOwner = it)
+ }
+
+ val scrollState: State<ScrollableState?> = remember(viewModel) {
+ derivedStateOf {
+ viewModel?.timeTextScrollableState()
+ }
+ }
+
+ Scaffold(
+ modifier = modifier.fillMaxSize(),
+ timeText = {
+ timeText(Modifier.scrollAway(scrollState))
+ },
+ positionIndicator = {
+ key(currentBackStackEntry?.destination?.route) {
+ val mode = viewModel?.positionIndicatorMode
+
+ if (mode == NavScaffoldViewModel.PositionIndicatorMode.On) {
+ NavPositionIndicator(viewModel)
+ }
+ }
+ },
+ vignette = {
+ key(currentBackStackEntry?.destination?.route) {
+ val vignettePosition = viewModel?.vignettePosition
+ if (vignettePosition is NavScaffoldViewModel.VignetteMode.On) {
+ Vignette(vignettePosition = vignettePosition.position)
+ }
+ }
+ },
+ ) {
+ Box {
+ SwipeDismissableNavHost(
+ navController = navController,
+ startDestination = startDestination,
+ state = state,
+ ) {
+ builder()
+ }
+
+ snackbar()
+ }
+ }
+}
+
+@Composable
+private fun NavPositionIndicator(viewModel: NavScaffoldViewModel) {
+ when (viewModel.scrollType) {
+ NavScaffoldViewModel.ScrollType.ScrollState ->
+ PositionIndicator(
+ scrollState = viewModel.scrollableState as ScrollState,
+ )
+
+ NavScaffoldViewModel.ScrollType.ScalingLazyColumn -> {
+ PositionIndicator(
+ scalingLazyListState = viewModel.scrollableState as ScalingLazyListState,
+ )
+ }
+
+ NavScaffoldViewModel.ScrollType.LazyList ->
+ PositionIndicator(
+ lazyListState = viewModel.scrollableState as LazyListState,
+ )
+
+ else -> {}
+ }
+}
+
+/**
+ * Add a screen to the navigation graph featuring a ScalingLazyColumn.
+ *
+ * The scalingLazyListState must be taken from the [ScaffoldContext].
+ */
+@Deprecated(
+ "Use listComposable",
+)
+public fun NavGraphBuilder.scalingLazyColumnComposable(
+ route: String,
+ arguments: List<NamedNavArgument> = emptyList(),
+ deepLinks: List<NavDeepLink> = emptyList(),
+ scrollStateBuilder: () -> ScalingLazyListState,
+ content: @Composable (ScaffoldContext<ScalingLazyListState>) -> Unit,
+) {
+ composable(route, arguments, deepLinks) {
+ FocusedDestination {
+ val viewModel: NavScaffoldViewModel = viewModel(it)
+
+ val scrollState = viewModel.initializeScalingLazyListState(scrollStateBuilder)
+
+ content(ScaffoldContext(it, scrollState, viewModel))
+ }
+ }
+}
+
+/**
+ * Add a screen to the navigation graph featuring a ScalingLazyColumn.
+ *
+ * The [ScalingLazyColumnState] must be taken from the [ScrollableScaffoldContext].
+ */
+@ExperimentalHorologistApi
+public fun NavGraphBuilder.scrollable(
+ route: String,
+ arguments: List<NamedNavArgument> = emptyList(),
+ deepLinks: List<NavDeepLink> = emptyList(),
+ columnStateFactory: ScalingLazyColumnState.Factory =
+ ScalingLazyColumnDefaults.belowTimeText(),
+ content: @Composable (ScrollableScaffoldContext) -> Unit,
+) {
+ this@scrollable.composable(route, arguments, deepLinks) {
+ FocusedDestination {
+ val columnState = columnStateFactory.create()
+
+ val viewModel: NavScaffoldViewModel = viewModel(it)
+
+ viewModel.initializeScalingLazyListState(columnState)
+
+ content(ScrollableScaffoldContext(it, columnState, viewModel))
+ }
+ }
+}
+
+/**
+ * Add a screen to the navigation graph featuring a Scrollable item.
+ *
+ * The scrollState must be taken from the [ScaffoldContext].
+ */
+public fun NavGraphBuilder.scrollStateComposable(
+ route: String,
+ arguments: List<NamedNavArgument> = emptyList(),
+ deepLinks: List<NavDeepLink> = emptyList(),
+ scrollStateBuilder: () -> ScrollState = { ScrollState(0) },
+ content: @Composable (ScaffoldContext<ScrollState>) -> Unit,
+) {
+ composable(route, arguments, deepLinks) {
+ FocusedDestination {
+ val viewModel: NavScaffoldViewModel = viewModel(it)
+
+ val scrollState = viewModel.initializeScrollState(scrollStateBuilder)
+
+ content(ScaffoldContext(it, scrollState, viewModel))
+ }
+ }
+}
+
+/**
+ * Add a screen to the navigation graph featuring a Lazy list such as LazyColumn.
+ *
+ * The scrollState must be taken from the [ScaffoldContext].
+ */
+public fun NavGraphBuilder.lazyListComposable(
+ route: String,
+ arguments: List<NamedNavArgument> = emptyList(),
+ deepLinks: List<NavDeepLink> = emptyList(),
+ lazyListStateBuilder: () -> LazyListState = { LazyListState() },
+ content: @Composable (ScaffoldContext<LazyListState>) -> Unit,
+) {
+ composable(route, arguments, deepLinks) {
+ FocusedDestination {
+ val viewModel: NavScaffoldViewModel = viewModel(it)
+
+ val scrollState = viewModel.initializeLazyList(lazyListStateBuilder)
+
+ content(ScaffoldContext(it, scrollState, viewModel))
+ }
+ }
+}
+
+/**
+ * Add non scrolling screen to the navigation graph. The [NavBackStackEntry] and
+ * [NavScaffoldViewModel] are passed into the [content] block so that
+ * the Scaffold may be customised, such as disabling TimeText.
+ */
+@Deprecated(
+ "Use composable",
+ ReplaceWith("composable(route, arguments, deepLinks, lazyListStateBuilder, content)"),
+)
+public fun NavGraphBuilder.wearNavComposable(
+ route: String,
+ arguments: List<NamedNavArgument> = emptyList(),
+ deepLinks: List<NavDeepLink> = emptyList(),
+ content: @Composable (NavBackStackEntry, NavScaffoldViewModel) -> Unit,
+) {
+ composable(route, arguments, deepLinks) {
+ FocusedDestination {
+ val viewModel: NavScaffoldViewModel = viewModel()
+
+ content(it, viewModel)
+ }
+ }
+}
+
+/**
+ * Add non scrolling screen to the navigation graph. The [NavBackStackEntry] and
+ * [NavScaffoldViewModel] are passed into the [content] block so that
+ * the Scaffold may be customised, such as disabling TimeText.
+ */
+@ExperimentalHorologistApi
+public fun NavGraphBuilder.composable(
+ route: String,
+ arguments: List<NamedNavArgument> = emptyList(),
+ deepLinks: List<NavDeepLink> = emptyList(),
+ content: @Composable (NonScrollableScaffoldContext) -> Unit,
+) {
+ this@composable.composable(route, arguments, deepLinks) {
+ FocusedDestination {
+ val viewModel: NavScaffoldViewModel = viewModel()
+
+ content(NonScrollableScaffoldContext(it, viewModel))
+ }
+ }
+}
+
+@Composable
+internal fun FocusedDestination(content: @Composable () -> Unit) {
+ val lifecycle = LocalLifecycleOwner.current.lifecycle
+ val focused =
+ remember { mutableStateOf(lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)) }
+
+ DisposableEffect(lifecycle) {
+ val listener = LifecycleEventObserver { _, _ ->
+ focused.value = lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)
+ }
+ lifecycle.addObserver(listener)
+ onDispose {
+ lifecycle.removeObserver(listener)
+ }
+ }
+
+ HierarchicalFocusCoordinator(requiresFocus = { focused.value }) {
+ content()
+ }
+}
diff --git a/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/rotaryinput/Haptics.kt b/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/rotaryinput/Haptics.kt
new file mode 100644
index 0000000..c4af4a6
--- /dev/null
+++ b/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/rotaryinput/Haptics.kt
@@ -0,0 +1,380 @@
+/*
+ * Copyright 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
+ *
+ * https://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.
+ */
+
+@file:OptIn(ExperimentalHorologistApi::class)
+
+package com.google.android.horologist.compose.rotaryinput
+
+import android.os.Build
+import android.view.HapticFeedbackConstants
+import android.view.View
+import androidx.compose.foundation.gestures.ScrollableState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.remember
+import androidx.compose.ui.platform.LocalView
+import com.google.android.horologist.annotations.ExperimentalHorologistApi
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.channels.BufferOverflow
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.conflate
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.receiveAsFlow
+import kotlinx.coroutines.withContext
+import kotlin.math.abs
+
+private const val DEBUG = false
+
+/**
+ * Debug logging that can be enabled.
+ */
+private inline fun debugLog(generateMsg: () -> String) {
+ if (DEBUG) {
+ println("RotaryHaptics: ${generateMsg()}")
+ }
+}
+
+/**
+ * Throttling events within specified timeframe. Only first and last events will be received.
+ * For a flow emitting elements 1 to 30, with a 100ms delay between them:
+ * ```
+ * val flow = flow {
+ * for (i in 1..30) {
+ * delay(100)
+ * emit(i)
+ * }
+ * }
+ * ```
+ * With timeframe=1000 only those integers will be received: 1, 10, 20, 30 .
+ */
+internal fun <T> Flow<T>.throttleLatest(timeframe: Long): Flow<T> =
+ flow {
+ conflate().collect {
+ emit(it)
+ delay(timeframe)
+ }
+ }
+
+/**
+ * Handles haptics for rotary usage
+ */
+@ExperimentalHorologistApi
+public interface RotaryHapticHandler {
+
+ /**
+ * Handles haptics when scroll is used
+ */
+ @ExperimentalHorologistApi
+ public fun handleScrollHaptic(scrollDelta: Float)
+
+ /**
+ * Handles haptics when scroll with snap is used
+ */
+ @ExperimentalHorologistApi
+ public fun handleSnapHaptic(scrollDelta: Float)
+}
+
+/**
+ * Default implementation of [RotaryHapticHandler]. It handles haptic feedback based
+ * on the [scrollableState], scrolled pixels and [hapticsThresholdPx].
+ * Haptic is not fired in this class, instead it's sent to [hapticsChannel]
+ * where it'll performed later.
+ *
+ * @param scrollableState Haptic performed based on this state
+ * @param hapticsChannel Channel to which haptic events will be sent
+ * @param hapticsThresholdPx A scroll threshold after which haptic is produced.
+ */
+public class DefaultRotaryHapticHandler(
+ private val scrollableState: ScrollableState,
+ private val hapticsChannel: Channel<RotaryHapticsType>,
+ private val hapticsThresholdPx: Long = 50,
+) : RotaryHapticHandler {
+
+ private var overscrollHapticTriggered = false
+ private var currScrollPosition = 0f
+ private var prevHapticsPosition = 0f
+
+ override fun handleScrollHaptic(scrollDelta: Float) {
+ if ((scrollDelta > 0 && !scrollableState.canScrollForward) ||
+ (scrollDelta < 0 && !scrollableState.canScrollBackward)
+ ) {
+ if (!overscrollHapticTriggered) {
+ trySendHaptic(RotaryHapticsType.ScrollLimit)
+ overscrollHapticTriggered = true
+ }
+ } else {
+ overscrollHapticTriggered = false
+ currScrollPosition += scrollDelta
+ val diff = abs(currScrollPosition - prevHapticsPosition)
+
+ if (diff >= hapticsThresholdPx) {
+ trySendHaptic(RotaryHapticsType.ScrollTick)
+ prevHapticsPosition = currScrollPosition
+ }
+ }
+ }
+
+ override fun handleSnapHaptic(scrollDelta: Float) {
+ if ((scrollDelta > 0 && !scrollableState.canScrollForward) ||
+ (scrollDelta < 0 && !scrollableState.canScrollBackward)
+ ) {
+ if (!overscrollHapticTriggered) {
+ trySendHaptic(RotaryHapticsType.ScrollLimit)
+ overscrollHapticTriggered = true
+ }
+ } else {
+ overscrollHapticTriggered = false
+ trySendHaptic(RotaryHapticsType.ScrollItemFocus)
+ }
+ }
+
+ private fun trySendHaptic(rotaryHapticsType: RotaryHapticsType) {
+ // Ok to ignore the ChannelResult because we default to capacity = 2 and DROP_OLDEST
+ @Suppress("UNUSED_VARIABLE")
+ val unused = hapticsChannel.trySend(rotaryHapticsType)
+ }
+}
+
+/**
+ * Interface for Rotary haptic feedback
+ */
+@ExperimentalHorologistApi
+public interface RotaryHapticFeedback {
+ @ExperimentalHorologistApi
+ public fun performHapticFeedback(type: RotaryHapticsType)
+}
+
+/**
+ * Rotary haptic types
+ */
+@ExperimentalHorologistApi
+@JvmInline
+public value class RotaryHapticsType(private val type: Int) {
+ public companion object {
+ /**
+ * A scroll ticking haptic. Similar to texture haptic - performed each time when
+ * a scrollable content is scrolled by a certain distance
+ */
+ @ExperimentalHorologistApi
+ public val ScrollTick: RotaryHapticsType = RotaryHapticsType(1)
+
+ /**
+ * An item focus (snap) haptic. Performed when a scrollable content is snapped
+ * to a specific item.
+ */
+ @ExperimentalHorologistApi
+ public val ScrollItemFocus: RotaryHapticsType = RotaryHapticsType(2)
+
+ /**
+ * A limit(overscroll) haptic. Performed when a list reaches the limit
+ * (start or end) and can't scroll further
+ */
+ @ExperimentalHorologistApi
+ public val ScrollLimit: RotaryHapticsType = RotaryHapticsType(3)
+ }
+}
+
+/**
+ * Remember disabled haptics handler
+ */
+@ExperimentalHorologistApi
+@Composable
+public fun rememberDisabledHaptic(): RotaryHapticHandler = remember {
+ object : RotaryHapticHandler {
+
+ override fun handleScrollHaptic(scrollDelta: Float) {
+ // Do nothing
+ }
+
+ override fun handleSnapHaptic(scrollDelta: Float) {
+ // Do nothing
+ }
+ }
+}
+
+/**
+ * Remember rotary haptic handler.
+ * @param scrollableState A scrollableState, used to determine whether the end of the scrollable
+ * was reached or not.
+ * @param throttleThresholdMs Throttling events within specified timeframe.
+ * Only first and last events will be received. Check [throttleLatest] for more info.
+ * @param hapticsThresholdPx A scroll threshold after which haptic is produced.
+ * @param hapticsChannel Channel to which haptic events will be sent
+ * @param rotaryHaptics Interface for Rotary haptic feedback which performs haptics
+ */
+@ExperimentalHorologistApi
+@Composable
+public fun rememberRotaryHapticHandler(
+ scrollableState: ScrollableState,
+ throttleThresholdMs: Long = 30,
+ hapticsThresholdPx: Long = 50,
+ hapticsChannel: Channel<RotaryHapticsType> = rememberHapticChannel(),
+ rotaryHaptics: RotaryHapticFeedback = rememberDefaultRotaryHapticFeedback(),
+): RotaryHapticHandler {
+ return remember(scrollableState, hapticsChannel, rotaryHaptics) {
+ DefaultRotaryHapticHandler(scrollableState, hapticsChannel, hapticsThresholdPx)
+ }.apply {
+ LaunchedEffect(hapticsChannel) {
+ hapticsChannel.receiveAsFlow()
+ .throttleLatest(throttleThresholdMs)
+ .collect { hapticType ->
+ // 'withContext' launches performHapticFeedback in a separate thread,
+ // as otherwise it produces a visible lag (b/219776664)
+ val currentTime = System.currentTimeMillis()
+ debugLog { "Haptics started" }
+ withContext(Dispatchers.Default) {
+ debugLog {
+ "Performing haptics, delay: " +
+ "${System.currentTimeMillis() - currentTime}"
+ }
+ rotaryHaptics.performHapticFeedback(hapticType)
+ }
+ }
+ }
+ }
+}
+
+@Composable
+private fun rememberHapticChannel() =
+ remember {
+ Channel<RotaryHapticsType>(
+ capacity = 2,
+ onBufferOverflow = BufferOverflow.DROP_OLDEST,
+ )
+ }
+
+@ExperimentalHorologistApi
+@Composable
+public fun rememberDefaultRotaryHapticFeedback(): RotaryHapticFeedback =
+ LocalView.current.let { view -> remember { findDeviceSpecificHapticFeedback(view) } }
+
+internal fun findDeviceSpecificHapticFeedback(view: View): RotaryHapticFeedback =
+ if (isGooglePixelWatch()) {
+ PixelWatchRotaryHapticFeedback(view)
+ } else if (isGalaxyWatchClassic()) {
+ GalaxyWatchClassicHapticFeedback(view)
+ } else {
+ DefaultRotaryHapticFeedback(view)
+ }
+
+/**
+ * Default Rotary implementation for [RotaryHapticFeedback]
+ */
+@ExperimentalHorologistApi
+public class DefaultRotaryHapticFeedback(private val view: View) : RotaryHapticFeedback {
+
+ @ExperimentalHorologistApi
+ override fun performHapticFeedback(
+ type: RotaryHapticsType,
+ ) {
+ when (type) {
+ RotaryHapticsType.ScrollItemFocus -> {
+ view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
+ }
+
+ RotaryHapticsType.ScrollTick -> {
+ view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP)
+ }
+
+ RotaryHapticsType.ScrollLimit -> {
+ view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
+ }
+ }
+ }
+}
+
+/**
+ * Implementation of [RotaryHapticFeedback] for Pixel Watch
+ */
+@ExperimentalHorologistApi
+private class PixelWatchRotaryHapticFeedback(private val view: View) : RotaryHapticFeedback {
+
+ @ExperimentalHorologistApi
+ override fun performHapticFeedback(
+ type: RotaryHapticsType,
+ ) {
+ when (type) {
+ RotaryHapticsType.ScrollItemFocus -> {
+ view.performHapticFeedback(
+ if (Build.VERSION.SDK_INT >= 33) {
+ ROTARY_SCROLL_ITEM_FOCUS
+ } else {
+ WEAR_SCROLL_ITEM_FOCUS
+ },
+ )
+ }
+
+ RotaryHapticsType.ScrollTick -> {
+ view.performHapticFeedback(
+ if (Build.VERSION.SDK_INT >= 33) ROTARY_SCROLL_TICK else WEAR_SCROLL_TICK,
+ )
+ }
+
+ RotaryHapticsType.ScrollLimit -> {
+ view.performHapticFeedback(
+ if (Build.VERSION.SDK_INT >= 33) ROTARY_SCROLL_LIMIT else WEAR_SCROLL_LIMIT,
+ )
+ }
+ }
+ }
+
+ private companion object {
+ // Hidden constants from HapticFeedbackConstants.java specific for Pixel Watch
+ // API 33
+ public const val ROTARY_SCROLL_TICK: Int = 18
+ public const val ROTARY_SCROLL_ITEM_FOCUS: Int = 19
+ public const val ROTARY_SCROLL_LIMIT: Int = 20
+
+ // API 30
+ public const val WEAR_SCROLL_TICK: Int = 10002
+ public const val WEAR_SCROLL_ITEM_FOCUS: Int = 10003
+ public const val WEAR_SCROLL_LIMIT: Int = 10003
+ }
+}
+
+/**
+ * Implementation of [RotaryHapticFeedback] for Galaxy Watch 4 Classic
+ */
+@ExperimentalHorologistApi
+private class GalaxyWatchClassicHapticFeedback(private val view: View) : RotaryHapticFeedback {
+
+ @ExperimentalHorologistApi
+ override fun performHapticFeedback(
+ type: RotaryHapticsType,
+ ) {
+ when (type) {
+ RotaryHapticsType.ScrollItemFocus -> {
+ // No haptic for scroll snap ( we have physical bezel)
+ }
+
+ RotaryHapticsType.ScrollTick -> {
+ // No haptic for scroll tick ( we have physical bezel)
+ }
+
+ RotaryHapticsType.ScrollLimit -> {
+ view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
+ }
+ }
+ }
+}
+
+private fun isGalaxyWatchClassic(): Boolean =
+ Build.MODEL.matches("SM-R8[89]5.".toRegex())
+
+private fun isGooglePixelWatch(): Boolean =
+ Build.MODEL.startsWith("Google Pixel Watch")
diff --git a/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/rotaryinput/Rotary.kt b/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/rotaryinput/Rotary.kt
new file mode 100644
index 0000000..3ca16c1
--- /dev/null
+++ b/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/rotaryinput/Rotary.kt
@@ -0,0 +1,1301 @@
+/*
+ * Copyright 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
+ *
+ * https://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.
+ */
+
+@file:OptIn(ExperimentalHorologistApi::class)
+
+package com.google.android.horologist.compose.rotaryinput
+
+import android.view.ViewConfiguration
+import androidx.compose.animation.core.AnimationState
+import androidx.compose.animation.core.CubicBezierEasing
+import androidx.compose.animation.core.Easing
+import androidx.compose.animation.core.FastOutSlowInEasing
+import androidx.compose.animation.core.SpringSpec
+import androidx.compose.animation.core.animateTo
+import androidx.compose.animation.core.copy
+import androidx.compose.animation.core.spring
+import androidx.compose.animation.core.tween
+import androidx.compose.foundation.MutatePriority
+import androidx.compose.foundation.focusable
+import androidx.compose.foundation.gestures.FlingBehavior
+import androidx.compose.foundation.gestures.ScrollableDefaults
+import androidx.compose.foundation.gestures.ScrollableState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.remember
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.composed
+import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.focus.focusRequester
+import androidx.compose.ui.input.rotary.onRotaryScrollEvent
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.util.fastSumBy
+import androidx.compose.ui.util.lerp
+import androidx.wear.compose.foundation.ExperimentalWearFoundationApi
+import androidx.wear.compose.foundation.lazy.ScalingLazyListState
+import androidx.wear.compose.foundation.rememberActiveFocusRequester
+import com.google.android.horologist.annotations.ExperimentalHorologistApi
+import kotlinx.coroutines.CompletableDeferred
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.async
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.flow.receiveAsFlow
+import kotlinx.coroutines.flow.transformLatest
+import kotlin.math.abs
+import kotlin.math.absoluteValue
+import kotlin.math.sign
+
+private const val DEBUG = false
+
+/**
+ * Debug logging that can be enabled.
+ */
+private inline fun debugLog(generateMsg: () -> String) {
+ if (DEBUG) {
+ println("RotaryScroll: ${generateMsg()}")
+ }
+}
+
+/**
+ * A modifier which connects rotary events with scrollable.
+ * This modifier supports fling.
+ *
+ * Fling algorithm:
+ * - A scroll with RSB/ Bezel happens.
+ * - If this is a first rotary event after the threshold ( by default 200ms), a new scroll
+ * session starts by resetting all necessary parameters
+ * - A delta value is added into VelocityTracker and a new speed is calculated.
+ * - If the current speed is bigger than the previous one, this value is remembered as
+ * a latest fling speed with a timestamp
+ * - After each scroll event a fling countdown starts ( by default 70ms) which
+ * resets if new scroll event is received
+ * - If fling countdown is finished - it means that the finger was probably raised from RSB, there will be no other events and probably
+ * this is the last event during this session. After it a fling is triggered.
+ * - Fling is stopped when a new scroll event happens
+ *
+ * The screen containing the scrollable item should request the focus
+ * by calling [requestFocus] method
+ *
+ * ```
+ * LaunchedEffect(Unit) {
+ * focusRequester.requestFocus()
+ * }
+ * ```
+ * @param focusRequester Requests the focus for rotary input
+ * @param scrollableState Scrollable state which will be scrolled while receiving rotary events
+ * @param flingBehavior Logic describing fling behavior.
+ * @param rotaryHaptics Class which will handle haptic feedback
+ * @param reverseDirection Reverse the direction of scrolling. Should be aligned with
+ * Scrollable `reverseDirection` parameter
+ */
+@ExperimentalHorologistApi
+@Suppress("ComposableModifierFactory")
+@Deprecated(
+ "Use rotaryWithScroll instead",
+ ReplaceWith(
+ "this.rotaryWithScroll(scrollableState, focusRequester, " +
+ "flingBehavior, rotaryHaptics, reverseDirection)",
+ ),
+)
+@Composable
+public fun Modifier.rotaryWithFling(
+ focusRequester: FocusRequester,
+ scrollableState: ScrollableState,
+ flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior(),
+ rotaryHaptics: RotaryHapticHandler = rememberRotaryHapticHandler(scrollableState),
+ reverseDirection: Boolean = false,
+): Modifier = rotaryHandler(
+ rotaryScrollHandler = RotaryDefaults.rememberFlingHandler(scrollableState, flingBehavior),
+ reverseDirection = reverseDirection,
+ rotaryHaptics = rotaryHaptics,
+)
+ .focusRequester(focusRequester)
+ .focusable()
+
+/**
+ * A modifier which connects rotary events with scrollable.
+ * This modifier supports scroll with fling.
+ *
+ * @param scrollableState Scrollable state which will be scrolled while receiving rotary events
+ * @param focusRequester Requests the focus for rotary input.
+ * By default comes from [rememberActiveFocusRequester],
+ * which is used with [HierarchicalFocusCoordinator]
+ * @param flingBehavior Logic describing fling behavior. If null fling will not happen.
+ * @param rotaryHaptics Class which will handle haptic feedback
+ * @param reverseDirection Reverse the direction of scrolling. Should be aligned with
+ * Scrollable `reverseDirection` parameter
+ */
+@OptIn(ExperimentalWearFoundationApi::class)
+@ExperimentalHorologistApi
+@Suppress("ComposableModifierFactory")
+@Composable
+public fun Modifier.rotaryWithScroll(
+ scrollableState: ScrollableState,
+ focusRequester: FocusRequester = rememberActiveFocusRequester(),
+ flingBehavior: FlingBehavior? = ScrollableDefaults.flingBehavior(),
+ rotaryHaptics: RotaryHapticHandler = rememberRotaryHapticHandler(scrollableState),
+ reverseDirection: Boolean = false,
+): Modifier = rotaryHandler(
+ rotaryScrollHandler = RotaryDefaults.rememberFlingHandler(scrollableState, flingBehavior),
+ reverseDirection = reverseDirection,
+ rotaryHaptics = rotaryHaptics,
+)
+ .focusRequester(focusRequester)
+ .focusable()
+
+/**
+ * A modifier which connects rotary events with scrollable.
+ * This modifier supports snap.
+ *
+ * @param focusRequester Requests the focus for rotary input.
+ * By default comes from [rememberActiveFocusRequester],
+ * which is used with [HierarchicalFocusCoordinator]
+ * @param rotaryScrollAdapter A connection between scrollable objects and rotary events
+ * @param rotaryHaptics Class which will handle haptic feedback
+ * @param reverseDirection Reverse the direction of scrolling. Should be aligned with
+ * Scrollable `reverseDirection` parameter
+ */
+@OptIn(ExperimentalWearFoundationApi::class)
+@ExperimentalHorologistApi
+@Suppress("ComposableModifierFactory")
+@Composable
+public fun Modifier.rotaryWithSnap(
+ rotaryScrollAdapter: RotaryScrollAdapter,
+ focusRequester: FocusRequester = rememberActiveFocusRequester(),
+ snapParameters: SnapParameters = RotaryDefaults.snapParametersDefault(),
+ rotaryHaptics: RotaryHapticHandler =
+ rememberRotaryHapticHandler(rotaryScrollAdapter.scrollableState),
+ reverseDirection: Boolean = false,
+): Modifier = rotaryHandler(
+ rotaryScrollHandler =
+ RotaryDefaults.rememberSnapHandler(rotaryScrollAdapter, snapParameters),
+ reverseDirection = reverseDirection,
+ rotaryHaptics = rotaryHaptics,
+)
+ .focusRequester(focusRequester)
+ .focusable()
+
+/**
+ * An extension function for creating [RotaryScrollAdapter] from [ScalingLazyListState]
+ */
+@ExperimentalHorologistApi
+public fun ScalingLazyListState.toRotaryScrollAdapter(): RotaryScrollAdapter =
+ ScalingLazyColumnRotaryScrollAdapter(this)
+
+/**
+ * An implementation of rotary scroll adapter for [ScalingLazyColumn]
+ */
+@ExperimentalHorologistApi
+public class ScalingLazyColumnRotaryScrollAdapter(
+ override val scrollableState: ScalingLazyListState,
+) : RotaryScrollAdapter {
+
+ /**
+ * Calculates an average height of an item by taking an average from visible items height.
+ */
+ override fun averageItemSize(): Float {
+ val visibleItems = scrollableState.layoutInfo.visibleItemsInfo
+ return (visibleItems.fastSumBy { it.unadjustedSize } / visibleItems.size).toFloat()
+ }
+
+ /**
+ * Current (centred) item index
+ */
+ override fun currentItemIndex(): Int = scrollableState.centerItemIndex
+
+ /**
+ * An offset from the item centre
+ */
+ override fun currentItemOffset(): Float = scrollableState.centerItemScrollOffset.toFloat()
+
+ /**
+ * The total count of items in ScalingLazyColumn
+ */
+ override fun totalItemsCount(): Int = scrollableState.layoutInfo.totalItemsCount
+}
+
+/**
+ * An adapter which connects scrollableState to Rotary
+ */
+@ExperimentalHorologistApi
+public interface RotaryScrollAdapter {
+
+ /**
+ * A scrollable state. Used for performing scroll when Rotary events received
+ */
+ @ExperimentalHorologistApi
+ public val scrollableState: ScrollableState
+
+ /**
+ * Average size of an item. Used for estimating the scrollable distance
+ */
+ @ExperimentalHorologistApi
+ public fun averageItemSize(): Float
+
+ /**
+ * A current item index. Used for scrolling
+ */
+ @ExperimentalHorologistApi
+ public fun currentItemIndex(): Int
+
+ /**
+ * An offset from the centre or the border of the current item.
+ */
+ @ExperimentalHorologistApi
+ public fun currentItemOffset(): Float
+
+ /**
+ * The total count of items in [scrollableState]
+ */
+ @ExperimentalHorologistApi
+ public fun totalItemsCount(): Int
+}
+
+/**
+ * Defaults for rotary modifiers
+ */
+@ExperimentalHorologistApi
+public object RotaryDefaults {
+
+ /**
+ * Handles scroll with fling.
+ * @param scrollableState Scrollable state which will be scrolled while receiving rotary events
+ * @param flingBehavior Logic describing Fling behavior. If null - fling will not happen
+ * @param isLowRes Whether the input is Low-res (a bezel) or high-res(a crown/rsb)
+ */
+ @ExperimentalHorologistApi
+ @Composable
+ public fun rememberFlingHandler(
+ scrollableState: ScrollableState,
+ flingBehavior: FlingBehavior? = null,
+ isLowRes: Boolean = isLowResInput(),
+ ): RotaryScrollHandler {
+ val viewConfiguration = ViewConfiguration.get(LocalContext.current)
+
+ return remember(scrollableState, flingBehavior, isLowRes) {
+ debugLog { "isLowRes : $isLowRes" }
+ fun rotaryFlingBehavior() = flingBehavior?.run {
+ DefaultRotaryFlingBehavior(
+ scrollableState,
+ flingBehavior,
+ viewConfiguration,
+ flingTimeframe =
+ if (isLowRes) lowResFlingTimeframe else highResFlingTimeframe,
+ )
+ }
+
+ fun scrollBehavior() = AnimationScrollBehavior(scrollableState)
+
+ if (isLowRes) {
+ LowResRotaryScrollHandler(
+ rotaryFlingBehaviorFactory = { rotaryFlingBehavior() },
+ scrollBehaviorFactory = { scrollBehavior() },
+ )
+ } else {
+ HighResRotaryScrollHandler(
+ rotaryFlingBehaviorFactory = { rotaryFlingBehavior() },
+ scrollBehaviorFactory = { scrollBehavior() },
+ )
+ }
+ }
+ }
+
+ /**
+ * Handles scroll with snap
+ * @param rotaryScrollAdapter A connection between scrollable objects and rotary events
+ * @param snapParameters Snap parameters
+ */
+ @ExperimentalHorologistApi
+ @Composable
+ public fun rememberSnapHandler(
+ rotaryScrollAdapter: RotaryScrollAdapter,
+ snapParameters: SnapParameters = snapParametersDefault(),
+ isLowRes: Boolean = isLowResInput(),
+ ): RotaryScrollHandler {
+ return remember(rotaryScrollAdapter, snapParameters) {
+ if (isLowRes) {
+ LowResSnapHandler(
+ snapBehaviourFactory = {
+ DefaultSnapBehavior(rotaryScrollAdapter, snapParameters)
+ },
+ )
+ } else {
+ HighResSnapHandler(
+ resistanceFactor = snapParameters.resistanceFactor,
+ thresholdBehaviorFactory = {
+ ThresholdBehavior(
+ rotaryScrollAdapter,
+ snapParameters.thresholdDivider,
+ )
+ },
+ snapBehaviourFactory = {
+ DefaultSnapBehavior(rotaryScrollAdapter, snapParameters)
+ },
+ scrollBehaviourFactory = {
+ AnimationScrollBehavior(rotaryScrollAdapter.scrollableState)
+ },
+ )
+ }
+ }
+ }
+
+ /**
+ * Returns default [SnapParameters]
+ */
+ @ExperimentalHorologistApi
+ public fun snapParametersDefault(): SnapParameters =
+ SnapParameters(
+ snapOffset = 0,
+ thresholdDivider = 1.5f,
+ resistanceFactor = 3f,
+ )
+
+ /**
+ * Returns whether the input is Low-res (a bezel) or high-res(a crown/rsb).
+ */
+ @ExperimentalHorologistApi
+ @Composable
+ public fun isLowResInput(): Boolean = LocalContext.current.packageManager
+ .hasSystemFeature("android.hardware.rotaryencoder.lowres")
+
+ private val lowResFlingTimeframe: Long = 100L
+ private val highResFlingTimeframe: Long = 30L
+}
+
+/**
+ * Parameters used for snapping
+ *
+ * @param snapOffset an optional offset to be applied when snapping the item. After the snap the
+ * snapped items offset will be [snapOffset].
+ */
+public class SnapParameters(
+ public val snapOffset: Int,
+ public val thresholdDivider: Float,
+ public val resistanceFactor: Float,
+) {
+ /**
+ * Returns a snapping offset in [Dp]
+ */
+ @Composable
+ public fun snapOffsetDp(): Dp {
+ return with(LocalDensity.current) {
+ snapOffset.toDp()
+ }
+ }
+}
+
+/**
+ * An interface for handling scroll events
+ */
+@ExperimentalHorologistApi
+public interface RotaryScrollHandler {
+ /**
+ * Handles scrolling events
+ * @param coroutineScope A scope for performing async actions
+ * @param event A scrollable event from rotary input, containing scrollable delta and timestamp
+ * @param rotaryHaptics
+ */
+ @ExperimentalHorologistApi
+ public suspend fun handleScrollEvent(
+ coroutineScope: CoroutineScope,
+ event: TimestampedDelta,
+ rotaryHaptics: RotaryHapticHandler,
+ )
+}
+
+/**
+ * An interface for scrolling behavior
+ */
+@ExperimentalHorologistApi
+public interface RotaryScrollBehavior {
+ /**
+ * Handles scroll event to [targetValue]
+ */
+ @ExperimentalHorologistApi
+ public suspend fun handleEvent(targetValue: Float)
+}
+
+/**
+ * Default implementation of [RotaryFlingBehavior]
+ */
+@ExperimentalHorologistApi
+public class DefaultRotaryFlingBehavior(
+ private val scrollableState: ScrollableState,
+ private val flingBehavior: FlingBehavior,
+ viewConfiguration: ViewConfiguration,
+ private val flingTimeframe: Long,
+) : RotaryFlingBehavior {
+
+ // A time range during which the fling is valid.
+ // For simplicity it's twice as long as [flingTimeframe]
+ private val timeRangeToFling = flingTimeframe * 2
+
+ // A default fling factor for making fling slower
+ private val flingScaleFactor = 0.7f
+
+ private var previousVelocity = 0f
+
+ private val rotaryVelocityTracker = RotaryVelocityTracker()
+
+ private val minFlingSpeed = viewConfiguration.scaledMinimumFlingVelocity.toFloat()
+ private val maxFlingSpeed = viewConfiguration.scaledMaximumFlingVelocity.toFloat()
+ private var latestEventTimestamp: Long = 0
+
+ private var flingVelocity: Float = 0f
+ private var flingTimestamp: Long = 0
+
+ @ExperimentalHorologistApi
+ override fun startFlingTracking(timestamp: Long) {
+ rotaryVelocityTracker.start(timestamp)
+ latestEventTimestamp = timestamp
+ previousVelocity = 0f
+ }
+
+ @ExperimentalHorologistApi
+ override fun observeEvent(timestamp: Long, delta: Float) {
+ rotaryVelocityTracker.move(timestamp, delta)
+ latestEventTimestamp = timestamp
+ }
+
+ @ExperimentalHorologistApi
+ override suspend fun trackFling(beforeFling: () -> Unit) {
+ val currentVelocity = rotaryVelocityTracker.velocity
+ debugLog { "currentVelocity: $currentVelocity" }
+
+ if (abs(currentVelocity) >= abs(previousVelocity)) {
+ flingTimestamp = latestEventTimestamp
+ flingVelocity = currentVelocity * flingScaleFactor
+ }
+ previousVelocity = currentVelocity
+
+ // Waiting for a fixed amount of time before checking the fling
+ delay(flingTimeframe)
+
+ // For making a fling 2 criteria should be met:
+ // 1) no more than
+ // `rangeToFling` ms should pass between last fling detection
+ // and the time of last motion event
+ // 2) flingVelocity should exceed the minFlingSpeed
+ debugLog {
+ "Check fling: flingVelocity: $flingVelocity " +
+ "minFlingSpeed: $minFlingSpeed, maxFlingSpeed: $maxFlingSpeed"
+ }
+ if (latestEventTimestamp - flingTimestamp < timeRangeToFling &&
+ abs(flingVelocity) > minFlingSpeed
+ ) {
+ // Stops scrollAnimationCoroutine because a fling will be performed
+ beforeFling()
+ val velocity = flingVelocity.coerceIn(-maxFlingSpeed, maxFlingSpeed)
+ scrollableState.scroll(MutatePriority.UserInput) {
+ with(flingBehavior) {
+ debugLog { "Flinging with velocity $velocity" }
+ performFling(velocity)
+ }
+ }
+ }
+ }
+}
+
+/**
+ * An interface for flinging with rotary
+ */
+@ExperimentalHorologistApi
+public interface RotaryFlingBehavior {
+
+ /**
+ * Observing new event within a fling tracking session with new timestamp and delta
+ */
+ @ExperimentalHorologistApi
+ public fun observeEvent(timestamp: Long, delta: Float)
+
+ /**
+ * Performing fling if necessary and calling [beforeFling] lambda before it is triggered
+ */
+ @ExperimentalHorologistApi
+ public suspend fun trackFling(beforeFling: () -> Unit)
+
+ /**
+ * Starts a new fling tracking session
+ * with specified timestamp
+ */
+ @ExperimentalHorologistApi
+ public fun startFlingTracking(timestamp: Long)
+}
+
+/**
+ * An interface for snapping with rotary
+ */
+@ExperimentalHorologistApi
+public interface RotarySnapBehavior {
+
+ /**
+ * Preparing snapping. This method should be called before [snapToTargetItem] is called.
+ *
+ * Snapping is done for current + [moveForElements] items.
+ *
+ * If [sequentialSnap] is true, items are summed up together.
+ * For example, if [prepareSnapForItems] is called with
+ * [moveForElements] = 2, 3, 5 -> then the snapping will happen to current + 10 items
+ *
+ * If [sequentialSnap] is false, then [moveForElements] are not summed up together.
+ */
+ public fun prepareSnapForItems(moveForElements: Int, sequentialSnap: Boolean)
+
+ /**
+ * Performs snapping to the closest item.
+ */
+ public suspend fun snapToClosestItem()
+
+ /**
+ * Returns true if top edge was reached
+ */
+ public fun topEdgeReached(): Boolean
+
+ /**
+ * Returns true if bottom edge was reached
+ */
+ public fun bottomEdgeReached(): Boolean
+
+ /**
+ * Performs snapping to the specified in [prepareSnapForItems] element
+ */
+ public suspend fun snapToTargetItem()
+}
+
+/**
+ * A rotary event object which contains a [timestamp] of the rotary event and a scrolled [delta].
+ */
+@ExperimentalHorologistApi
+public data class TimestampedDelta(val timestamp: Long, val delta: Float)
+
+/** Animation implementation of [RotaryScrollBehavior].
+ * This class does a smooth animation when the scroll by N pixels is done.
+ * This animation works well on Rsb(high-res) and Bezel(low-res) devices.
+ */
+@ExperimentalHorologistApi
+public class AnimationScrollBehavior(
+ private val scrollableState: ScrollableState,
+) : RotaryScrollBehavior {
+ private var sequentialAnimation = false
+ private var scrollAnimation = AnimationState(0f)
+ private var prevPosition = 0f
+
+ @ExperimentalHorologistApi
+ override suspend fun handleEvent(targetValue: Float) {
+ scrollableState.scroll(MutatePriority.UserInput) {
+ debugLog { "ScrollAnimation value before start: ${scrollAnimation.value}" }
+
+ scrollAnimation.animateTo(
+ targetValue,
+ animationSpec = spring(),
+ sequentialAnimation = sequentialAnimation,
+ ) {
+ val delta = value - prevPosition
+ debugLog { "Animated by $delta, value: $value" }
+ scrollBy(delta)
+ prevPosition = value
+ sequentialAnimation = value != this.targetValue
+ }
+ }
+ }
+}
+
+/**
+ * An animated implementation of [RotarySnapBehavior]. Uses animateScrollToItem
+ * method for snapping to the Nth item
+ */
+@ExperimentalHorologistApi
+public class DefaultSnapBehavior(
+ private val rotaryScrollAdapter: RotaryScrollAdapter,
+ private val snapParameters: SnapParameters,
+) : RotarySnapBehavior {
+ private var snapTarget: Int = rotaryScrollAdapter.currentItemIndex()
+ private var sequentialSnap: Boolean = false
+
+ private var anim = AnimationState(0f)
+ private var expectedDistance = 0f
+
+ private val defaultStiffness = 200f
+ private var snapTargetUpdated = true
+
+ @ExperimentalHorologistApi
+ override fun prepareSnapForItems(moveForElements: Int, sequentialSnap: Boolean) {
+ this.sequentialSnap = sequentialSnap
+ if (sequentialSnap) {
+ snapTarget += moveForElements
+ } else {
+ snapTarget = rotaryScrollAdapter.currentItemIndex() + moveForElements
+ }
+ snapTargetUpdated = true
+ snapTarget = snapTarget.coerceIn(0 until rotaryScrollAdapter.totalItemsCount())
+ }
+
+ override suspend fun snapToClosestItem() {
+ // Snapping to the closest item by using performFling method with 0 speed
+ rotaryScrollAdapter.scrollableState.scroll(MutatePriority.UserInput) {
+ debugLog { "snap to closest item" }
+ var prevPosition = 0f
+ AnimationState(0f).animateTo(
+ targetValue = -rotaryScrollAdapter.currentItemOffset(),
+ animationSpec = tween(durationMillis = 100, easing = FastOutSlowInEasing),
+ ) {
+ val animDelta = value - prevPosition
+ scrollBy(animDelta)
+ prevPosition = value
+ }
+ snapTarget = rotaryScrollAdapter.currentItemIndex()
+ }
+ }
+
+ override fun topEdgeReached(): Boolean = snapTarget <= 0
+
+ override fun bottomEdgeReached(): Boolean =
+ snapTarget >= rotaryScrollAdapter.totalItemsCount() - 1
+
+ override suspend fun snapToTargetItem() {
+ if (sequentialSnap) {
+ anim = anim.copy(0f)
+ } else {
+ anim = AnimationState(0f)
+ }
+ rotaryScrollAdapter.scrollableState.scroll(MutatePriority.UserInput) {
+ // If snapTargetUpdated is true - then the target was updated so we
+ // need to do snap again
+ while (snapTargetUpdated) {
+ snapTargetUpdated = false
+ var latestCenterItem: Int
+ var continueFirstScroll = true
+ debugLog { "snapTarget $snapTarget" }
+ while (continueFirstScroll) {
+ latestCenterItem = rotaryScrollAdapter.currentItemIndex()
+ anim = anim.copy(0f)
+ expectedDistance = expectedDistanceTo(snapTarget, snapParameters.snapOffset)
+ debugLog {
+ "expectedDistance = $expectedDistance, " +
+ "scrollableState.centerItemScrollOffset " +
+ "${rotaryScrollAdapter.currentItemOffset()}"
+ }
+ continueFirstScroll = false
+ var prevPosition = 0f
+
+ anim.animateTo(
+ expectedDistance,
+ animationSpec = SpringSpec(
+ stiffness = defaultStiffness,
+ visibilityThreshold = 0.1f,
+ ),
+ sequentialAnimation = (anim.velocity != 0f),
+ ) {
+ val animDelta = value - prevPosition
+ debugLog {
+ "First animation, value:$value, velocity:$velocity, " +
+ "animDelta:$animDelta"
+ }
+
+ // Exit animation if snap target was updated
+ if (snapTargetUpdated) cancelAnimation()
+
+ scrollBy(animDelta)
+ prevPosition = value
+
+ if (latestCenterItem != rotaryScrollAdapter.currentItemIndex()) {
+ continueFirstScroll = true
+ cancelAnimation()
+ return@animateTo
+ }
+
+ debugLog { "centerItemIndex = ${rotaryScrollAdapter.currentItemIndex()}" }
+ if (rotaryScrollAdapter.currentItemIndex() == snapTarget) {
+ debugLog { "Target is visible. Cancelling first animation" }
+ debugLog {
+ "scrollableState.centerItemScrollOffset " +
+ "${rotaryScrollAdapter.currentItemOffset()}"
+ }
+ expectedDistance = -rotaryScrollAdapter.currentItemOffset()
+ continueFirstScroll = false
+ cancelAnimation()
+ return@animateTo
+ }
+ }
+ }
+ // Exit animation if snap target was updated
+ if (snapTargetUpdated) continue
+
+ anim = anim.copy(0f)
+ var prevPosition = 0f
+ anim.animateTo(
+ expectedDistance,
+ animationSpec = SpringSpec(
+ stiffness = defaultStiffness,
+ visibilityThreshold = 0.1f,
+ ),
+ sequentialAnimation = (anim.velocity != 0f),
+ ) {
+ // Exit animation if snap target was updated
+ if (snapTargetUpdated) cancelAnimation()
+
+ val animDelta = value - prevPosition
+ debugLog { "Final animation. velocity:$velocity, animDelta:$animDelta" }
+ scrollBy(animDelta)
+ prevPosition = value
+ }
+ }
+ }
+ }
+
+ private fun expectedDistanceTo(index: Int, targetScrollOffset: Int): Float {
+ val averageSize = rotaryScrollAdapter.averageItemSize()
+ val indexesDiff = index - rotaryScrollAdapter.currentItemIndex()
+ debugLog { "Average size $averageSize" }
+ return (averageSize * indexesDiff) +
+ targetScrollOffset - rotaryScrollAdapter.currentItemOffset()
+ }
+}
+
+/**
+ * A modifier which handles rotary events.
+ * It accepts ScrollHandler as the input - a class where main logic about how
+ * scroll should be handled is lying
+ */
+@ExperimentalHorologistApi
+@OptIn(ExperimentalComposeUiApi::class)
+public fun Modifier.rotaryHandler(
+ rotaryScrollHandler: RotaryScrollHandler,
+ // TODO: batching causes additional delays. Return once it's clear that
+ // we will use it
+ /* batchTimeframe: Long = 0L,*/
+ reverseDirection: Boolean,
+ rotaryHaptics: RotaryHapticHandler,
+): Modifier = composed {
+ val channel = rememberTimestampChannel()
+ val eventsFlow = remember(channel) { channel.receiveAsFlow() }
+
+ composed {
+ LaunchedEffect(eventsFlow) {
+ eventsFlow
+ // TODO: batching causes additional delays. Return once it's clear that
+ // we will use it
+ // Do we really need to do this on this level?
+// .batchRequestsWithinTimeframe(batchTimeframe)
+ .collectLatest {
+ debugLog {
+ "Scroll event received: " +
+ "delta:${it.delta}, timestamp:${it.timestamp}"
+ }
+ rotaryScrollHandler.handleScrollEvent(this, it, rotaryHaptics)
+ }
+ }
+ this
+ .onRotaryScrollEvent {
+ // Okay to ignore the ChannelResult returned from trySend because it is conflated
+ // (see rememberTimestampChannel()).
+ @Suppress("UNUSED_VARIABLE")
+ val unused = channel.trySend(
+ TimestampedDelta(
+ it.uptimeMillis,
+ it.verticalScrollPixels * if (reverseDirection) -1f else 1f,
+ ),
+ )
+ true
+ }
+ }
+}
+
+/**
+ * Batching requests for scrolling events. This function combines all events together
+ * (except first) within specified timeframe. Should help with performance on high-res devices.
+ */
+@ExperimentalHorologistApi
+@OptIn(ExperimentalCoroutinesApi::class)
+public fun Flow<TimestampedDelta>.batchRequestsWithinTimeframe(
+ timeframe: Long
+): Flow<TimestampedDelta> {
+ var delta = 0f
+ var lastTimestamp = -timeframe
+ return if (timeframe == 0L) {
+ this
+ } else {
+ this.transformLatest {
+ delta += it.delta
+ debugLog { "Batching requests. delta:$delta" }
+ if (lastTimestamp + timeframe <= it.timestamp) {
+ lastTimestamp = it.timestamp
+ debugLog { "No events before, delta= $delta" }
+ emit(TimestampedDelta(it.timestamp, delta))
+ } else {
+ delay(timeframe)
+ debugLog { "After delay, delta= $delta" }
+ if (delta > 0f) {
+ emit(TimestampedDelta(it.timestamp, delta))
+ }
+ }
+ delta = 0f
+ }
+ }
+}
+
+/**
+ * A scroll handler for RSB(high-res) without snapping and with or without fling
+ * A list is scrolled by the number of pixels received from the rotary device.
+ *
+ * This class is a little bit different from LowResScrollHandler class - it has a filtering
+ * for events which are coming with wrong sign ( this happens to rsb devices,
+ * especially at the end of the scroll)
+ *
+ * This scroll handler supports fling. It can be set with [RotaryFlingBehavior].
+ */
+internal class HighResRotaryScrollHandler(
+ private val rotaryFlingBehaviorFactory: () -> RotaryFlingBehavior?,
+ private val scrollBehaviorFactory: () -> RotaryScrollBehavior,
+ private val hapticsThreshold: Long = 50,
+) : RotaryScrollHandler {
+
+ // This constant is specific for high-res devices. Because that input values
+ // can sometimes come with different sign, we have to filter them in this threshold
+ private val gestureThresholdTime = 200L
+ private var scrollJob: Job = CompletableDeferred<Unit>()
+ private var flingJob: Job = CompletableDeferred<Unit>()
+
+ private var previousScrollEventTime = 0L
+ private var rotaryScrollDistance = 0f
+
+ private var rotaryFlingBehavior: RotaryFlingBehavior? = rotaryFlingBehaviorFactory()
+ private var scrollBehavior: RotaryScrollBehavior = scrollBehaviorFactory()
+
+ override suspend fun handleScrollEvent(
+ coroutineScope: CoroutineScope,
+ event: TimestampedDelta,
+ rotaryHaptics: RotaryHapticHandler,
+ ) {
+ val time = event.timestamp
+ val isOppositeScrollValue = isOppositeValueAfterScroll(event.delta)
+
+ if (isNewScrollEvent(time)) {
+ debugLog { "New scroll event" }
+ resetTracking(time)
+ rotaryScrollDistance = event.delta
+ } else {
+ // Due to the physics of Rotary side button, some events might come
+ // with an opposite axis value - either at the start or at the end of the motion.
+ // We don't want to use these values for fling calculations.
+ if (!isOppositeScrollValue) {
+ rotaryFlingBehavior?.observeEvent(event.timestamp, event.delta)
+ } else {
+ debugLog { "Opposite value after scroll :${event.delta}" }
+ }
+ rotaryScrollDistance += event.delta
+ }
+
+ scrollJob.cancel()
+
+ rotaryHaptics.handleScrollHaptic(event.delta)
+ debugLog { "Rotary scroll distance: $rotaryScrollDistance" }
+
+ previousScrollEventTime = time
+ scrollJob = coroutineScope.async {
+ scrollBehavior.handleEvent(rotaryScrollDistance)
+ }
+
+ if (rotaryFlingBehavior != null) {
+ flingJob.cancel()
+ flingJob = coroutineScope.async {
+ rotaryFlingBehavior?.trackFling(beforeFling = {
+ debugLog { "Calling before fling section" }
+ scrollJob.cancel()
+ scrollBehavior = scrollBehaviorFactory()
+ })
+ }
+ }
+ }
+
+ private fun isOppositeValueAfterScroll(delta: Float): Boolean =
+ sign(rotaryScrollDistance) * sign(delta) == -1f &&
+ (abs(delta) < abs(rotaryScrollDistance))
+
+ private fun isNewScrollEvent(timestamp: Long): Boolean {
+ val timeDelta = timestamp - previousScrollEventTime
+ return previousScrollEventTime == 0L || timeDelta > gestureThresholdTime
+ }
+
+ private fun resetTracking(timestamp: Long) {
+ scrollBehavior = scrollBehaviorFactory()
+ rotaryFlingBehavior = rotaryFlingBehaviorFactory()
+ rotaryFlingBehavior?.startFlingTracking(timestamp)
+ }
+}
+
+/**
+ * A scroll handler for Bezel(low-res) without snapping.
+ * This scroll handler supports fling. It can be set with RotaryFlingBehavior.
+ */
+internal class LowResRotaryScrollHandler(
+ private val rotaryFlingBehaviorFactory: () -> RotaryFlingBehavior?,
+ private val scrollBehaviorFactory: () -> RotaryScrollBehavior,
+) : RotaryScrollHandler {
+
+ private val gestureThresholdTime = 200L
+ private var previousScrollEventTime = 0L
+ private var rotaryScrollDistance = 0f
+
+ private var scrollJob: Job = CompletableDeferred<Unit>()
+ private var flingJob: Job = CompletableDeferred<Unit>()
+
+ private var rotaryFlingBehavior: RotaryFlingBehavior? = rotaryFlingBehaviorFactory()
+ private var scrollBehavior: RotaryScrollBehavior = scrollBehaviorFactory()
+
+ override suspend fun handleScrollEvent(
+ coroutineScope: CoroutineScope,
+ event: TimestampedDelta,
+ rotaryHaptics: RotaryHapticHandler,
+ ) {
+ val time = event.timestamp
+
+ if (isNewScrollEvent(time)) {
+ resetTracking(time)
+ rotaryScrollDistance = event.delta
+ } else {
+ rotaryFlingBehavior?.observeEvent(event.timestamp, event.delta)
+ rotaryScrollDistance += event.delta
+ }
+
+ scrollJob.cancel()
+ flingJob.cancel()
+
+ rotaryHaptics.handleScrollHaptic(event.delta)
+ debugLog { "Rotary scroll distance: $rotaryScrollDistance" }
+
+ previousScrollEventTime = time
+ scrollJob = coroutineScope.async {
+ scrollBehavior.handleEvent(rotaryScrollDistance)
+ }
+
+ flingJob = coroutineScope.async {
+ rotaryFlingBehavior?.trackFling(
+ beforeFling = {
+ debugLog { "Calling before fling section" }
+ scrollJob.cancel()
+ scrollBehavior = scrollBehaviorFactory()
+ },
+ )
+ }
+ }
+
+ private fun isNewScrollEvent(timestamp: Long): Boolean {
+ val timeDelta = timestamp - previousScrollEventTime
+ return previousScrollEventTime == 0L || timeDelta > gestureThresholdTime
+ }
+
+ private fun resetTracking(timestamp: Long) {
+ scrollBehavior = scrollBehaviorFactory()
+ debugLog { "Velocity tracker reset" }
+ rotaryFlingBehavior = rotaryFlingBehaviorFactory()
+ rotaryFlingBehavior?.startFlingTracking(timestamp)
+ }
+}
+
+/**
+ * A scroll handler for RSB(high-res) with snapping and without fling
+ * Snapping happens after a threshold is reached ( set in [RotarySnapBehavior])
+ *
+ * This scroll handler doesn't support fling.
+ */
+internal class HighResSnapHandler(
+ private val resistanceFactor: Float,
+ private val thresholdBehaviorFactory: () -> ThresholdBehavior,
+ private val snapBehaviourFactory: () -> RotarySnapBehavior,
+ private val scrollBehaviourFactory: () -> RotaryScrollBehavior,
+) : RotaryScrollHandler {
+ private val gestureThresholdTime = 200L
+ private val snapDelay = 100L
+ private val maxSnapsPerEvent = 2
+
+ private var scrollJob: Job = CompletableDeferred<Unit>()
+ private var snapJob: Job = CompletableDeferred<Unit>()
+
+ private var previousScrollEventTime = 0L
+ private var snapAccumulator = 0f
+ private var rotaryScrollDistance = 0f
+ private var scrollInProgress = false
+
+ private var snapBehaviour = snapBehaviourFactory()
+ private var scrollBehaviour = scrollBehaviourFactory()
+ private var thresholdBehavior = thresholdBehaviorFactory()
+
+ private val scrollEasing: Easing = CubicBezierEasing(0.0f, 0.0f, 0.5f, 1.0f)
+
+ override suspend fun handleScrollEvent(
+ coroutineScope: CoroutineScope,
+ event: TimestampedDelta,
+ rotaryHaptics: RotaryHapticHandler,
+ ) {
+ val time = event.timestamp
+
+ if (isNewScrollEvent(time)) {
+ debugLog { "New scroll event" }
+ resetTracking()
+ snapJob.cancel()
+ snapBehaviour = snapBehaviourFactory()
+ scrollBehaviour = scrollBehaviourFactory()
+ thresholdBehavior = thresholdBehaviorFactory()
+ thresholdBehavior.startThresholdTracking(time)
+ snapAccumulator = 0f
+ rotaryScrollDistance = 0f
+ }
+
+ if (!isOppositeValueAfterScroll(event.delta)) {
+ thresholdBehavior.observeEvent(event.timestamp, event.delta)
+ } else {
+ debugLog { "Opposite value after scroll :${event.delta}" }
+ }
+
+ thresholdBehavior.applySmoothing()
+ val snapThreshold = thresholdBehavior.snapThreshold()
+
+ snapAccumulator += event.delta
+ if (!snapJob.isActive) {
+ val resistanceCoeff =
+ 1 - scrollEasing.transform(rotaryScrollDistance.absoluteValue / snapThreshold)
+ rotaryScrollDistance += event.delta * resistanceCoeff
+ }
+
+ debugLog { "Snap accumulator: $snapAccumulator" }
+ debugLog { "Rotary scroll distance: $rotaryScrollDistance" }
+
+ debugLog { "snapThreshold: $snapThreshold" }
+ previousScrollEventTime = time
+
+ if (abs(snapAccumulator) > snapThreshold) {
+ scrollInProgress = false
+ scrollBehaviour = scrollBehaviourFactory()
+ scrollJob.cancel()
+
+ val snapDistance = (snapAccumulator / snapThreshold).toInt()
+ .coerceIn(-maxSnapsPerEvent..maxSnapsPerEvent)
+ snapAccumulator -= snapThreshold * snapDistance
+ val sequentialSnap = snapJob.isActive
+
+ debugLog {
+ "Snap threshold reached: snapDistance:$snapDistance, " +
+ "sequentialSnap: $sequentialSnap, " +
+ "snap accumulator remaining: $snapAccumulator"
+ }
+ if ((!snapBehaviour.topEdgeReached() && snapDistance < 0) ||
+ (!snapBehaviour.bottomEdgeReached() && snapDistance > 0)
+ ) {
+ rotaryHaptics.handleSnapHaptic(event.delta)
+ }
+
+ snapBehaviour.prepareSnapForItems(snapDistance, sequentialSnap)
+ if (!snapJob.isActive) {
+ snapJob.cancel()
+ snapJob = coroutineScope.async {
+ debugLog { "Snap started" }
+ try {
+ snapBehaviour.snapToTargetItem()
+ } finally {
+ debugLog { "Snap called finally" }
+ }
+ }
+ }
+ rotaryScrollDistance = 0f
+ } else {
+ if (!snapJob.isActive) {
+ scrollJob.cancel()
+ debugLog { "Scrolling for $rotaryScrollDistance/$resistanceFactor px" }
+ scrollJob = coroutineScope.async {
+ scrollBehaviour.handleEvent(rotaryScrollDistance / resistanceFactor)
+ }
+ delay(snapDelay)
+ scrollInProgress = false
+ scrollBehaviour = scrollBehaviourFactory()
+ rotaryScrollDistance = 0f
+ snapAccumulator = 0f
+ snapBehaviour.prepareSnapForItems(0, false)
+
+ snapJob.cancel()
+ snapJob = coroutineScope.async {
+ snapBehaviour.snapToClosestItem()
+ }
+ }
+ }
+ }
+
+ private fun isOppositeValueAfterScroll(delta: Float): Boolean =
+ sign(rotaryScrollDistance) * sign(delta) == -1f &&
+ (abs(delta) < abs(rotaryScrollDistance))
+
+ private fun isNewScrollEvent(timestamp: Long): Boolean {
+ val timeDelta = timestamp - previousScrollEventTime
+ return previousScrollEventTime == 0L || timeDelta > gestureThresholdTime
+ }
+
+ private fun resetTracking() {
+ scrollInProgress = true
+ }
+}
+
+/**
+ * A scroll handler for RSB(high-res) with snapping and without fling
+ * Snapping happens after a threshold is reached ( set in [RotarySnapBehavior])
+ *
+ * This scroll handler doesn't support fling.
+ */
+internal class LowResSnapHandler(
+ private val snapBehaviourFactory: () -> RotarySnapBehavior,
+) : RotaryScrollHandler {
+ private val gestureThresholdTime = 200L
+
+ private var snapJob: Job = CompletableDeferred<Unit>()
+
+ private var previousScrollEventTime = 0L
+ private var snapAccumulator = 0f
+ private var scrollInProgress = false
+
+ private var snapBehaviour = snapBehaviourFactory()
+
+ override suspend fun handleScrollEvent(
+ coroutineScope: CoroutineScope,
+ event: TimestampedDelta,
+ rotaryHaptics: RotaryHapticHandler,
+ ) {
+ val time = event.timestamp
+
+ if (isNewScrollEvent(time)) {
+ debugLog { "New scroll event" }
+ resetTracking()
+ snapJob.cancel()
+ snapBehaviour = snapBehaviourFactory()
+ snapAccumulator = 0f
+ }
+
+ snapAccumulator += event.delta
+
+ debugLog { "Snap accumulator: $snapAccumulator" }
+
+ previousScrollEventTime = time
+
+ if (abs(snapAccumulator) > 1f) {
+ scrollInProgress = false
+
+ val snapDistance = sign(snapAccumulator).toInt()
+ rotaryHaptics.handleSnapHaptic(event.delta)
+ val sequentialSnap = snapJob.isActive
+ debugLog {
+ "Snap threshold reached: snapDistance:$snapDistance, " +
+ "sequentialSnap: $sequentialSnap, " +
+ "snap accumulator: $snapAccumulator"
+ }
+
+ snapBehaviour.prepareSnapForItems(snapDistance, sequentialSnap)
+ if (!snapJob.isActive) {
+ snapJob.cancel()
+ snapJob = coroutineScope.async {
+ debugLog { "Snap started" }
+ try {
+ snapBehaviour.snapToTargetItem()
+ } finally {
+ debugLog { "Snap called finally" }
+ }
+ }
+ }
+ snapAccumulator = 0f
+ }
+ }
+
+ private fun isNewScrollEvent(timestamp: Long): Boolean {
+ val timeDelta = timestamp - previousScrollEventTime
+ return previousScrollEventTime == 0L || timeDelta > gestureThresholdTime
+ }
+
+ private fun resetTracking() {
+ scrollInProgress = true
+ }
+}
+
+internal class ThresholdBehavior(
+ private val rotaryScrollAdapter: RotaryScrollAdapter,
+ private val thresholdDivider: Float,
+ private val minVelocity: Float = 300f,
+ private val maxVelocity: Float = 3000f,
+ private val smoothingConstant: Float = 0.4f,
+) {
+ private val thresholdDividerEasing: Easing = CubicBezierEasing(0.5f, 0.0f, 0.5f, 1.0f)
+
+ private val rotaryVelocityTracker = RotaryVelocityTracker()
+
+ private var smoothedVelocity = 0f
+ fun startThresholdTracking(time: Long) {
+ rotaryVelocityTracker.start(time)
+ smoothedVelocity = 0f
+ }
+
+ fun observeEvent(timestamp: Long, delta: Float) {
+ rotaryVelocityTracker.move(timestamp, delta)
+ }
+
+ fun applySmoothing() {
+ if (rotaryVelocityTracker.velocity != 0.0f) {
+ // smooth the velocity
+ smoothedVelocity = exponentialSmoothing(
+ currentVelocity = rotaryVelocityTracker.velocity.absoluteValue,
+ prevVelocity = smoothedVelocity,
+ smoothingConstant = smoothingConstant,
+ )
+ }
+ debugLog { "rotaryVelocityTracker velocity: ${rotaryVelocityTracker.velocity}" }
+ debugLog { "SmoothedVelocity: $smoothedVelocity" }
+ }
+
+ fun snapThreshold(): Float {
+ val thresholdDividerFraction =
+ thresholdDividerEasing.transform(
+ inverseLerp(
+ minVelocity,
+ maxVelocity,
+ smoothedVelocity,
+ ),
+ )
+ return rotaryScrollAdapter.averageItemSize() / lerp(
+ 1f,
+ thresholdDivider,
+ thresholdDividerFraction,
+ )
+ }
+
+ private fun exponentialSmoothing(
+ currentVelocity: Float,
+ prevVelocity: Float,
+ smoothingConstant: Float,
+ ): Float =
+ smoothingConstant * currentVelocity + (1 - smoothingConstant) * prevVelocity
+}
+
+@Composable
+private fun rememberTimestampChannel() = remember {
+ Channel<TimestampedDelta>(capacity = Channel.CONFLATED)
+}
+
+private fun inverseLerp(start: Float, stop: Float, value: Float): Float {
+ return ((value - start) / (stop - start)).coerceIn(0f, 1f)
+}
diff --git a/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/rotaryinput/RotaryVelocityTracker.kt b/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/rotaryinput/RotaryVelocityTracker.kt
new file mode 100644
index 0000000..6e627c6
--- /dev/null
+++ b/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/rotaryinput/RotaryVelocityTracker.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright 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
+ *
+ * https://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.google.android.horologist.compose.rotaryinput
+
+import androidx.compose.ui.input.pointer.util.VelocityTracker1D
+
+/**
+ * A wrapper around VelocityTracker1D to provide support for rotary input.
+ */
+public class RotaryVelocityTracker {
+ private var velocityTracker: VelocityTracker1D = VelocityTracker1D(true)
+
+ /**
+ * Retrieve the last computed velocity.
+ */
+ public val velocity: Float
+ get() = velocityTracker.calculateVelocity()
+
+ /**
+ * Start tracking motion.
+ */
+ public fun start(currentTime: Long) {
+ velocityTracker.resetTracking()
+ velocityTracker.addDataPoint(currentTime, 0f)
+ }
+
+ /**
+ * Continue tracking motion as the input rotates.
+ */
+ public fun move(currentTime: Long, delta: Float) {
+ velocityTracker.addDataPoint(currentTime, delta)
+ }
+
+ /**
+ * Stop tracking motion.
+ */
+ public fun end() {
+ velocityTracker.resetTracking()
+ }
+}
diff --git a/packages/CredentialManager/res/xml/autofill_service_configuration.xml b/packages/CredentialManager/res/xml/autofill_service_configuration.xml
new file mode 100644
index 0000000..25cc094
--- /dev/null
+++ b/packages/CredentialManager/res/xml/autofill_service_configuration.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Sample backup rules file; uncomment and customize as necessary.
+ See https://developer.android.com/guide/topics/data/autobackup
+ for details.
+ Note: This file is ignored for devices older that API 31
+ See https://developer.android.com/about/versions/12/backup-restore
+-->
+<autofill-service-configuration
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:supportsInlineSuggestions="true"/>
\ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
index 8361877..473d7b6f 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
@@ -19,6 +19,7 @@
import android.app.slice.Slice
import android.content.ComponentName
import android.content.Context
+import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.credentials.Credential.TYPE_PASSWORD_CREDENTIAL
import android.credentials.ui.AuthenticationEntry
@@ -67,7 +68,7 @@
appPackageName: String
): String? {
return try {
- val pkgInfo = pm.getPackageInfo(appPackageName, PackageManager.PackageInfoFlags.of(0))
+ val pkgInfo = getPackageInfo(pm, appPackageName)
val applicationInfo = checkNotNull(pkgInfo.applicationInfo)
applicationInfo.loadSafeLabel(
pm, 0f,
@@ -90,10 +91,7 @@
// Test data has only package name not component name.
// For test data usage only.
try {
- val pkgInfo = pm.getPackageInfo(
- providerFlattenedComponentName,
- PackageManager.PackageInfoFlags.of(0)
- )
+ val pkgInfo = getPackageInfo(pm, providerFlattenedComponentName)
val applicationInfo = checkNotNull(pkgInfo.applicationInfo)
providerLabel =
applicationInfo.loadSafeLabel(
@@ -117,10 +115,7 @@
// Added for mdoc use case where the provider may not need to register a service and
// instead only relies on the registration api.
try {
- val pkgInfo = pm.getPackageInfo(
- component.packageName,
- PackageManager.PackageInfoFlags.of(0)
- )
+ val pkgInfo = getPackageInfo(pm, providerFlattenedComponentName)
val applicationInfo = checkNotNull(pkgInfo.applicationInfo)
providerLabel =
applicationInfo.loadSafeLabel(
@@ -144,6 +139,19 @@
}
}
+private fun getPackageInfo(
+ pm: PackageManager,
+ packageName: String
+): PackageInfo {
+ val flags = PackageManager.MATCH_INSTANT
+
+ return pm.getPackageInfo(
+ packageName,
+ PackageManager.PackageInfoFlags.of(
+ (flags).toLong())
+ )
+}
+
/** Utility functions for converting CredentialManager data structures to or from UI formats. */
class GetFlowUtils {
companion object {
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
new file mode 100644
index 0000000..943c2b4
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
@@ -0,0 +1,38 @@
+/*
+ * 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.credentialmanager.autofill
+
+import android.os.CancellationSignal
+import android.service.autofill.AutofillService
+import android.service.autofill.FillCallback
+import android.service.autofill.FillRequest
+import android.service.autofill.SaveRequest
+import android.service.autofill.SaveCallback
+
+class CredentialAutofillService : AutofillService() {
+ override fun onFillRequest(
+ request: FillRequest,
+ cancellationSignal: CancellationSignal,
+ callback: FillCallback
+ ) {
+ TODO("Not yet implemented")
+ }
+
+ override fun onSaveRequest(request: SaveRequest, callback: SaveCallback) {
+ TODO("Not yet implemented")
+ }
+}
\ No newline at end of file
diff --git a/packages/CredentialManager/wear/Android.bp b/packages/CredentialManager/wear/Android.bp
new file mode 100644
index 0000000..639e8d1
--- /dev/null
+++ b/packages/CredentialManager/wear/Android.bp
@@ -0,0 +1,53 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_app {
+ name: "ClockworkCredentialManager",
+ defaults: ["platform_app_defaults"],
+ certificate: "platform",
+ manifest: "AndroidManifest.xml",
+ srcs: ["src/**/*.kt"],
+ resource_dirs: ["res"],
+
+ dex_preopt: {
+ profile_guided: true,
+ profile: "profile.txt.prof",
+ },
+
+ static_libs: [
+ "Horologist",
+ "PlatformComposeCore",
+ "androidx.activity_activity-compose",
+ "androidx.appcompat_appcompat",
+ "androidx.compose.foundation_foundation",
+ "androidx.compose.foundation_foundation-layout",
+ "androidx.compose.material_material-icons-core",
+ "androidx.compose.material_material-icons-extended",
+ "androidx.compose.runtime_runtime",
+ "androidx.compose.ui_ui",
+ "androidx.core_core-ktx",
+ "androidx.lifecycle_lifecycle-extensions",
+ "androidx.lifecycle_lifecycle-livedata",
+ "androidx.lifecycle_lifecycle-runtime-ktx",
+ "androidx.lifecycle_lifecycle-viewmodel-compose",
+ "androidx.wear.compose_compose-foundation",
+ "androidx.wear.compose_compose-material",
+ "androidx.wear.compose_compose-navigation",
+ "kotlinx-coroutines-core",
+ ],
+
+ platform_apis: true,
+ privileged: true,
+
+ kotlincflags: ["-Xjvm-default=all"],
+
+ optimize: {
+ proguard_compatibility: false,
+ },
+}
diff --git a/packages/CredentialManager/wear/AndroidManifest.xml b/packages/CredentialManager/wear/AndroidManifest.xml
index 001a56d..90248734 100644
--- a/packages/CredentialManager/wear/AndroidManifest.xml
+++ b/packages/CredentialManager/wear/AndroidManifest.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
- * Copyright (c) 2017 Google Inc.
+ * Copyright (c) 2023 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,6 +19,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.credentialmanager">
+ <uses-feature android:name="android.hardware.type.watch" />
+
<uses-permission android:name="android.permission.LAUNCH_CREDENTIAL_SELECTOR"/>
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
<uses-permission android:name="android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS"/>
@@ -28,17 +30,15 @@
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:label="@string/app_name"
- android:supportsRtl="true"
- android:theme="@style/Theme.CredentialSelector">
+ android:supportsRtl="true">
<activity
- android:name=".CredentialSelectorActivity"
+ android:name=".ui.CredentialSelectorActivity"
android:exported="true"
android:permission="android.permission.LAUNCH_CREDENTIAL_SELECTOR"
android:launchMode="singleTop"
android:label="@string/app_name"
- android:excludeFromRecents="true"
- android:theme="@style/Theme.CredentialSelector">
+ android:excludeFromRecents="true">
</activity>
</application>
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/CredentialSelectorActivity.kt
similarity index 68%
rename from packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt
rename to packages/CredentialManager/wear/src/com/android/credentialmanager/ui/CredentialSelectorActivity.kt
index f7b2499..77fffaa 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/CredentialSelectorActivity.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 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,19 +14,29 @@
* limitations under the License.
*/
+package com.android.credentialmanager.ui
+
import android.os.Bundle
-import androidx.activity.compose.setContent
import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.navigation.NavHostController
import androidx.wear.compose.material.MaterialTheme
-import androidx.wear.compose.material.Text
+import androidx.wear.compose.navigation.rememberSwipeDismissableNavController
class CredentialSelectorActivity : ComponentActivity() {
+
+ lateinit var navController: NavHostController
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
+ setTheme(android.R.style.Theme_DeviceDefault)
+
setContent {
+ navController = rememberSwipeDismissableNavController()
+
MaterialTheme {
- Text("Credential Manager entry point")
+ WearApp(navController = navController)
}
}
}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/Screen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/Screen.kt
new file mode 100644
index 0000000..ee6ea5e
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/Screen.kt
@@ -0,0 +1,23 @@
+/*
+ * 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.0N
+ *
+ * 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.credentialmanager.ui
+
+sealed class Screen(
+ val route: String,
+) {
+ object Main : Screen("main")
+}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt
new file mode 100644
index 0000000..5ec0c8c
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt
@@ -0,0 +1,47 @@
+/*
+ * 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.0N
+ *
+ * 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.
+ */
+
+@file:OptIn(ExperimentalHorologistApi::class)
+
+package com.android.credentialmanager.ui
+
+import androidx.compose.runtime.Composable
+import androidx.navigation.NavHostController
+import androidx.wear.compose.foundation.rememberSwipeToDismissBoxState
+import androidx.wear.compose.navigation.rememberSwipeDismissableNavHostState
+import com.android.credentialmanager.ui.screens.MainScreen
+import com.google.android.horologist.annotations.ExperimentalHorologistApi
+import com.google.android.horologist.compose.navscaffold.WearNavScaffold
+import com.google.android.horologist.compose.navscaffold.composable
+
+@Composable
+fun WearApp(
+ navController: NavHostController
+) {
+ val swipeToDismissBoxState = rememberSwipeToDismissBoxState()
+ val navHostState =
+ rememberSwipeDismissableNavHostState(swipeToDismissBoxState = swipeToDismissBoxState)
+
+ WearNavScaffold(
+ startDestination = Screen.Main.route,
+ navController = navController,
+ state = navHostState,
+ ) {
+ composable(Screen.Main.route) {
+ MainScreen()
+ }
+ }
+}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/MainScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/MainScreen.kt
new file mode 100644
index 0000000..662d710
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/MainScreen.kt
@@ -0,0 +1,30 @@
+/*
+ * 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.0N
+ *
+ * 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.credentialmanager.ui.screens
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.wear.compose.material.Text
+
+@Composable
+fun MainScreen(modifier: Modifier = Modifier) {
+ Box(modifier = modifier, contentAlignment = Alignment.Center) {
+ Text("This is a placeholder for the main screen.")
+ }
+}
diff --git a/packages/PackageInstaller/AndroidManifest.xml b/packages/PackageInstaller/AndroidManifest.xml
index 5bd422b..a16f9f5 100644
--- a/packages/PackageInstaller/AndroidManifest.xml
+++ b/packages/PackageInstaller/AndroidManifest.xml
@@ -70,15 +70,18 @@
</intent-filter>
</activity>
+ <!-- NOTE: the workaround to fix the screen flash problem. Remember to check the problem
+ is resolved for new implementation -->
<activity android:name=".InstallStaging"
- android:exported="false" />
+ android:theme="@style/Theme.AlertDialogActivity.NoDim"
+ android:exported="false" />
<activity android:name=".DeleteStagedFileOnResult"
android:theme="@style/Theme.AlertDialogActivity.NoActionBar"
android:exported="false" />
<activity android:name=".PackageInstallerActivity"
- android:exported="false" />
+ android:exported="false" />
<activity android:name=".InstallInstalling"
android:theme="@style/Theme.AlertDialogActivity.NoAnimation"
diff --git a/packages/PackageInstaller/res/values/themes.xml b/packages/PackageInstaller/res/values/themes.xml
index 9a06229..aa1fa16 100644
--- a/packages/PackageInstaller/res/values/themes.xml
+++ b/packages/PackageInstaller/res/values/themes.xml
@@ -32,4 +32,9 @@
<item name="android:windowNoTitle">true</item>
</style>
+ <style name="Theme.AlertDialogActivity.NoDim">
+ <item name="android:windowNoTitle">true</item>
+ <item name="android:backgroundDimAmount">0</item>
+ </style>
+
</resources>
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
index 80e8761..d97fb54 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
@@ -306,6 +306,7 @@
}
private void initiateInstall() {
+ bindUi();
String pkgName = mPkgInfo.packageName;
// Check if there is already a package on the device with this name
// but it has been renamed to something else.
@@ -445,7 +446,6 @@
if (mAppSnippet != null) {
// load placeholder layout with OK button disabled until we override this layout in
// startInstallConfirm
- bindUi();
checkIfAllowedAndInitiateInstall();
}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
index 471f3b9..01596d2 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
@@ -32,6 +32,7 @@
import com.android.settingslib.spa.gallery.itemList.ItemOperatePageProvider
import com.android.settingslib.spa.gallery.itemList.OperateListPageProvider
import com.android.settingslib.spa.gallery.editor.SettingsOutlinedTextFieldPageProvider
+import com.android.settingslib.spa.gallery.editor.SettingsTextFieldPasswordPageProvider
import com.android.settingslib.spa.gallery.page.ArgumentPageProvider
import com.android.settingslib.spa.gallery.page.FooterPageProvider
import com.android.settingslib.spa.gallery.page.IllustrationPageProvider
@@ -44,6 +45,7 @@
import com.android.settingslib.spa.gallery.preference.PreferencePageProvider
import com.android.settingslib.spa.gallery.preference.SwitchPreferencePageProvider
import com.android.settingslib.spa.gallery.preference.TwoTargetSwitchPreferencePageProvider
+import com.android.settingslib.spa.gallery.scaffold.SearchScaffoldPageProvider
import com.android.settingslib.spa.gallery.ui.CategoryPageProvider
import com.android.settingslib.spa.gallery.ui.SpinnerPageProvider
import com.android.settingslib.spa.slice.SpaSliceBroadcastReceiver
@@ -92,6 +94,8 @@
SettingsOutlinedTextFieldPageProvider,
SettingsExposedDropdownMenuBoxPageProvider,
SettingsExposedDropdownMenuCheckBoxProvider,
+ SettingsTextFieldPasswordPageProvider,
+ SearchScaffoldPageProvider,
),
rootPages = listOf(
HomePageProvider.createSettingsPage(),
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/EditorMainPageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/EditorMainPageProvider.kt
index b74af21..4875ea9 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/EditorMainPageProvider.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/EditorMainPageProvider.kt
@@ -39,6 +39,8 @@
.build(),
SettingsExposedDropdownMenuCheckBoxProvider.buildInjectEntry().setLink(fromPage = owner)
.build(),
+ SettingsTextFieldPasswordPageProvider.buildInjectEntry().setLink(fromPage = owner)
+ .build(),
)
}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsExposedDropdownMenuBoxPageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsExposedDropdownMenuBoxPageProvider.kt
index 6d22e6a..5ffbe8ba 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsExposedDropdownMenuBoxPageProvider.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsExposedDropdownMenuBoxPageProvider.kt
@@ -19,7 +19,7 @@
import android.os.Bundle
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.tooling.preview.Preview
@@ -45,13 +45,13 @@
@Composable
override fun Page(arguments: Bundle?) {
- var selectedItem by remember { mutableStateOf("item1") }
+ var selectedItem by remember { mutableIntStateOf(-1) }
val options = listOf("item1", "item2", "item3")
RegularScaffold(title = TITLE) {
SettingsExposedDropdownMenuBox(
label = exposedDropdownMenuBoxLabel,
options = options,
- selectedOptionText = selectedItem,
+ selectedOptionIndex = selectedItem,
enabled = true,
onselectedOptionTextChange = { selectedItem = it })
}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsTextFieldPasswordPageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsTextFieldPasswordPageProvider.kt
new file mode 100644
index 0000000..fc51466e
--- /dev/null
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsTextFieldPasswordPageProvider.kt
@@ -0,0 +1,73 @@
+/*
+ * 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.settingslib.spa.gallery.editor
+
+import android.os.Bundle
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.tooling.preview.Preview
+import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
+import com.android.settingslib.spa.framework.common.SettingsPageProvider
+import com.android.settingslib.spa.framework.common.createSettingsPage
+import com.android.settingslib.spa.framework.compose.navigator
+import com.android.settingslib.spa.framework.theme.SettingsTheme
+import com.android.settingslib.spa.widget.editor.SettingsTextFieldPassword
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spa.widget.scaffold.RegularScaffold
+
+private const val TITLE = "Sample SettingsTextFieldPassword"
+
+object SettingsTextFieldPasswordPageProvider : SettingsPageProvider {
+ override val name = "SettingsTextFieldPassword"
+
+ override fun getTitle(arguments: Bundle?): String {
+ return TITLE
+ }
+
+ @Composable
+ override fun Page(arguments: Bundle?) {
+ var value by remember { mutableStateOf("value") }
+ RegularScaffold(title = TITLE) {
+ SettingsTextFieldPassword(
+ value = value,
+ label = "label",
+ onTextChange = { value = it })
+ }
+ }
+
+ fun buildInjectEntry(): SettingsEntryBuilder {
+ return SettingsEntryBuilder.createInject(owner = createSettingsPage())
+ .setUiLayoutFn {
+ Preference(object : PreferenceModel {
+ override val title = TITLE
+ override val onClick = navigator(name)
+ })
+ }
+ }
+}
+
+@Preview(showBackground = true)
+@Composable
+private fun SettingsTextFieldPasswordPagePreview() {
+ SettingsTheme {
+ SettingsTextFieldPasswordPageProvider.Page(null)
+ }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt
index 6cac220..b339b44 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt
@@ -41,6 +41,7 @@
import com.android.settingslib.spa.gallery.page.SettingsPagerPageProvider
import com.android.settingslib.spa.gallery.page.SliderPageProvider
import com.android.settingslib.spa.gallery.preference.PreferenceMainPageProvider
+import com.android.settingslib.spa.gallery.scaffold.SearchScaffoldPageProvider
import com.android.settingslib.spa.gallery.ui.CategoryPageProvider
import com.android.settingslib.spa.gallery.ui.SpinnerPageProvider
import com.android.settingslib.spa.widget.scaffold.HomeScaffold
@@ -55,6 +56,7 @@
PreferenceMainPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
OperateListPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
ArgumentPageProvider.buildInjectEntry("foo")!!.setLink(fromPage = owner).build(),
+ SearchScaffoldPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
SliderPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
SpinnerPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
SettingsPagerPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/scaffold/SearchScaffoldPageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/scaffold/SearchScaffoldPageProvider.kt
new file mode 100644
index 0000000..a1ab35b
--- /dev/null
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/scaffold/SearchScaffoldPageProvider.kt
@@ -0,0 +1,56 @@
+/*
+ * 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.settingslib.spa.gallery.scaffold
+
+import android.os.Bundle
+import androidx.compose.runtime.Composable
+import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
+import com.android.settingslib.spa.framework.common.SettingsPageProvider
+import com.android.settingslib.spa.framework.common.createSettingsPage
+import com.android.settingslib.spa.framework.compose.navigator
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spa.widget.scaffold.SearchScaffold
+import com.android.settingslib.spa.widget.ui.PlaceholderTitle
+
+private const val TITLE = "Sample SearchScaffold"
+
+object SearchScaffoldPageProvider : SettingsPageProvider {
+ override val name = "SearchScaffold"
+
+ private val owner = createSettingsPage()
+
+ fun buildInjectEntry() = SettingsEntryBuilder.createInject(owner = owner)
+ .setUiLayoutFn {
+ Preference(object : PreferenceModel {
+ override val title = TITLE
+ override val onClick = navigator(name)
+ })
+ }
+
+ @Composable
+ override fun Page(arguments: Bundle?) {
+ Page()
+ }
+}
+
+@Composable
+private fun Page() {
+ SearchScaffold(title = TITLE) { bottomPadding, searchQuery ->
+ PlaceholderTitle("Search query: ${searchQuery.value}")
+ }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuBox.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuBox.kt
index ec43aab..0d6c064 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuBox.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuBox.kt
@@ -41,9 +41,9 @@
fun SettingsExposedDropdownMenuBox(
label: String,
options: List<String>,
- selectedOptionText: String,
+ selectedOptionIndex: Int,
enabled: Boolean,
- onselectedOptionTextChange: (String) -> Unit,
+ onselectedOptionTextChange: (Int) -> Unit,
) {
var expanded by remember { mutableStateOf(false) }
ExposedDropdownMenuBox(
@@ -58,8 +58,8 @@
modifier = Modifier
.menuAnchor()
.fillMaxWidth(),
- value = selectedOptionText,
- onValueChange = onselectedOptionTextChange,
+ value = options.getOrElse(selectedOptionIndex) { "" },
+ onValueChange = { },
label = { Text(text = label) },
trailingIcon = {
ExposedDropdownMenuDefaults.TrailingIcon(
@@ -81,7 +81,7 @@
DropdownMenuItem(
text = { Text(option) },
onClick = {
- onselectedOptionTextChange(option)
+ onselectedOptionTextChange(options.indexOf(option))
expanded = false
},
contentPadding = ExposedDropdownMenuDefaults.ItemContentPadding,
@@ -103,7 +103,7 @@
SettingsExposedDropdownMenuBox(
label = "ExposedDropdownMenuBoxLabel",
options = options,
- selectedOptionText = item1,
+ selectedOptionIndex = 0,
enabled = true,
onselectedOptionTextChange = {})
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuCheckBox.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuCheckBox.kt
index 682b4ea..a258185 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuCheckBox.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuCheckBox.kt
@@ -31,6 +31,7 @@
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -54,7 +55,7 @@
enabled: Boolean,
onSelectedOptionStateChange: () -> Unit,
) {
- var dropDownWidth by remember { mutableStateOf(0) }
+ var dropDownWidth by remember { mutableIntStateOf(0) }
var expanded by remember { mutableStateOf(false) }
ExposedDropdownMenuBox(
expanded = expanded,
@@ -104,7 +105,7 @@
)
}
onSelectedOptionStateChange()
- }) {
+ }) {
Row(
modifier = Modifier
.fillMaxHeight()
@@ -131,7 +132,8 @@
val options = listOf("item1", "item2", "item3")
val selectedOptionsState = remember { mutableStateListOf("item1", "item2") }
SettingsTheme {
- SettingsExposedDropdownMenuCheckBox(label = "label",
+ SettingsExposedDropdownMenuCheckBox(
+ label = "label",
options = options,
selectedOptionsState = selectedOptionsState,
enabled = true,
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsTextFieldPassword.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsTextFieldPassword.kt
new file mode 100644
index 0000000..d0a6188
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsTextFieldPassword.kt
@@ -0,0 +1,92 @@
+/*
+ * 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.settingslib.spa.widget.editor
+
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.selection.toggleable
+import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.Visibility
+import androidx.compose.material.icons.outlined.VisibilityOff
+import androidx.compose.material3.Icon
+import androidx.compose.material3.OutlinedTextField
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.text.input.ImeAction
+import androidx.compose.ui.text.input.KeyboardType
+import androidx.compose.ui.text.input.PasswordVisualTransformation
+import androidx.compose.ui.text.input.VisualTransformation
+import androidx.compose.ui.tooling.preview.Preview
+import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.framework.theme.SettingsTheme
+
+@Composable
+fun SettingsTextFieldPassword(
+ value: String,
+ label: String,
+ onTextChange: (String) -> Unit,
+) {
+ var visibility by remember { mutableStateOf(false) }
+ OutlinedTextField(
+ modifier = Modifier
+ .padding(SettingsDimension.itemPadding)
+ .fillMaxWidth(),
+ value = value,
+ onValueChange = onTextChange,
+ label = { Text(text = label) },
+ keyboardOptions = KeyboardOptions(
+ keyboardType = KeyboardType.Password,
+ imeAction = ImeAction.Send
+ ),
+ trailingIcon = {
+ Icon(
+ imageVector = if (visibility) Icons.Outlined.VisibilityOff
+ else Icons.Outlined.Visibility,
+ contentDescription = "Visibility Icon",
+ modifier = Modifier
+ .testTag("Visibility Icon")
+ .size(SettingsDimension.itemIconSize)
+ .toggleable(visibility) {
+ visibility = !visibility
+ },
+ )
+ },
+ visualTransformation = if (visibility) VisualTransformation.None
+ else PasswordVisualTransformation()
+ )
+}
+
+@Preview
+@Composable
+private fun SettingsTextFieldPasswordPreview() {
+ var value by remember { mutableStateOf("value") }
+ SettingsTheme {
+ SettingsTextFieldPassword(
+ value = value,
+ label = "label",
+ onTextChange = { value = it },
+ )
+ }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuBoxTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuBoxTest.kt
index 09f5945..bc67e4c 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuBoxTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuBoxTest.kt
@@ -17,6 +17,7 @@
package com.android.settingslib.spa.widget.editor
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
@@ -40,13 +41,13 @@
@Test
fun exposedDropdownMenuBoxs_displayed() {
composeTestRule.setContent {
- var selectedItem by remember { mutableStateOf("item1") }
+ var selectedItem by remember { mutableStateOf(0) }
SettingsExposedDropdownMenuBox(
label = exposedDropdownMenuBoxLabel,
options = options,
- selectedOptionText = selectedItem,
+ selectedOptionIndex = selectedItem,
enabled = true,
- onselectedOptionTextChange = {selectedItem = it})
+ onselectedOptionTextChange = { selectedItem = it })
}
composeTestRule.onNodeWithText(exposedDropdownMenuBoxLabel, substring = true)
.assertIsDisplayed()
@@ -55,13 +56,13 @@
@Test
fun exposedDropdownMenuBoxs_expanded() {
composeTestRule.setContent {
- var selectedItem by remember { mutableStateOf("item1") }
+ var selectedItem by remember { mutableIntStateOf(0) }
SettingsExposedDropdownMenuBox(
label = exposedDropdownMenuBoxLabel,
options = options,
- selectedOptionText = selectedItem,
+ selectedOptionIndex = selectedItem,
enabled = true,
- onselectedOptionTextChange = {selectedItem = it})
+ onselectedOptionTextChange = { selectedItem = it })
}
composeTestRule.onNodeWithText(item2, substring = true)
.assertDoesNotExist()
@@ -74,13 +75,13 @@
@Test
fun exposedDropdownMenuBoxs_valueChanged() {
composeTestRule.setContent {
- var selectedItem by remember { mutableStateOf("item1") }
+ var selectedItem by remember { mutableIntStateOf(0) }
SettingsExposedDropdownMenuBox(
label = exposedDropdownMenuBoxLabel,
options = options,
- selectedOptionText = selectedItem,
+ selectedOptionIndex = selectedItem,
enabled = true,
- onselectedOptionTextChange = {selectedItem = it})
+ onselectedOptionTextChange = { selectedItem = it })
}
composeTestRule.onNodeWithText(item2, substring = true)
.assertDoesNotExist()
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsTextFieldPasswordTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsTextFieldPasswordTest.kt
new file mode 100644
index 0000000..6f2d1f9
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsTextFieldPasswordTest.kt
@@ -0,0 +1,85 @@
+/*
+ * 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.settingslib.spa.widget.editor
+
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertTextContains
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.performClick
+import androidx.compose.ui.test.performTextReplacement
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class SettingsTextFieldPasswordTest {
+ @get:Rule
+ val composeTestRule = createComposeRule()
+ private val label = "label"
+ private val value = "value"
+ private val valueChanged = "Value Changed"
+ private val visibilityIconTag = "Visibility Icon"
+
+ @Test
+ fun textFieldPassword_displayed() {
+ composeTestRule.setContent {
+ SettingsTextFieldPassword(
+ value = value,
+ label = label,
+ onTextChange = {})
+ }
+ composeTestRule.onNodeWithText(label, substring = true)
+ .assertIsDisplayed()
+ }
+
+ @Test
+ fun textFieldPassword_invisible() {
+ composeTestRule.setContent {
+ var value by remember { mutableStateOf(value) }
+ SettingsTextFieldPassword(
+ value = value,
+ label = label,
+ onTextChange = { value = it })
+ }
+ composeTestRule.onNodeWithText(value, substring = true)
+ .assertDoesNotExist()
+ }
+
+ @Test
+ fun textFieldPassword_visible_inputValue() {
+ composeTestRule.setContent {
+ var value by remember { mutableStateOf(value) }
+ SettingsTextFieldPassword(
+ value = value,
+ label = label,
+ onTextChange = { value = it })
+ }
+ composeTestRule.onNodeWithTag(visibilityIconTag)
+ .performClick()
+ composeTestRule.onNodeWithText(label, substring = true)
+ .performTextReplacement(valueChanged)
+ composeTestRule.onNodeWithText(label, substring = true)
+ .assertTextContains(valueChanged)
+ }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 8dc95f4..f9ea773 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1254,7 +1254,6 @@
<!-- Summary to show how many devices are connected in wifi hotspot [CHAR LIMIT=NONE] -->
<string name="wifi_tether_connected_summary">
{count, plural,
- =0 {0 device connected}
=1 {1 device connected}
other {# devices connected}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
index 91b852a..70126ac 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
@@ -198,19 +198,19 @@
@Override
public boolean setEnabled(BluetoothDevice device, boolean enabled) {
- boolean isEnabled = false;
+ boolean isSuccessful = false;
if (mService == null) {
return false;
}
if (enabled) {
if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
} else {
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
- return isEnabled;
+ return isSuccessful;
}
boolean isA2dpPlaying() {
if (mService == null) return false;
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
index c7a5bd8..38cd04e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
@@ -140,19 +140,19 @@
@Override
public boolean setEnabled(BluetoothDevice device, boolean enabled) {
- boolean isEnabled = false;
+ boolean isSuccessful = false;
if (mService == null) {
return false;
}
if (enabled) {
if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
} else {
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
- return isEnabled;
+ return isSuccessful;
}
boolean isAudioPlaying() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipSetCoordinatorProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipSetCoordinatorProfile.java
index c3f845c..45cafc6 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipSetCoordinatorProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipSetCoordinatorProfile.java
@@ -178,19 +178,19 @@
@Override
public boolean setEnabled(BluetoothDevice device, boolean enabled) {
- boolean isEnabled = false;
+ boolean isSuccessful = false;
if (mService == null || device == null) {
return false;
}
if (enabled) {
if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
} else {
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
- return isEnabled;
+ return isSuccessful;
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HapClientProfile.java
index 6b7f733..660090d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HapClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HapClientProfile.java
@@ -258,19 +258,19 @@
@Override
public boolean setEnabled(BluetoothDevice device, boolean enabled) {
- boolean isEnabled = false;
+ boolean isSuccessful = false;
if (mService == null || device == null) {
return false;
}
if (enabled) {
if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
} else {
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
- return isEnabled;
+ return isSuccessful;
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
index 7e5c124..b79f147 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
@@ -165,19 +165,19 @@
@Override
public boolean setEnabled(BluetoothDevice device, boolean enabled) {
- boolean isEnabled = false;
+ boolean isSuccessful = false;
if (mService == null) {
return false;
}
if (enabled) {
if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
} else {
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
- return isEnabled;
+ return isSuccessful;
}
public List<BluetoothDevice> getConnectedDevices() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
index 5fbb4c3..14fab16 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
@@ -231,19 +231,19 @@
@Override
public boolean setEnabled(BluetoothDevice device, boolean enabled) {
- boolean isEnabled = false;
+ boolean isSuccessful = false;
if (mService == null || device == null) {
return false;
}
if (enabled) {
if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
} else {
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
- return isEnabled;
+ return isSuccessful;
}
/**
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
index 66225a2..b0e0049 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
@@ -150,19 +150,19 @@
@Override
public boolean setEnabled(BluetoothDevice device, boolean enabled) {
- boolean isEnabled = false;
+ boolean isSuccessful = false;
if (mService == null) {
return false;
}
if (enabled) {
if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
} else {
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
- return isEnabled;
+ return isSuccessful;
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java
index 8a2c4f8..5468efb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java
@@ -123,13 +123,13 @@
@Override
public boolean setEnabled(BluetoothDevice device, boolean enabled) {
- boolean isEnabled = false;
+ boolean isSuccessful = false;
// if set preferred to false, then disconnect to the current device
if (!enabled) {
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
- return isEnabled;
+ return isSuccessful;
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
index 3c24b4a..5b91ac9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
@@ -126,19 +126,19 @@
@Override
public boolean setEnabled(BluetoothDevice device, boolean enabled) {
- boolean isEnabled = false;
+ boolean isSuccessful = false;
if (mService == null) {
return false;
}
if (enabled) {
if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
} else {
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
- return isEnabled;
+ return isSuccessful;
}
public String toString() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java
index e781f13..a93524f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java
@@ -24,24 +24,18 @@
import android.annotation.Nullable;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
-import android.bluetooth.BluetoothCodecConfig;
-import android.bluetooth.BluetoothCodecStatus;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothLeAudio;
import android.bluetooth.BluetoothProfile;
-import android.bluetooth.BluetoothUuid;
import android.content.Context;
import android.os.Build;
-import android.os.ParcelUuid;
import android.util.Log;
import androidx.annotation.RequiresApi;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.R;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
public class LeAudioProfile implements LocalBluetoothProfile {
@@ -233,19 +227,19 @@
@Override
public boolean setEnabled(BluetoothDevice device, boolean enabled) {
- boolean isEnabled = false;
+ boolean isSuccessful = false;
if (mService == null || device == null) {
return false;
}
if (enabled) {
if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
} else {
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
- return isEnabled;
+ return isSuccessful;
}
public String toString() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
index 4881a86..0a803bc 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
@@ -137,19 +137,19 @@
@Override
public boolean setEnabled(BluetoothDevice device, boolean enabled) {
- boolean isEnabled = false;
+ boolean isSuccessful = false;
if (mService == null) {
return false;
}
if (enabled) {
if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
} else {
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
- return isEnabled;
+ return isSuccessful;
}
public List<BluetoothDevice> getConnectedDevices() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java
index 75c1926..db1ba5e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java
@@ -138,19 +138,19 @@
@Override
public boolean setEnabled(BluetoothDevice device, boolean enabled) {
- boolean isEnabled = false;
+ boolean isSuccessful = false;
if (mService == null) {
return false;
}
if (enabled) {
if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
} else {
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
- return isEnabled;
+ return isSuccessful;
}
public List<BluetoothDevice> getConnectedDevices() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java
index 767df35..dec862b0 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java
@@ -105,7 +105,7 @@
@Override
public boolean setEnabled(BluetoothDevice device, boolean enabled) {
- boolean isEnabled = false;
+ boolean isSuccessful = false;
if (mService == null) {
return false;
}
@@ -117,12 +117,12 @@
mService.setConnectionPolicy(sink, CONNECTION_POLICY_FORBIDDEN);
}
}
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
} else {
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
- return isEnabled;
+ return isSuccessful;
}
public String toString() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
index 0d11293..65b89ba 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
@@ -151,19 +151,19 @@
@Override
public boolean setEnabled(BluetoothDevice device, boolean enabled) {
- boolean isEnabled = false;
+ boolean isSuccessful = false;
if (mService == null) {
return false;
}
if (enabled) {
if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
} else {
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
- return isEnabled;
+ return isSuccessful;
}
public String toString() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
index 9e2e4a1..784b821 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
@@ -108,16 +108,16 @@
@Override
public boolean setEnabled(BluetoothDevice device, boolean enabled) {
- boolean isEnabled = false;
+ boolean isSuccessful = false;
if (mService == null) {
return false;
}
if (!enabled) {
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
- return isEnabled;
+ return isSuccessful;
}
public String toString() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
index 104f1d7..0f8892f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
@@ -136,19 +136,19 @@
@Override
public boolean setEnabled(BluetoothDevice device, boolean enabled) {
- boolean isEnabled = false;
+ boolean isSuccessful = false;
if (mService == null) {
return false;
}
if (enabled) {
if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
} else {
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
- return isEnabled;
+ return isSuccessful;
}
public List<BluetoothDevice> getConnectedDevices() {
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 77925d6..6080101 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -203,6 +203,9 @@
"LowLightDreamLib",
"motion_tool_lib",
],
+ libs: [
+ "keepanno-annotations",
+ ],
manifest: "AndroidManifest.xml",
javacflags: ["-Adagger.fastInit=enabled"],
@@ -466,6 +469,7 @@
"android.test.runner",
"android.test.base",
"android.test.mock",
+ "keepanno-annotations",
],
kotlincflags: ["-Xjvm-default=all"],
aaptflags: [
@@ -497,6 +501,9 @@
static_libs: [
"SystemUI-tests-base",
],
+ libs: [
+ "keepanno-annotations",
+ ],
aaptflags: [
"--extra-packages",
"com.android.systemui",
diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING
index cb9e9ee..af6fa86 100644
--- a/packages/SystemUI/TEST_MAPPING
+++ b/packages/SystemUI/TEST_MAPPING
@@ -43,7 +43,9 @@
{
"exclude-annotation": "android.platform.test.annotations.Postsubmit"
}
- ]
+ ],
+ // The test doesn't run on AOSP Cuttlefish
+ "keywords": ["internal"]
},
{
// TODO(b/251476085): Consider merging with SystemUIGoogleScreenshotTests (in U+)
@@ -55,7 +57,9 @@
{
"exclude-annotation": "androidx.test.filters.FlakyTest"
}
- ]
+ ],
+ // The test doesn't run on AOSP Cuttlefish
+ "keywords": ["internal"]
},
{
// Permission indicators
@@ -112,10 +116,8 @@
"include-filter": "android.permissionui.cts.CameraMicIndicatorsPermissionTest"
}
]
- }
- ],
- "postsubmit": [
- {
+ },
+ {
"name": "SystemUIGoogleScreenshotTests",
"options": [
{
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/aconfig/accessibility.aconfig b/packages/SystemUI/accessibility/accessibilitymenu/aconfig/accessibility.aconfig
index 03cbc16..0f55f35 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/aconfig/accessibility.aconfig
+++ b/packages/SystemUI/accessibility/accessibilitymenu/aconfig/accessibility.aconfig
@@ -5,4 +5,11 @@
namespace: "accessibility"
description: "Provides/restores back button functionality for the a11yMenu settings page. Also, fixes sizing problems with large shortcut buttons."
bug: "298467628"
+}
+
+flag {
+ name: "a11y_menu_hide_before_taking_action"
+ namespace: "accessibility"
+ description: "Hides the AccessibilityMenuService UI before taking action instead of after."
+ bug: "292020123"
}
\ No newline at end of file
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/layout/grid_item.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/grid_item.xml
index 587395d..fd9a9c6 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/layout/grid_item.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/grid_item.xml
@@ -15,7 +15,7 @@
android:layout_centerHorizontal="true"
android:scaleType="fitCenter"/>
-<TextView
+ <TextView
android:id="@+id/shortcutLabel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -27,6 +27,6 @@
android:lines="2"
android:textSize="@dimen/label_text_size"
android:textAlignment="center"
- android:textAppearance="@android:style/TextAppearance.DeviceDefault.Widget.Button"/>
+ android:textColor="@color/grid_item_text_color"/>
</RelativeLayout>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-night/colors.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-night/colors.xml
index fbf2b07..f961bdb 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-night/colors.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-night/colors.xml
@@ -20,5 +20,6 @@
<color name="footer_icon_disabled_color">#5F6368</color>
<color name="colorControlNormal">#202124</color>
<color name="snackbar_bg_color">@android:color/white</color>
+ <color name="grid_item_text_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 d81d0d5..9722eb0 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/values/colors.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values/colors.xml
@@ -21,6 +21,7 @@
<color name="footer_icon_disabled_color">#ddd</color>
<color name="colorControlNormal">@android:color/white</color>
<color name="snackbar_bg_color">#313235</color>
+ <color name="grid_item_text_color">@android:color/black</color>
<color name="colorAccessibilityMenuIcon">#3AA757</color>
</resources>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java
index 27aade5..008732e 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java
@@ -260,6 +260,27 @@
// Shortcuts are repeatable in a11y menu rather than unique, so use tag ID to handle.
int viewTag = (int) view.getTag();
+ // First check if this was a shortcut which should keep a11y menu visible. If so,
+ // perform the shortcut and return without hiding the UI.
+ if (viewTag == ShortcutId.ID_BRIGHTNESS_UP_VALUE.ordinal()) {
+ adjustBrightness(BRIGHTNESS_UP_INCREMENT_GAMMA);
+ return;
+ } else if (viewTag == ShortcutId.ID_BRIGHTNESS_DOWN_VALUE.ordinal()) {
+ adjustBrightness(BRIGHTNESS_DOWN_INCREMENT_GAMMA);
+ return;
+ } else if (viewTag == ShortcutId.ID_VOLUME_UP_VALUE.ordinal()) {
+ adjustVolume(AudioManager.ADJUST_RAISE);
+ return;
+ } else if (viewTag == ShortcutId.ID_VOLUME_DOWN_VALUE.ordinal()) {
+ adjustVolume(AudioManager.ADJUST_LOWER);
+ return;
+ }
+
+ if (Flags.a11yMenuHideBeforeTakingAction()) {
+ // Hide the a11y menu UI before performing the following shortcut actions.
+ mA11yMenuLayout.hideMenu();
+ }
+
if (viewTag == ShortcutId.ID_ASSISTANT_VALUE.ordinal()) {
// Always restart the voice command activity, so that the UI is reloaded.
startActivityIfIntentIsSafe(
@@ -281,21 +302,11 @@
performGlobalActionInternal(GLOBAL_ACTION_NOTIFICATIONS);
} else if (viewTag == ShortcutId.ID_SCREENSHOT_VALUE.ordinal()) {
performGlobalActionInternal(GLOBAL_ACTION_TAKE_SCREENSHOT);
- } else if (viewTag == ShortcutId.ID_BRIGHTNESS_UP_VALUE.ordinal()) {
- adjustBrightness(BRIGHTNESS_UP_INCREMENT_GAMMA);
- return;
- } else if (viewTag == ShortcutId.ID_BRIGHTNESS_DOWN_VALUE.ordinal()) {
- adjustBrightness(BRIGHTNESS_DOWN_INCREMENT_GAMMA);
- return;
- } else if (viewTag == ShortcutId.ID_VOLUME_UP_VALUE.ordinal()) {
- adjustVolume(AudioManager.ADJUST_RAISE);
- return;
- } else if (viewTag == ShortcutId.ID_VOLUME_DOWN_VALUE.ordinal()) {
- adjustVolume(AudioManager.ADJUST_LOWER);
- return;
}
- mA11yMenuLayout.hideMenu();
+ if (!Flags.a11yMenuHideBeforeTakingAction()) {
+ mA11yMenuLayout.hideMenu();
+ }
}
/**
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
index 0c07616..1b56d4a 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
@@ -605,12 +605,28 @@
object : Controller by delegate {
override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {
listener?.onLaunchAnimationStart()
+
+ if (DEBUG_LAUNCH_ANIMATION) {
+ Log.d(
+ TAG,
+ "Calling controller.onLaunchAnimationStart(isExpandingFullyAbove=" +
+ "$isExpandingFullyAbove) [controller=$delegate]"
+ )
+ }
delegate.onLaunchAnimationStart(isExpandingFullyAbove)
}
override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {
listener?.onLaunchAnimationEnd()
iCallback?.invoke()
+
+ if (DEBUG_LAUNCH_ANIMATION) {
+ Log.d(
+ TAG,
+ "Calling controller.onLaunchAnimationEnd(isExpandingFullyAbove=" +
+ "$isExpandingFullyAbove) [controller=$delegate]"
+ )
+ }
delegate.onLaunchAnimationEnd(isExpandingFullyAbove)
}
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
index 767756e..17c74ba 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
@@ -263,9 +263,11 @@
override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {
delegate.onLaunchAnimationStart(isExpandingFullyAbove)
overlay.value = composeViewRoot.rootView.overlay as ViewGroupOverlay
+ cujType?.let { InteractionJankMonitor.getInstance().begin(composeViewRoot, it) }
}
override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {
+ cujType?.let { InteractionJankMonitor.getInstance().end(it) }
delegate.onLaunchAnimationEnd(isExpandingFullyAbove)
overlay.value = null
}
@@ -320,9 +322,8 @@
}
override fun jankConfigurationBuilder(): InteractionJankMonitor.Configuration.Builder? {
- // TODO(b/252723237): Add support for jank monitoring when animating from a
- // Composable.
- return null
+ val type = cuj?.cujType ?: return null
+ return InteractionJankMonitor.Configuration.Builder.withView(type, composeViewRoot)
}
}
}
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/AnimateToScene.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/AnimateToScene.kt
index 7536728..88944f10 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/AnimateToScene.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/AnimateToScene.kt
@@ -107,6 +107,9 @@
reversed: Boolean = false,
) {
val fromScene = layoutImpl.state.transitionState.currentScene
+ val isUserInput =
+ (layoutImpl.state.transitionState as? TransitionState.Transition)?.isUserInputDriven
+ ?: false
val animationSpec = layoutImpl.transitions.transitionSpec(fromScene, target).spec
val visibilityThreshold =
@@ -116,9 +119,9 @@
val targetProgress = if (reversed) 0f else 1f
val transition =
if (reversed) {
- OneOffTransition(target, fromScene, currentScene = target, animatable)
+ OneOffTransition(target, fromScene, currentScene = target, isUserInput, animatable)
} else {
- OneOffTransition(fromScene, target, currentScene = target, animatable)
+ OneOffTransition(fromScene, target, currentScene = target, isUserInput, animatable)
}
// Change the current layout state to use this new transition.
@@ -139,6 +142,7 @@
override val fromScene: SceneKey,
override val toScene: SceneKey,
override val currentScene: SceneKey,
+ override val isUserInputDriven: Boolean,
private val animatable: Animatable<Float, AnimationVector1D>,
) : TransitionState.Transition {
override val progress: Float
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/ObservableTransitionState.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/ObservableTransitionState.kt
index a625250..ccdec6e 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/ObservableTransitionState.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/ObservableTransitionState.kt
@@ -42,6 +42,17 @@
val fromScene: SceneKey,
val toScene: SceneKey,
val progress: Flow<Float>,
+
+ /**
+ * Whether the transition was originally triggered by user input rather than being
+ * programmatic. If this value is initially true, it will remain true until the transition
+ * fully completes, even if the user input that triggered the transition has ended. Any
+ * sub-transitions launched by this one will inherit this value. For example, if the user
+ * drags a pointer but does not exceed the threshold required to transition to another
+ * scene, this value will remain true after the pointer is no longer touching the screen and
+ * will be true in any transition created to animate back to the original position.
+ */
+ val isUserInputDriven: Boolean,
) : ObservableTransitionState()
}
@@ -62,6 +73,7 @@
fromScene = state.fromScene,
toScene = state.toScene,
progress = snapshotFlow { state.progress },
+ isUserInputDriven = state.isUserInputDriven,
)
}
}
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
index 47e3d5a..7a21211 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
@@ -68,5 +68,8 @@
* when flinging quickly during a swipe gesture.
*/
val progress: Float
+
+ /** Whether the transition was triggered by user input rather than being programmatic. */
+ val isUserInputDriven: Boolean
}
}
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SwipeToScene.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SwipeToScene.kt
index 2069ebd..790ea08 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SwipeToScene.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SwipeToScene.kt
@@ -137,6 +137,8 @@
return offset / distance
}
+ override val isUserInputDriven = true
+
/** The current offset caused by the drag gesture. */
var dragOffset by mutableFloatStateOf(0f)
diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt b/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
index cb2607a..2232370 100644
--- a/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
+++ b/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
@@ -122,6 +122,7 @@
assertThat(transition.toScene).isEqualTo(TestScenes.SceneB)
assertThat(transition.currentScene).isEqualTo(TestScenes.SceneA)
assertThat(transition.progress).isEqualTo(55.dp / LayoutWidth)
+ assertThat(transition.isUserInputDriven).isTrue()
// Release the finger. We should now be animating back to A (currentScene = SceneA) given
// that 55dp < positional threshold.
@@ -133,6 +134,7 @@
assertThat(transition.toScene).isEqualTo(TestScenes.SceneB)
assertThat(transition.currentScene).isEqualTo(TestScenes.SceneA)
assertThat(transition.progress).isEqualTo(55.dp / LayoutWidth)
+ assertThat(transition.isUserInputDriven).isTrue()
// Wait for the animation to finish. We should now be in scene A.
rule.waitForIdle()
@@ -154,6 +156,7 @@
assertThat(transition.toScene).isEqualTo(TestScenes.SceneC)
assertThat(transition.currentScene).isEqualTo(TestScenes.SceneA)
assertThat(transition.progress).isEqualTo(56.dp / LayoutHeight)
+ assertThat(transition.isUserInputDriven).isTrue()
// Release the finger. We should now be animating to C (currentScene = SceneC) given
// that 56dp >= positional threshold.
@@ -165,6 +168,7 @@
assertThat(transition.toScene).isEqualTo(TestScenes.SceneC)
assertThat(transition.currentScene).isEqualTo(TestScenes.SceneC)
assertThat(transition.progress).isEqualTo(56.dp / LayoutHeight)
+ assertThat(transition.isUserInputDriven).isTrue()
// Wait for the animation to finish. We should now be in scene C.
rule.waitForIdle()
diff --git a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/scene/CommunalSceneModule.kt b/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/scene/CommunalSceneModule.kt
new file mode 100644
index 0000000..f80a906
--- /dev/null
+++ b/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/scene/CommunalSceneModule.kt
@@ -0,0 +1,21 @@
+/*
+ * 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 com.android.systemui.scene
+
+import dagger.Module
+
+@Module interface CommunalSceneModule
diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/CommunalSceneModule.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/CommunalSceneModule.kt
new file mode 100644
index 0000000..94b5db2
--- /dev/null
+++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/CommunalSceneModule.kt
@@ -0,0 +1,28 @@
+/*
+ * 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.systemui.scene
+
+import com.android.systemui.communal.ui.compose.CommunalScene
+import com.android.systemui.scene.shared.model.Scene
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoSet
+
+@Module
+interface CommunalSceneModule {
+ @Binds @IntoSet fun communalScene(scene: CommunalScene): Scene
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
index e06a69b..5505eaf 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
@@ -82,7 +82,7 @@
) : ComposableScene {
override val key = SceneKey.Bouncer
- override fun destinationScenes(): StateFlow<Map<UserAction, SceneModel>> =
+ override val destinationScenes: StateFlow<Map<UserAction, SceneModel>> =
MutableStateFlow(
mapOf(
UserAction.Back to SceneModel(SceneKey.Lockscreen),
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt
new file mode 100644
index 0000000..0d2ba28
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt
@@ -0,0 +1,63 @@
+/*
+ * 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.systemui.communal.ui.compose
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import com.android.compose.animation.scene.SceneScope
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.scene.shared.model.Direction
+import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.scene.shared.model.SceneModel
+import com.android.systemui.scene.shared.model.UserAction
+import com.android.systemui.scene.ui.composable.ComposableScene
+import javax.inject.Inject
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+/** The communal scene shows glanceable hub when the device is locked and docked. */
+@SysUISingleton
+class CommunalScene @Inject constructor() : ComposableScene {
+ override val key = SceneKey.Communal
+
+ override val destinationScenes: StateFlow<Map<UserAction, SceneModel>> =
+ MutableStateFlow<Map<UserAction, SceneModel>>(
+ mapOf(
+ UserAction.Swipe(Direction.RIGHT) to SceneModel(SceneKey.Lockscreen),
+ )
+ )
+ .asStateFlow()
+
+ @Composable
+ override fun SceneScope.Content(modifier: Modifier) {
+ Box(
+ modifier = modifier.fillMaxSize().background(Color.White),
+ ) {
+ Text(
+ modifier = Modifier.align(Alignment.Center),
+ text = "Hello Communal!",
+ )
+ }
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
index 463253b..77daebb 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
@@ -66,13 +66,19 @@
) : ComposableScene {
override val key = SceneKey.Lockscreen
- override fun destinationScenes(): StateFlow<Map<UserAction, SceneModel>> =
+ override val destinationScenes: StateFlow<Map<UserAction, SceneModel>> =
viewModel.upDestinationSceneKey
- .map { pageKey -> destinationScenes(up = pageKey) }
+ .map { pageKey ->
+ destinationScenes(up = pageKey, left = viewModel.leftDestinationSceneKey)
+ }
.stateIn(
scope = applicationScope,
started = SharingStarted.Eagerly,
- initialValue = destinationScenes(up = null)
+ initialValue =
+ destinationScenes(
+ up = viewModel.upDestinationSceneKey.value,
+ left = viewModel.leftDestinationSceneKey,
+ )
)
@Composable
@@ -88,9 +94,11 @@
private fun destinationScenes(
up: SceneKey?,
+ left: SceneKey?,
): Map<UserAction, SceneModel> {
return buildMap {
up?.let { this[UserAction.Swipe(Direction.UP)] = SceneModel(up) }
+ left?.let { this[UserAction.Swipe(Direction.LEFT)] = SceneModel(left) }
this[UserAction.Swipe(Direction.DOWN)] = SceneModel(SceneKey.Shade)
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
index 7ac3901..1f9c3e6 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
@@ -58,13 +58,14 @@
) : ComposableScene {
override val key = SceneKey.QuickSettings
- override fun destinationScenes(): StateFlow<Map<UserAction, SceneModel>> =
+ private val _destinationScenes =
MutableStateFlow<Map<UserAction, SceneModel>>(
mapOf(
UserAction.Swipe(Direction.UP) to SceneModel(SceneKey.Shade),
)
)
.asStateFlow()
+ override val destinationScenes: StateFlow<Map<UserAction, SceneModel>> = _destinationScenes
@Composable
override fun SceneScope.Content(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
index 40b0b4a..2ee461f 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
@@ -38,7 +38,7 @@
class GoneScene @Inject constructor() : ComposableScene {
override val key = SceneKey.Gone
- override fun destinationScenes(): StateFlow<Map<UserAction, SceneModel>> =
+ override val destinationScenes: StateFlow<Map<UserAction, SceneModel>> =
MutableStateFlow<Map<UserAction, SceneModel>>(
mapOf(
UserAction.Swipe(Direction.DOWN) to SceneModel(SceneKey.Shade),
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
index 6a5a368..ef01266 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
@@ -79,7 +79,7 @@
val currentSceneKey = currentSceneModel.key
val currentScene = checkNotNull(sceneByKey[currentSceneKey])
val currentDestinations: Map<UserAction, SceneModel> by
- currentScene.destinationScenes().collectAsState()
+ currentScene.destinationScenes.collectAsState()
val state = remember { SceneTransitionLayoutState(currentSceneKey.toTransitionSceneKey()) }
val isRibbonEnabled = remember { SystemProperties.getBoolean("flexi.ribbon", false) }
@@ -116,7 +116,7 @@
if (sceneKey == currentSceneKey) {
currentDestinations
} else {
- composableScene.destinationScenes().value
+ composableScene.destinationScenes.value
}
.map { (userAction, destinationSceneModel) ->
toTransitionModels(userAction, destinationSceneModel)
@@ -158,6 +158,7 @@
fromScene = fromScene.toModel().key,
toScene = toScene.toModel().key,
progress = progress,
+ isUserInputDriven = isUserInputDriven,
)
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
index 404bf81..2848245 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
@@ -5,6 +5,7 @@
import com.android.systemui.scene.ui.composable.transitions.goneToQuickSettingsTransition
import com.android.systemui.scene.ui.composable.transitions.goneToShadeTransition
import com.android.systemui.scene.ui.composable.transitions.lockscreenToBouncerTransition
+import com.android.systemui.scene.ui.composable.transitions.lockscreenToCommunalTransition
import com.android.systemui.scene.ui.composable.transitions.lockscreenToGoneTransition
import com.android.systemui.scene.ui.composable.transitions.lockscreenToQuickSettingsTransition
import com.android.systemui.scene.ui.composable.transitions.lockscreenToShadeTransition
@@ -13,7 +14,7 @@
/**
* Comprehensive definition of all transitions between scenes in [SceneContainer].
*
- * Transitions are automatically reversible, so define only one transition per scene pair. By
+ * Transitions are automatically reversible, so define only one transition per scene pair. By\
* convention, use the more common transition direction when defining the pair order, e.g.
* Lockscreen to Bouncer rather than Bouncer to Lockscreen.
*
@@ -27,6 +28,7 @@
from(Gone, to = Shade) { goneToShadeTransition() }
from(Gone, to = QuickSettings) { goneToQuickSettingsTransition() }
from(Lockscreen, to = Bouncer) { lockscreenToBouncerTransition() }
+ from(Lockscreen, to = Communal) { lockscreenToCommunalTransition() }
from(Lockscreen, to = Shade) { lockscreenToShadeTransition() }
from(Lockscreen, to = QuickSettings) { lockscreenToQuickSettingsTransition() }
from(Lockscreen, to = Gone) { lockscreenToGoneTransition() }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/TransitionSceneKeys.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/TransitionSceneKeys.kt
index 8d0d705..5336bf6 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/TransitionSceneKeys.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/TransitionSceneKeys.kt
@@ -8,6 +8,7 @@
val Shade = SceneKey.Shade.toTransitionSceneKey()
val QuickSettings = SceneKey.QuickSettings.toTransitionSceneKey()
val Gone = SceneKey.Gone.toTransitionSceneKey()
+val Communal = SceneKey.Communal.toTransitionSceneKey()
// TODO(b/293899074): Remove this file once we can use the scene keys from SceneTransitionLayout.
fun SceneKey.toTransitionSceneKey(): SceneTransitionSceneKey {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToCommunalTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToCommunalTransition.kt
new file mode 100644
index 0000000..ea8110a
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToCommunalTransition.kt
@@ -0,0 +1,33 @@
+/*
+ * 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.systemui.scene.ui.composable.transitions
+
+import androidx.compose.animation.core.tween
+import com.android.compose.animation.scene.Edge
+import com.android.compose.animation.scene.TransitionBuilder
+import com.android.systemui.scene.ui.composable.Communal
+import com.android.systemui.scene.ui.composable.Lockscreen
+
+fun TransitionBuilder.lockscreenToCommunalTransition() {
+ spec = tween(durationMillis = 500)
+
+ // Translate lockscreen to the left.
+ translate(Lockscreen.rootElementKey, Edge.Left)
+
+ // Translate communal from the right.
+ translate(Communal.rootElementKey, Edge.Right)
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
index b105637..8832a11 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
@@ -87,7 +87,7 @@
) : ComposableScene {
override val key = SceneKey.Shade
- override fun destinationScenes(): StateFlow<Map<UserAction, SceneModel>> =
+ override val destinationScenes: StateFlow<Map<UserAction, SceneModel>> =
viewModel.upDestinationSceneKey
.map { sceneKey -> destinationScenes(up = sceneKey) }
.stateIn(
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt
index 92d2bd2..9d62e38 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt
@@ -163,9 +163,6 @@
const val TABLE_NAME = "flags"
val URI: Uri = BASE_URI.buildUpon().path(TABLE_NAME).build()
- /** Flag denoting whether the Wallpaper Picker should use the new, revamped UI. */
- const val FLAG_NAME_REVAMPED_WALLPAPER_UI = "revamped_wallpaper_ui"
-
/**
* Flag denoting whether the customizable lock screen quick affordances feature is enabled.
*/
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/StatusBarStateController.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/StatusBarStateController.java
index b7088d5..02085b9 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/StatusBarStateController.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/StatusBarStateController.java
@@ -124,13 +124,6 @@
default void onDozeAmountChanged(float linear, float eased) {}
/**
- * Callback to be notified when the fullscreen or immersive state changes.
- *
- * @param isFullscreen if any of the system bar is hidden by the focused window.
- */
- default void onFullscreenStateChanged(boolean isFullscreen) {}
-
- /**
* Callback to be notified when the pulsing state changes
*/
default void onPulsingChanged(boolean pulsing) {}
diff --git a/packages/SystemUI/proguard_common.flags b/packages/SystemUI/proguard_common.flags
index e2d8891..be1e655 100644
--- a/packages/SystemUI/proguard_common.flags
+++ b/packages/SystemUI/proguard_common.flags
@@ -1,8 +1,5 @@
-keep class com.android.systemui.VendorServices
-# the `#inject` methods are accessed via reflection to work on ContentProviders
--keepclassmembers class * extends com.android.systemui.dagger.SysUIComponent { void inject(***); }
-
# Needed to ensure callback field references are kept in their respective
# owning classes when the downstream callback registrars only store weak refs.
# TODO(b/264686688): Handle these cases with more targeted annotations.
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_password_motion_layout.xml b/packages/SystemUI/res-keyguard/layout/keyguard_password_motion_layout.xml
index 7abf9ae..173d57b 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_password_motion_layout.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_password_motion_layout.xml
@@ -26,7 +26,6 @@
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
- androidprv:layout_maxWidth="@dimen/keyguard_security_width"
android:layout_gravity="center_horizontal|bottom"
android:gravity="bottom">
@@ -36,6 +35,7 @@
android:id="@+id/password_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:maxWidth="@dimen/keyguard_security_width"
android:layout_gravity="center_horizontal"
android:clipChildren="false"
android:clipToPadding="false"
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pattern_motion_layout.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pattern_motion_layout.xml
new file mode 100644
index 0000000..b562d7b
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_pattern_motion_layout.xml
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** 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.
+*/
+-->
+
+<!-- This file is needed when flag lockscreen.enable_landscape is on
+ Required for landscape lockscreen on small screens. -->
+<com.android.keyguard.KeyguardPatternView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/keyguard_pattern_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center_horizontal|bottom"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:orientation="vertical">
+
+ <!-- Layout here is visually identical to the previous keyguard_pattern_view.
+ I.E., 'constraints here effectively the same as the previous linear layout' -->
+ <androidx.constraintlayout.motion.widget.MotionLayout
+ android:id="@+id/pattern_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center_horizontal"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:layoutDirection="ltr"
+ android:orientation="vertical"
+ android:maxWidth = "@dimen/biometric_auth_pattern_view_max_size"
+ androidprv:layoutDescription="@xml/keyguard_pattern_scene">
+
+ <!-- Guideline need to align pattern right of centre,
+ when on small screen landscape layout -->
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/pattern_center_guideline"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ androidprv:layout_constraintGuide_percent="0.5" />
+
+ <LinearLayout
+ android:id="@+id/keyguard_bouncer_message_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:layoutDirection="ltr"
+ android:orientation="vertical"
+ androidprv:layout_constraintTop_toTopOf="parent">
+
+ <include layout="@layout/keyguard_bouncer_message_area" />
+
+ <com.android.systemui.bouncer.ui.BouncerMessageView
+ android:id="@+id/bouncer_message_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical" />
+
+ </LinearLayout>
+
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/pattern_top_guideline"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ androidprv:layout_constraintGuide_percent="0" />
+
+ <com.android.internal.widget.LockPatternView
+ android:id="@+id/lockPatternView"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_marginBottom="8dp"
+ androidprv:layout_constraintVertical_bias="1.0"
+ androidprv:layout_constraintDimensionRatio="1.0"
+ androidprv:layout_constraintStart_toStartOf="parent"
+ androidprv:layout_constraintBottom_toTopOf="@+id/keyguard_selector_fade_container"
+ androidprv:layout_constraintEnd_toEndOf="parent"
+ androidprv:layout_constraintVertical_chainStyle="packed"
+ androidprv:layout_constraintTop_toBottomOf="@id/pattern_top_guideline"/>
+
+ <include
+ android:id="@+id/keyguard_selector_fade_container"
+ layout="@layout/keyguard_eca"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/keyguard_eca_bottom_margin"
+ android:layout_marginTop="@dimen/keyguard_eca_top_margin"
+ android:orientation="vertical"
+ androidprv:layout_constraintBottom_toBottomOf="parent"
+ androidprv:layout_constraintTop_toBottomOf="@+id/lockPatternView" />
+
+ </androidx.constraintlayout.motion.widget.MotionLayout>
+
+</com.android.keyguard.KeyguardPatternView>
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pin_motion_layout.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pin_motion_layout.xml
index 6835d59..6c79d5a 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_pin_motion_layout.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_pin_motion_layout.xml
@@ -34,6 +34,7 @@
android:id="@+id/pin_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:maxWidth="@dimen/keyguard_security_width"
android:clipChildren="false"
android:clipToPadding="false"
android:layoutDirection="ltr"
diff --git a/packages/SystemUI/res-keyguard/xml/keyguard_pattern_scene.xml b/packages/SystemUI/res-keyguard/xml/keyguard_pattern_scene.xml
new file mode 100644
index 0000000..6112411
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/xml/keyguard_pattern_scene.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<MotionScene
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:motion="http://schemas.android.com/apk/res-auto"
+ xmlns:androidprv="http://schemas.android.com/apk/res-auto">
+
+ <Transition
+ motion:constraintSetStart="@id/single_constraints"
+ motion:constraintSetEnd="@+id/split_constraints"
+ motion:duration="0"
+ motion:autoTransition="none"/>
+
+ <!-- No changes to default layout -->
+ <ConstraintSet android:id="@+id/single_constraints"/>
+
+ <ConstraintSet android:id="@+id/split_constraints">
+
+ <Constraint
+ android:id="@+id/keyguard_bouncer_message_container"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ androidprv:layout_constraintEnd_toStartOf="@+id/pattern_center_guideline"
+ androidprv:layout_constraintStart_toStartOf="parent"
+ androidprv:layout_constraintTop_toTopOf="parent" />
+ <Constraint
+ android:id="@+id/lockPatternView"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ androidprv:layout_constraintDimensionRatio="1.0"
+ androidprv:layout_constraintVertical_bias="0.5"
+ androidprv:layout_constraintBottom_toBottomOf="parent"
+ androidprv:layout_constraintEnd_toEndOf="parent"
+ androidprv:layout_constraintStart_toStartOf="@+id/pattern_center_guideline"
+ androidprv:layout_constraintTop_toTopOf="parent"
+ android:layout_marginBottom="0dp" />
+ <Constraint
+ android:id="@+id/keyguard_selector_fade_container"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ androidprv:layout_constraintBottom_toBottomOf="parent"
+ androidprv:layout_constraintEnd_toStartOf="@+id/pattern_center_guideline"
+ androidprv:layout_constraintStart_toStartOf="parent"
+ android:layout_marginBottom="@dimen/keyguard_eca_bottom_margin"
+ android:layout_marginTop="@dimen/keyguard_eca_top_margin" />
+
+ </ConstraintSet>
+</MotionScene>
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/xml/keyguard_pin_scene.xml b/packages/SystemUI/res-keyguard/xml/keyguard_pin_scene.xml
index 44af9ef..2a1270c 100644
--- a/packages/SystemUI/res-keyguard/xml/keyguard_pin_scene.xml
+++ b/packages/SystemUI/res-keyguard/xml/keyguard_pin_scene.xml
@@ -26,12 +26,10 @@
motion:constraintSetStart="@id/single_constraints"
motion:constraintSetEnd="@+id/split_constraints"
motion:duration="0"
- motion:autoTransition="none">
- </Transition>
+ motion:autoTransition="none"/>
- <ConstraintSet android:id="@+id/single_constraints">
- <!-- No changes to default layout -->
- </ConstraintSet>
+ <!-- No changes to default layout -->
+ <ConstraintSet android:id="@+id/single_constraints"/>
<ConstraintSet android:id="@+id/split_constraints">
diff --git a/packages/SystemUI/res/color/notification_overlay_color.xml b/packages/SystemUI/res/color/notification_overlay_color.xml
new file mode 100644
index 0000000..c24bff9
--- /dev/null
+++ b/packages/SystemUI/res/color/notification_overlay_color.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+ <item android:state_pressed="true" android:color="?androidprv:attr/materialColorOnSurface" android:alpha="0.15" />
+ <item android:state_hovered="true" android:color="?androidprv:attr/materialColorOnSurface" android:alpha="0.11" />
+ <item android:color="@color/transparent" />
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/notification_material_bg.xml b/packages/SystemUI/res/drawable/notification_material_bg.xml
index 61a8e8e..3eaa618 100644
--- a/packages/SystemUI/res/drawable/notification_material_bg.xml
+++ b/packages/SystemUI/res/drawable/notification_material_bg.xml
@@ -15,7 +15,7 @@
~ limitations under the License
-->
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:color="?android:attr/colorControlHighlight">
<item>
@@ -23,4 +23,9 @@
<solid android:color="?androidprv:attr/colorSurface" />
</shape>
</item>
-</ripple>
\ No newline at end of file
+ <item>
+ <shape>
+ <solid android:color="@color/notification_overlay_color" />
+ </shape>
+ </item>
+</layer-list>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/combined_qs_header.xml b/packages/SystemUI/res/layout/combined_qs_header.xml
index 60a78d6..beb481a 100644
--- a/packages/SystemUI/res/layout/combined_qs_header.xml
+++ b/packages/SystemUI/res/layout/combined_qs_header.xml
@@ -130,6 +130,7 @@
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="right|center_vertical"
+ android:gravity="center_vertical"
android:paddingStart="@dimen/hover_system_icons_container_padding_start"
android:paddingEnd="@dimen/hover_system_icons_container_padding_end"
android:paddingTop="@dimen/hover_system_icons_container_padding_top"
diff --git a/packages/SystemUI/res/layout/screen_record_dialog.xml b/packages/SystemUI/res/layout/screen_record_dialog.xml
index f6ce70d..dc560bf 100644
--- a/packages/SystemUI/res/layout/screen_record_dialog.xml
+++ b/packages/SystemUI/res/layout/screen_record_dialog.xml
@@ -150,19 +150,6 @@
android:layout_height="match_parent"
android:layout_weight="1"/>
- <!-- Temporary entrypoint for the partial screensharing used for teamfooding -->
- <!-- TODO(b/236838395) remove this and use redesigned dialog -->
- <TextView
- android:id="@+id/button_app"
- android:visibility="gone"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="0"
- android:layout_gravity="end"
- android:layout_marginEnd="8dp"
- android:text="App"
- style="@style/Widget.Dialog.Button.BorderButton" />
-
<TextView
android:id="@+id/button_start"
android:layout_width="wrap_content"
diff --git a/packages/SystemUI/res/layout/status_bar_notification_row.xml b/packages/SystemUI/res/layout/status_bar_notification_row.xml
index 356b36f..fa7f9cf 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_row.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_row.xml
@@ -30,12 +30,14 @@
<com.android.systemui.statusbar.notification.row.NotificationBackgroundView
android:id="@+id/backgroundNormal"
android:layout_width="match_parent"
- android:layout_height="match_parent" />
+ android:layout_height="match_parent"
+ android:duplicateParentState="true"/>
<com.android.systemui.statusbar.notification.row.NotificationBackgroundView
android:id="@+id/backgroundDimmed"
android:layout_width="match_parent"
- android:layout_height="match_parent" />
+ android:layout_height="match_parent"
+ android:duplicateParentState="true"/>
<com.android.systemui.statusbar.notification.row.NotificationContentView
android:id="@+id/expanded"
diff --git a/packages/SystemUI/res/values-land/bools.xml b/packages/SystemUI/res/values-land/bools.xml
new file mode 100644
index 0000000..e24792d
--- /dev/null
+++ b/packages/SystemUI/res/values-land/bools.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * 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.
+*/
+-->
+<resources>
+ <!-- Only use small clock on lockscreen.
+ True here because only small clock used on small devices in landscape -->
+ <bool name="force_small_clock_on_lockscreen">true</bool>
+</resources>
diff --git a/packages/SystemUI/res/values-sw600dp-land/bools.xml b/packages/SystemUI/res/values-sw600dp-land/bools.xml
new file mode 100644
index 0000000..c4d77e8
--- /dev/null
+++ b/packages/SystemUI/res/values-sw600dp-land/bools.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * 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.
+*/
+-->
+<resources>
+ <!-- Only use small clock on lockscreen.
+ False here because large clock is allowed on large devices in landscape -->
+ <bool name="force_small_clock_on_lockscreen">false</bool>
+</resources>
diff --git a/packages/SystemUI/res/values/bools.xml b/packages/SystemUI/res/values/bools.xml
index 91d3a88..3956662 100644
--- a/packages/SystemUI/res/values/bools.xml
+++ b/packages/SystemUI/res/values/bools.xml
@@ -59,4 +59,8 @@
True here so bouncers constraints are updated when rotating on small screens -->
<bool name="update_bouncer_constraints">true</bool>
+
+ <!-- Only use small clock on lockscreen.
+ False here because large clock used by default, unless otherwise specified -->
+ <bool name="force_small_clock_on_lockscreen">false</bool>
</resources>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java
index 0c2341f..3cb5bc7 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java
@@ -59,9 +59,8 @@
* Updates the matrix based on the provided parameters
*/
public void updateThumbnailMatrix(Rect thumbnailBounds, ThumbnailData thumbnailData,
- int canvasWidth, int canvasHeight, int screenWidthPx, int screenHeightPx,
- int taskbarSize, boolean isLargeScreen,
- int currentRotation, boolean isRtl) {
+ int canvasWidth, int canvasHeight, boolean isLargeScreen, int currentRotation,
+ boolean isRtl) {
boolean isRotated = false;
boolean isOrientationDifferent;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt
index bb799fc..d7019b5 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt
@@ -27,7 +27,7 @@
override var userId: Int = 0,
override var listening: Boolean = false,
// keep sorted
- var allowedDisplayState: Boolean = false,
+ var allowedDisplayStateWhileAwake: Boolean = false,
var alternateBouncerShowing: Boolean = false,
var authInterruptActive: Boolean = false,
var biometricSettingEnabledForUser: Boolean = false,
@@ -58,7 +58,7 @@
userId.toString(),
listening.toString(),
// keep sorted
- allowedDisplayState.toString(),
+ allowedDisplayStateWhileAwake.toString(),
alternateBouncerShowing.toString(),
authInterruptActive.toString(),
biometricSettingEnabledForUser.toString(),
@@ -98,7 +98,7 @@
userId = model.userId
listening = model.listening
// keep sorted
- allowedDisplayState = model.allowedDisplayState
+ allowedDisplayStateWhileAwake = model.allowedDisplayStateWhileAwake
alternateBouncerShowing = model.alternateBouncerShowing
authInterruptActive = model.authInterruptActive
biometricSettingEnabledForUser = model.biometricSettingEnabledForUser
@@ -143,7 +143,7 @@
"userId",
"listening",
// keep sorted
- "allowedDisplayState",
+ "allowedDisplayStateWhileAwake",
"alternateBouncerShowing",
"authInterruptActive",
"biometricSettingEnabledForUser",
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
index bfd43dc..abd1563 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
@@ -16,8 +16,6 @@
package com.android.keyguard;
-import static com.android.systemui.flags.Flags.LOCKSCREEN_ENABLE_LANDSCAPE;
-
import android.annotation.CallSuper;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -259,8 +257,6 @@
mFalsingCollector, mKeyguardViewController,
mDevicePostureController, mFeatureFlags);
} else if (keyguardInputView instanceof KeyguardPINView) {
- ((KeyguardPINView) keyguardInputView).setIsLockScreenLandscapeEnabled(
- mFeatureFlags.isEnabled(LOCKSCREEN_ENABLE_LANDSCAPE));
return new KeyguardPinViewController((KeyguardPINView) keyguardInputView,
mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
index bb3e759..72b4ae5 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
@@ -17,6 +17,7 @@
package com.android.keyguard;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+
import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_PIN_APPEAR;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_PIN_DISAPPEAR;
import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_CLOSED;
@@ -105,21 +106,21 @@
}
void onDevicePostureChanged(@DevicePostureInt int posture) {
- if (mLastDevicePosture != posture) {
- mLastDevicePosture = posture;
+ if (mLastDevicePosture == posture) return;
+ mLastDevicePosture = posture;
- if (mIsLockScreenLandscapeEnabled) {
- boolean useSplitBouncerAfterFold =
- mLastDevicePosture == DEVICE_POSTURE_CLOSED
- && getResources().getConfiguration().orientation == ORIENTATION_LANDSCAPE;
+ if (mIsLockScreenLandscapeEnabled) {
+ boolean useSplitBouncerAfterFold =
+ mLastDevicePosture == DEVICE_POSTURE_CLOSED
+ && getResources().getConfiguration().orientation == ORIENTATION_LANDSCAPE
+ && getResources().getBoolean(R.bool.update_bouncer_constraints);
- if (mAlreadyUsingSplitBouncer != useSplitBouncerAfterFold) {
- updateConstraints(useSplitBouncerAfterFold);
- }
+ if (mAlreadyUsingSplitBouncer != useSplitBouncerAfterFold) {
+ updateConstraints(useSplitBouncerAfterFold);
}
-
- updateMargins();
}
+
+ updateMargins();
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
index d30f497..8d5fc04 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
@@ -158,7 +158,8 @@
if (mIsLockScreenLandscapeEnabled) {
boolean useSplitBouncerAfterFold =
mLastDevicePosture == DEVICE_POSTURE_CLOSED
- && getResources().getConfiguration().orientation == ORIENTATION_LANDSCAPE;
+ && getResources().getConfiguration().orientation == ORIENTATION_LANDSCAPE
+ && getResources().getBoolean(R.bool.update_bouncer_constraints);
if (mAlreadyUsingSplitBouncer != useSplitBouncerAfterFold) {
updateConstraints(useSplitBouncerAfterFold);
@@ -170,20 +171,14 @@
@Override
protected void updateConstraints(boolean useSplitBouncer) {
mAlreadyUsingSplitBouncer = useSplitBouncer;
- KeyguardSecurityViewFlipper.LayoutParams params =
- (KeyguardSecurityViewFlipper.LayoutParams) getLayoutParams();
-
if (useSplitBouncer) {
- if (mContainerMotionLayout == null) return;
mContainerMotionLayout.jumpToState(R.id.split_constraints);
- params.maxWidth = Integer.MAX_VALUE;
+ mContainerMotionLayout.setMaxWidth(Integer.MAX_VALUE);
} else {
mContainerMotionLayout.jumpToState(R.id.single_constraints);
- params.maxWidth = getResources()
- .getDimensionPixelSize(R.dimen.keyguard_security_width);
+ mContainerMotionLayout.setMaxWidth(getResources()
+ .getDimensionPixelSize(R.dimen.keyguard_security_width));
}
-
- setLayoutParams(params);
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
index 2bdf46e..56706b5 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
@@ -15,9 +15,13 @@
*/
package com.android.keyguard;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+
+import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_CLOSED;
import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_HALF_OPENED;
import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_UNKNOWN;
+import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Rect;
@@ -29,6 +33,7 @@
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
+import androidx.constraintlayout.motion.widget.MotionLayout;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.constraintlayout.widget.ConstraintSet;
@@ -75,7 +80,10 @@
BouncerKeyguardMessageArea mSecurityMessageDisplay;
private View mEcaView;
- private ConstraintLayout mContainer;
+ @Nullable private MotionLayout mContainerMotionLayout;
+ @Nullable private ConstraintLayout mContainerConstraintLayout;
+ private boolean mAlreadyUsingSplitBouncer = false;
+ private boolean mIsLockScreenLandscapeEnabled = false;
@DevicePostureInt private int mLastDevicePosture = DEVICE_POSTURE_UNKNOWN;
public KeyguardPatternView(Context context) {
@@ -98,16 +106,44 @@
mContext, android.R.interpolator.fast_out_linear_in));
}
+ /**
+ * Use motion layout (new bouncer implementation) if LOCKSCREEN_ENABLE_LANDSCAPE flag is
+ * enabled, instead of constraint layout (old bouncer implementation)
+ */
+ public void setIsLockScreenLandscapeEnabled(boolean isLockScreenLandscapeEnabled) {
+ mIsLockScreenLandscapeEnabled = isLockScreenLandscapeEnabled;
+ findContainerLayout();
+ }
+
+ private void findContainerLayout() {
+ if (mIsLockScreenLandscapeEnabled) {
+ mContainerMotionLayout = findViewById(R.id.pattern_container);
+ } else {
+ mContainerConstraintLayout = findViewById(R.id.pattern_container);
+ }
+ }
+
@Override
protected void onConfigurationChanged(Configuration newConfig) {
updateMargins();
}
void onDevicePostureChanged(@DevicePostureInt int posture) {
- if (mLastDevicePosture != posture) {
- mLastDevicePosture = posture;
- updateMargins();
+ if (mLastDevicePosture == posture) return;
+ mLastDevicePosture = posture;
+
+ if (mIsLockScreenLandscapeEnabled) {
+ boolean useSplitBouncerAfterFold =
+ mLastDevicePosture == DEVICE_POSTURE_CLOSED
+ && getResources().getConfiguration().orientation == ORIENTATION_LANDSCAPE
+ && getResources().getBoolean(R.bool.update_bouncer_constraints);
+
+ if (mAlreadyUsingSplitBouncer != useSplitBouncerAfterFold) {
+ updateConstraints(useSplitBouncerAfterFold);
+ }
}
+
+ updateMargins();
}
private void updateMargins() {
@@ -115,12 +151,36 @@
float halfOpenPercentage =
mContext.getResources().getFloat(R.dimen.half_opened_bouncer_height_ratio);
- ConstraintSet cs = new ConstraintSet();
- cs.clone(mContainer);
- cs.setGuidelinePercent(R.id.pattern_top_guideline,
- mLastDevicePosture == DEVICE_POSTURE_HALF_OPENED
- ? halfOpenPercentage : 0.0f);
- cs.applyTo(mContainer);
+ if (mIsLockScreenLandscapeEnabled) {
+ ConstraintSet cs = mContainerMotionLayout.getConstraintSet(R.id.single_constraints);
+ cs.setGuidelinePercent(R.id.pattern_top_guideline,
+ mLastDevicePosture == DEVICE_POSTURE_HALF_OPENED ? halfOpenPercentage : 0.0f);
+ cs.applyTo(mContainerMotionLayout);
+ } else {
+ ConstraintSet cs = new ConstraintSet();
+ cs.clone(mContainerConstraintLayout);
+ cs.setGuidelinePercent(R.id.pattern_top_guideline,
+ mLastDevicePosture == DEVICE_POSTURE_HALF_OPENED ? halfOpenPercentage : 0.0f);
+ cs.applyTo(mContainerConstraintLayout);
+ }
+ }
+
+ /**
+ * Updates the keyguard view's constraints (single or split constraints).
+ * Split constraints are only used for small landscape screens.
+ * Only called when flag LANDSCAPE_ENABLE_LOCKSCREEN is enabled.
+ */
+ @Override
+ protected void updateConstraints(boolean useSplitBouncer) {
+ mAlreadyUsingSplitBouncer = useSplitBouncer;
+ if (useSplitBouncer) {
+ mContainerMotionLayout.jumpToState(R.id.split_constraints);
+ mContainerMotionLayout.setMaxWidth(Integer.MAX_VALUE);
+ } else {
+ mContainerMotionLayout.jumpToState(R.id.single_constraints);
+ mContainerMotionLayout.setMaxWidth(getResources()
+ .getDimensionPixelSize(R.dimen.biometric_auth_pattern_view_max_size));
+ }
}
@Override
@@ -130,7 +190,6 @@
mLockPatternView = findViewById(R.id.lockPatternView);
mEcaView = findViewById(R.id.keyguard_selector_fade_container);
- mContainer = findViewById(R.id.pattern_container);
}
@Override
@@ -209,7 +268,7 @@
getAnimationListener(InteractionJankMonitor.CUJ_LOCKSCREEN_PATTERN_DISAPPEAR));
DisappearAnimationUtils disappearAnimationUtils = needsSlowUnlockTransition
- ? mDisappearAnimationUtilsLocked : mDisappearAnimationUtils;
+ ? mDisappearAnimationUtilsLocked : mDisappearAnimationUtils;
disappearAnimationUtils.startAnimation2d(mLockPatternView.getCellStates(),
() -> {
enableClipping(true);
@@ -220,7 +279,7 @@
if (!TextUtils.isEmpty(mSecurityMessageDisplay.getText())) {
mDisappearAnimationUtils.createAnimation(mSecurityMessageDisplay, 0,
(long) (200 * durationMultiplier),
- - mDisappearAnimationUtils.getStartTranslation() * 3,
+ -mDisappearAnimationUtils.getStartTranslation() * 3,
false /* appearing */,
mDisappearAnimationUtils.getInterpolator(),
null /* finishRunnable */);
@@ -229,9 +288,16 @@
}
private void enableClipping(boolean enable) {
- setClipChildren(enable);
- mContainer.setClipToPadding(enable);
- mContainer.setClipChildren(enable);
+ if (mContainerConstraintLayout != null) {
+ setClipChildren(enable);
+ mContainerConstraintLayout.setClipToPadding(enable);
+ mContainerConstraintLayout.setClipChildren(enable);
+ }
+ if (mContainerMotionLayout != null) {
+ setClipChildren(enable);
+ mContainerMotionLayout.setClipToPadding(enable);
+ mContainerMotionLayout.setClipChildren(enable);
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
index a30b447..98312b1 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
@@ -18,6 +18,7 @@
import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL;
import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL_UNLOCKED;
+import static com.android.systemui.flags.Flags.LOCKSCREEN_ENABLE_LANDSCAPE;
import android.content.res.ColorStateList;
import android.os.AsyncTask;
@@ -205,6 +206,8 @@
mLatencyTracker = latencyTracker;
mFalsingCollector = falsingCollector;
mEmergencyButtonController = emergencyButtonController;
+ view.setIsLockScreenLandscapeEnabled(
+ featureFlags.isEnabled(LOCKSCREEN_ENABLE_LANDSCAPE));
mLockPatternView = mView.findViewById(R.id.lockPatternView);
mPostureController = postureController;
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
index 574a059..0af803f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
@@ -16,6 +16,8 @@
package com.android.keyguard;
+import static com.android.systemui.flags.Flags.LOCKSCREEN_ENABLE_LANDSCAPE;
+
import android.view.View;
import com.android.internal.util.LatencyTracker;
@@ -61,6 +63,7 @@
mPostureController = postureController;
mLockPatternUtils = lockPatternUtils;
mFeatureFlags = featureFlags;
+ view.setIsLockScreenLandscapeEnabled(mFeatureFlags.isEnabled(LOCKSCREEN_ENABLE_LANDSCAPE));
mBackspaceKey = view.findViewById(R.id.delete_button);
mPinLength = mLockPatternUtils.getPinLength(KeyguardUpdateMonitor.getCurrentUser());
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java
index 5528837..f18504c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java
@@ -155,7 +155,7 @@
private int getLayoutIdFor(SecurityMode securityMode) {
// TODO (b/297863911, b/297864907) - implement motion layout for other bouncers
switch (securityMode) {
- case Pattern: return R.layout.keyguard_pattern_view;
+ case Pattern: return R.layout.keyguard_pattern_motion_layout;
case PIN: return R.layout.keyguard_pin_motion_layout;
case Password: return R.layout.keyguard_password_motion_layout;
case SimPin: return R.layout.keyguard_sim_pin_view;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index e4bbd3a..abab5e6 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -35,6 +35,7 @@
import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN;
import static android.os.BatteryManager.CHARGING_POLICY_DEFAULT;
import static android.os.PowerManager.WAKE_REASON_UNKNOWN;
+
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
@@ -74,6 +75,7 @@
import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_STRONG_AUTH_CHANGED;
import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_USER_SWITCHING;
import static com.android.systemui.DejankUtils.whitelistIpcs;
+import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_OPENED;
import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_UNKNOWN;
@@ -160,6 +162,7 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.dump.DumpsysTableLogger;
+import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.keyguard.domain.interactor.FaceAuthenticationListener;
import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
import com.android.systemui.keyguard.shared.constants.TrustAgentUiEvent;
@@ -202,7 +205,6 @@
import java.util.Optional;
import java.util.Set;
import java.util.TimeZone;
-import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;
@@ -342,15 +344,16 @@
return;
}
- if (mDisplayTracker.getDisplay(mDisplayTracker.getDefaultDisplayId()).getState()
+ if (mWakefulness.getWakefulness() == WAKEFULNESS_AWAKE
+ && mDisplayTracker.getDisplay(mDisplayTracker.getDefaultDisplayId()).getState()
== Display.STATE_OFF) {
- mAllowedDisplayStateForFaceAuth = false;
+ mAllowedDisplayStateWhileAwakeForFaceAuth = false;
updateFaceListeningState(
BIOMETRIC_ACTION_STOP,
FACE_AUTH_DISPLAY_OFF
);
} else {
- mAllowedDisplayStateForFaceAuth = true;
+ mAllowedDisplayStateWhileAwakeForFaceAuth = true;
}
}
};
@@ -374,7 +377,7 @@
private boolean mOccludingAppRequestingFp;
private boolean mOccludingAppRequestingFace;
private boolean mSecureCameraLaunched;
- private boolean mAllowedDisplayStateForFaceAuth = true;
+ private boolean mAllowedDisplayStateWhileAwakeForFaceAuth = true;
@VisibleForTesting
protected boolean mTelephonyCapable;
private boolean mAllowFingerprintOnCurrentOccludingActivity;
@@ -422,6 +425,7 @@
private KeyguardFaceAuthInteractor mFaceAuthInteractor;
private final TaskStackChangeListeners mTaskStackChangeListeners;
private final IActivityTaskManager mActivityTaskManager;
+ private final WakefulnessLifecycle mWakefulness;
private final DisplayTracker mDisplayTracker;
private final LockPatternUtils mLockPatternUtils;
@VisibleForTesting
@@ -2207,7 +2211,7 @@
Trace.beginSection("KeyguardUpdateMonitor#handleStartedWakingUp");
Assert.isMainThread();
- mAllowedDisplayStateForFaceAuth = true;
+ mAllowedDisplayStateWhileAwakeForFaceAuth = true;
updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
if (mFaceWakeUpTriggersConfig.shouldTriggerFaceAuthOnWakeUpFrom(pmWakeReason)) {
FACE_AUTH_UPDATED_STARTED_WAKING_UP.setExtraInfo(pmWakeReason);
@@ -2363,7 +2367,8 @@
Optional<FingerprintInteractiveToAuthProvider> interactiveToAuthProvider,
TaskStackChangeListeners taskStackChangeListeners,
IActivityTaskManager activityTaskManagerService,
- DisplayTracker displayTracker) {
+ DisplayTracker displayTracker,
+ WakefulnessLifecycle wakefulness) {
mContext = context;
mSubscriptionManager = subscriptionManager;
mUserTracker = userTracker;
@@ -2410,6 +2415,7 @@
.collect(Collectors.toSet());
mTaskStackChangeListeners = taskStackChangeListeners;
mActivityTaskManager = activityTaskManagerService;
+ mWakefulness = wakefulness;
mDisplayTracker = displayTracker;
mDisplayTracker.addDisplayChangeCallback(mDisplayCallback, mainExecutor);
@@ -2439,7 +2445,7 @@
handleDevicePolicyManagerStateChanged(msg.arg1);
break;
case MSG_USER_SWITCHING:
- handleUserSwitching(msg.arg1, (CountDownLatch) msg.obj);
+ handleUserSwitching(msg.arg1, (Runnable) msg.obj);
break;
case MSG_USER_SWITCH_COMPLETE:
handleUserSwitchComplete(msg.arg1);
@@ -2740,10 +2746,12 @@
}
private final UserTracker.Callback mUserChangedCallback = new UserTracker.Callback() {
+
@Override
- public void onUserChanging(int newUser, Context userContext, CountDownLatch latch) {
+ public void onUserChanging(int newUser, @NonNull Context userContext,
+ @NonNull Runnable resultCallback) {
mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_SWITCHING,
- newUser, 0, latch));
+ newUser, 0, resultCallback));
}
@Override
@@ -3212,7 +3220,7 @@
&& faceAndFpNotAuthenticated
&& !mGoingToSleep
&& isPostureAllowedForFaceAuth
- && mAllowedDisplayStateForFaceAuth;
+ && mAllowedDisplayStateWhileAwakeForFaceAuth;
// Aggregate relevant fields for debug logging.
logListenerModelData(
@@ -3220,7 +3228,7 @@
System.currentTimeMillis(),
user,
shouldListen,
- mAllowedDisplayStateForFaceAuth,
+ mAllowedDisplayStateWhileAwakeForFaceAuth,
mAlternateBouncerShowing,
mAuthInterruptActive,
biometricEnabledForUser,
@@ -3574,7 +3582,7 @@
* Handle {@link #MSG_USER_SWITCHING}
*/
@VisibleForTesting
- void handleUserSwitching(int userId, CountDownLatch latch) {
+ void handleUserSwitching(int userId, Runnable resultCallback) {
mLogger.logUserSwitching(userId, "from UserTracker");
Assert.isMainThread();
clearBiometricRecognized();
@@ -3588,7 +3596,7 @@
cb.onUserSwitching(userId);
}
}
- latch.countDown();
+ resultCallback.run();
}
/**
@@ -4201,7 +4209,7 @@
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
final boolean previousState = mAllowFingerprintOnCurrentOccludingActivity;
mAllowFingerprintOnCurrentOccludingActivity =
- standardTask.topActivity != null
+ standardTask != null && standardTask.topActivity != null
&& !TextUtils.isEmpty(standardTask.topActivity.getPackageName())
&& mAllowFingerprintOnOccludingActivitiesFromPackage.contains(
standardTask.topActivity.getPackageName())
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactoryBase.kt b/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactoryBase.kt
index 12108b0..b15aaaf 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactoryBase.kt
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactoryBase.kt
@@ -25,6 +25,9 @@
import android.util.Log
import androidx.core.app.AppComponentFactory
import com.android.systemui.dagger.ContextComponentHelper
+import com.android.systemui.dagger.SysUIComponent
+import com.android.tools.r8.keepanno.annotations.KeepTarget
+import com.android.tools.r8.keepanno.annotations.UsesReflection
import java.lang.reflect.InvocationTargetException
import java.util.concurrent.ExecutionException
import javax.inject.Inject
@@ -88,6 +91,7 @@
return app
}
+ @UsesReflection(KeepTarget(extendsClassConstant = SysUIComponent::class, methodName = "inject"))
override fun instantiateProviderCompat(cl: ClassLoader, className: String): ContentProvider {
val contentProvider = super.instantiateProviderCompat(cl, className)
if (contentProvider is ContextInitializer) {
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index c9801d7..9fd0602 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -54,6 +54,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -84,6 +85,7 @@
private final SystemClock mClock;
private H mBGHandler;
+ private final Executor mBgExecutor;
private final List<AppOpsController.Callback> mCallbacks = new ArrayList<>();
private final SparseArray<Set<Callback>> mCallbacksByCode = new SparseArray<>();
private boolean mListening;
@@ -153,6 +155,7 @@
public AppOpsControllerImpl(
Context context,
@Background Looper bgLooper,
+ @Background Executor bgExecutor,
DumpManager dumpManager,
AudioManager audioManager,
IndividualSensorPrivacyController sensorPrivacyController,
@@ -162,6 +165,7 @@
mDispatcher = dispatcher;
mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
mBGHandler = new H(bgLooper);
+ mBgExecutor = bgExecutor;
final int numOps = OPS.length;
for (int i = 0; i < numOps; i++) {
mCallbacksByCode.put(OPS[i], new ArraySet<>());
@@ -184,41 +188,43 @@
@VisibleForTesting
protected void setListening(boolean listening) {
mListening = listening;
- if (listening) {
- // System UI could be restarted while ops are active, so fetch the currently active ops
- // once System UI starts listening again.
- fetchCurrentActiveOps();
+ // Move IPCs to the background.
+ mBgExecutor.execute(() -> {
+ if (listening) {
+ // System UI could be restarted while ops are active, so fetch the currently active
+ // ops once System UI starts listening again -- see b/294104969.
+ fetchCurrentActiveOps();
- mAppOps.startWatchingActive(OPS, this);
- mAppOps.startWatchingNoted(OPS, this);
- mAudioManager.registerAudioRecordingCallback(mAudioRecordingCallback, mBGHandler);
- mSensorPrivacyController.addCallback(this);
+ mAppOps.startWatchingActive(OPS, this);
+ mAppOps.startWatchingNoted(OPS, this);
+ mAudioManager.registerAudioRecordingCallback(mAudioRecordingCallback, mBGHandler);
+ mSensorPrivacyController.addCallback(this);
- mMicMuted = mAudioManager.isMicrophoneMute()
- || mSensorPrivacyController.isSensorBlocked(MICROPHONE);
- mCameraDisabled = mSensorPrivacyController.isSensorBlocked(CAMERA);
+ mMicMuted = mAudioManager.isMicrophoneMute()
+ || mSensorPrivacyController.isSensorBlocked(MICROPHONE);
+ mCameraDisabled = mSensorPrivacyController.isSensorBlocked(CAMERA);
- mBGHandler.post(() -> mAudioRecordingCallback.onRecordingConfigChanged(
- mAudioManager.getActiveRecordingConfigurations()));
- mDispatcher.registerReceiverWithHandler(this,
- new IntentFilter(ACTION_MICROPHONE_MUTE_CHANGED), mBGHandler);
+ mBGHandler.post(() -> mAudioRecordingCallback.onRecordingConfigChanged(
+ mAudioManager.getActiveRecordingConfigurations()));
+ mDispatcher.registerReceiverWithHandler(this,
+ new IntentFilter(ACTION_MICROPHONE_MUTE_CHANGED), mBGHandler);
+ } else {
+ mAppOps.stopWatchingActive(this);
+ mAppOps.stopWatchingNoted(this);
+ mAudioManager.unregisterAudioRecordingCallback(mAudioRecordingCallback);
+ mSensorPrivacyController.removeCallback(this);
- } else {
- mAppOps.stopWatchingActive(this);
- mAppOps.stopWatchingNoted(this);
- mAudioManager.unregisterAudioRecordingCallback(mAudioRecordingCallback);
- mSensorPrivacyController.removeCallback(this);
-
- mBGHandler.removeCallbacksAndMessages(null); // null removes all
- mDispatcher.unregisterReceiver(this);
- synchronized (mActiveItems) {
- mActiveItems.clear();
- mRecordingsByUid.clear();
+ mBGHandler.removeCallbacksAndMessages(null); // null removes all
+ mDispatcher.unregisterReceiver(this);
+ synchronized (mActiveItems) {
+ mActiveItems.clear();
+ mRecordingsByUid.clear();
+ }
+ synchronized (mNotedItems) {
+ mNotedItems.clear();
+ }
}
- synchronized (mNotedItems) {
- mNotedItems.clear();
- }
- }
+ });
}
private void fetchCurrentActiveOps() {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetector.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetector.kt
index 97b0617..054bd08 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetector.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetector.kt
@@ -25,7 +25,7 @@
shadeExpansionCollectorJob =
scope.launch {
// wait for it to emit true once
- shadeInteractorLazy.get().anyExpanding.first { it }
+ shadeInteractorLazy.get().isAnyExpanding.first { it }
onShadeInteraction.run()
}
shadeExpansionCollectorJob?.invokeOnCompletion { shadeExpansionCollectorJob = null }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt
new file mode 100644
index 0000000..ea3ddac
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt
@@ -0,0 +1,21 @@
+package com.android.systemui.communal.data.repository
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.FeatureFlagsClassic
+import com.android.systemui.flags.Flags
+import javax.inject.Inject
+
+/** Encapsulates the state of communal mode. */
+interface CommunalRepository {
+ /** Whether communal features are enabled. */
+ val isCommunalEnabled: Boolean
+}
+
+@SysUISingleton
+class CommunalRepositoryImpl
+@Inject
+constructor(
+ featureFlags: FeatureFlagsClassic,
+) : CommunalRepository {
+ override val isCommunalEnabled = featureFlags.isEnabled(Flags.COMMUNAL_HUB)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepositoryModule.kt
new file mode 100644
index 0000000..9d95b9e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepositoryModule.kt
@@ -0,0 +1,9 @@
+package com.android.systemui.communal.data.repository
+
+import dagger.Binds
+import dagger.Module
+
+@Module
+interface CommunalRepositoryModule {
+ @Binds fun communalRepository(impl: CommunalRepositoryImpl): CommunalRepository
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
index 6dc305e..9fb8da3 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
@@ -16,6 +16,7 @@
package com.android.systemui.communal.domain.interactor
+import com.android.systemui.communal.data.repository.CommunalRepository
import com.android.systemui.communal.data.repository.CommunalWidgetRepository
import com.android.systemui.communal.shared.CommunalAppWidgetInfo
import com.android.systemui.dagger.SysUISingleton
@@ -27,8 +28,12 @@
class CommunalInteractor
@Inject
constructor(
+ communalRepository: CommunalRepository,
widgetRepository: CommunalWidgetRepository,
) {
+ /** Whether communal features are enabled. */
+ val isCommunalEnabled: Boolean = communalRepository.isCommunalEnabled
+
/** A flow of info about the widget to be displayed, or null if widget is unavailable. */
val appWidgetInfo: Flow<CommunalAppWidgetInfo?> = widgetRepository.stopwatchAppWidgetInfo
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
index 7b58b1f..9e5fd55 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
@@ -31,6 +31,7 @@
import com.android.systemui.dock.DockManagerImpl;
import com.android.systemui.doze.DozeHost;
import com.android.systemui.media.dagger.MediaModule;
+import com.android.systemui.navigationbar.NavigationBarControllerModule;
import com.android.systemui.navigationbar.gestural.GestureModule;
import com.android.systemui.plugins.qs.QSFactory;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -101,6 +102,7 @@
GestureModule.class,
MediaModule.class,
MultiUserUtilsModule.class,
+ NavigationBarControllerModule.class,
PowerModule.class,
QSModule.class,
ReferenceScreenshotModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
index f12b919..d89332d 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
@@ -49,6 +49,7 @@
import com.android.systemui.power.PowerUI
import com.android.systemui.reardisplay.RearDisplayDialogController
import com.android.systemui.recents.Recents
+import com.android.systemui.recents.ScreenPinningRequest
import com.android.systemui.settings.dagger.MultiUserUtilsModule
import com.android.systemui.shortcut.ShortcutKeyDispatcher
import com.android.systemui.statusbar.ImmersiveModeConfirmation
@@ -366,4 +367,9 @@
@IntoMap
@ClassKey(KeyguardDismissBinder::class)
abstract fun bindKeyguardDismissBinder(impl: KeyguardDismissBinder): CoreStartable
+
+ @Binds
+ @IntoMap
+ @ClassKey(ScreenPinningRequest::class)
+ abstract fun bindScreenPinningRequest(impl: ScreenPinningRequest): CoreStartable
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 3a942bd..58ba3c9 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -89,6 +89,7 @@
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.connectivity.ConnectivityModule;
+import com.android.systemui.statusbar.dagger.StatusBarModule;
import com.android.systemui.statusbar.disableflags.dagger.DisableFlagsModule;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
@@ -153,71 +154,72 @@
* may not appreciate that.
*/
@Module(includes = {
- AccessibilityModule.class,
- AccessibilityRepositoryModule.class,
- AConfigModule.class,
- AppOpsModule.class,
- AssistModule.class,
- AuthenticationModule.class,
- BiometricsModule.class,
- BouncerViewModule.class,
- ClipboardOverlayModule.class,
- ClockRegistryModule.class,
- CommonRepositoryModule.class,
- DisplayModule.class,
- ConnectivityModule.class,
- CoroutinesModule.class,
- DreamModule.class,
- ControlsModule.class,
- DemoModeModule.class,
- DisableFlagsModule.class,
- FalsingModule.class,
- FlagsModule.class,
- SystemPropertiesFlagsModule.class,
- FooterActionsModule.class,
- KeyboardModule.class,
- LetterboxModule.class,
- KeyguardBlueprintModule.class,
- LogModule.class,
- MediaProjectionModule.class,
- MediaProjectionTaskSwitcherModule.class,
- MotionToolModule.class,
- NotificationIconAreaControllerModule.class,
- PeopleHubModule.class,
- PeopleModule.class,
- PluginModule.class,
- PolicyModule.class,
- PrivacyModule.class,
- QRCodeScannerModule.class,
- QSFragmentStartableModule.class,
- RetailModeModule.class,
- ScreenshotModule.class,
- SensorModule.class,
- SecurityRepositoryModule.class,
- ScreenRecordModule.class,
- SettingsUtilModule.class,
- SmartRepliesInflationModule.class,
- SmartspaceModule.class,
- StatusBarPipelineModule.class,
- StatusBarPolicyModule.class,
- StatusBarWindowModule.class,
- SysUIConcurrencyModule.class,
- SysUIUnfoldModule.class,
- TelephonyRepositoryModule.class,
- TemporaryDisplayModule.class,
- TunerModule.class,
- UserModule.class,
- UtilModule.class,
- NoteTaskModule.class,
- WalletModule.class
+ AccessibilityModule.class,
+ AccessibilityRepositoryModule.class,
+ AConfigModule.class,
+ AppOpsModule.class,
+ AssistModule.class,
+ AuthenticationModule.class,
+ BiometricsModule.class,
+ BouncerViewModule.class,
+ ClipboardOverlayModule.class,
+ ClockRegistryModule.class,
+ CommonRepositoryModule.class,
+ ConnectivityModule.class,
+ ControlsModule.class,
+ CoroutinesModule.class,
+ DemoModeModule.class,
+ DisableFlagsModule.class,
+ DisplayModule.class,
+ DreamModule.class,
+ FalsingModule.class,
+ FlagsModule.class,
+ FooterActionsModule.class,
+ KeyboardModule.class,
+ KeyguardBlueprintModule.class,
+ LetterboxModule.class,
+ LogModule.class,
+ MediaProjectionModule.class,
+ MediaProjectionTaskSwitcherModule.class,
+ MotionToolModule.class,
+ NotificationIconAreaControllerModule.class,
+ PeopleHubModule.class,
+ PeopleModule.class,
+ PluginModule.class,
+ PolicyModule.class,
+ PrivacyModule.class,
+ QRCodeScannerModule.class,
+ QSFragmentStartableModule.class,
+ RetailModeModule.class,
+ ScreenshotModule.class,
+ SensorModule.class,
+ SecurityRepositoryModule.class,
+ ScreenRecordModule.class,
+ SettingsUtilModule.class,
+ SmartRepliesInflationModule.class,
+ SmartspaceModule.class,
+ StatusBarModule.class,
+ StatusBarPipelineModule.class,
+ StatusBarPolicyModule.class,
+ StatusBarWindowModule.class,
+ SystemPropertiesFlagsModule.class,
+ SysUIConcurrencyModule.class,
+ SysUIUnfoldModule.class,
+ TelephonyRepositoryModule.class,
+ TemporaryDisplayModule.class,
+ TunerModule.class,
+ UserModule.class,
+ UtilModule.class,
+ NoteTaskModule.class,
+ WalletModule.class
},
subcomponents = {
ComplicationComponent.class,
- NavigationBarComponent.class,
- NotificationRowComponent.class,
DozeComponent.class,
ExpandableNotificationRowComponent.class,
KeyguardBouncerComponent.class,
+ NavigationBarComponent.class,
+ NotificationRowComponent.class,
NotificationShelfComponent.class,
WindowRootViewComponent.class,
})
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
index 1751358..4f16685 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
@@ -17,6 +17,7 @@
package com.android.systemui.display.data.repository
import android.hardware.display.DisplayManager
+import android.hardware.display.DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED
import android.hardware.display.DisplayManager.DisplayListener
import android.hardware.display.DisplayManager.EVENT_FLAG_DISPLAY_ADDED
import android.hardware.display.DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
@@ -95,28 +96,36 @@
@Background backgroundCoroutineDispatcher: CoroutineDispatcher
) : DisplayRepository {
// Displays are enabled only after receiving them in [onDisplayAdded]
- private val allDisplayEvents: Flow<DisplayEvent> = conflatedCallbackFlow {
- val callback =
- object : DisplayListener {
- override fun onDisplayAdded(displayId: Int) {
- trySend(DisplayEvent.Added(displayId))
- }
+ private val allDisplayEvents: Flow<DisplayEvent> =
+ conflatedCallbackFlow {
+ val callback =
+ object : DisplayListener {
+ override fun onDisplayAdded(displayId: Int) {
+ trySend(DisplayEvent.Added(displayId))
+ }
- override fun onDisplayRemoved(displayId: Int) {
- trySend(DisplayEvent.Removed(displayId))
- }
+ override fun onDisplayRemoved(displayId: Int) {
+ trySend(DisplayEvent.Removed(displayId))
+ }
- override fun onDisplayChanged(displayId: Int) {
- trySend(DisplayEvent.Changed(displayId))
- }
+ override fun onDisplayChanged(displayId: Int) {
+ trySend(DisplayEvent.Changed(displayId))
+ }
+ }
+ // Triggers an initial event when subscribed. This is needed to avoid getDisplays to
+ // be called when this class is constructed, but only when someone subscribes to
+ // this flow.
+ trySend(DisplayEvent.Changed(Display.DEFAULT_DISPLAY))
+ displayManager.registerDisplayListener(
+ callback,
+ backgroundHandler,
+ EVENT_FLAG_DISPLAY_ADDED or
+ EVENT_FLAG_DISPLAY_CHANGED or
+ EVENT_FLAG_DISPLAY_REMOVED,
+ )
+ awaitClose { displayManager.unregisterDisplayListener(callback) }
}
- displayManager.registerDisplayListener(
- callback,
- backgroundHandler,
- EVENT_FLAG_DISPLAY_ADDED or EVENT_FLAG_DISPLAY_CHANGED or EVENT_FLAG_DISPLAY_REMOVED,
- )
- awaitClose { displayManager.unregisterDisplayListener(callback) }
- }
+ .flowOn(backgroundCoroutineDispatcher)
override val displayChangeEvent: Flow<Int> =
allDisplayEvents.filter { it is DisplayEvent.Changed }.map { it.displayId }
@@ -128,7 +137,9 @@
.stateIn(
applicationScope,
started = SharingStarted.WhileSubscribed(),
- initialValue = getDisplays()
+ // To avoid getting displays on this object construction, they are get after the
+ // first event. allDisplayEvents emits a changed event when we subscribe to it.
+ initialValue = emptySet()
)
private fun getDisplays(): Set<Display> =
@@ -146,12 +157,23 @@
private val ignoredDisplayIds = MutableStateFlow<Set<Int>>(emptySet())
+ private fun getInitialConnectedDisplays(): Set<Int> =
+ displayManager
+ .getDisplays(DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED)
+ .map { it.displayId }
+ .toSet()
+ .also {
+ if (DEBUG) {
+ Log.d(TAG, "getInitialConnectedDisplays: $it")
+ }
+ }
+
/* keeps connected displays until they are disconnected. */
private val connectedDisplayIds: StateFlow<Set<Int>> =
conflatedCallbackFlow {
+ val connectedIds = getInitialConnectedDisplays().toMutableSet()
val callback =
object : DisplayConnectionListener {
- private val connectedIds = mutableSetOf<Int>()
override fun onDisplayConnected(id: Int) {
if (DEBUG) {
Log.d(TAG, "display with id=$id connected.")
@@ -170,6 +192,7 @@
trySend(connectedIds.toSet())
}
}
+ trySend(connectedIds.toSet())
displayManager.registerDisplayListener(
callback,
backgroundHandler,
@@ -183,6 +206,10 @@
.stateIn(
applicationScope,
started = SharingStarted.WhileSubscribed(),
+ // The initial value is set to empty, but connected displays are gathered as soon as
+ // the flow starts being collected. This is to ensure the call to get displays (an
+ // IPC) happens in the background instead of when this object
+ // is instantiated.
initialValue = emptySet()
)
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index e11fcb7..02575eb 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -107,7 +107,7 @@
// TODO(b/268005230): Tracking Bug
@JvmField
- val SENSITIVE_REVEAL_ANIM = unreleasedFlag("sensitive_reveal_anim", teamfood = true)
+ val SENSITIVE_REVEAL_ANIM = releasedFlag("sensitive_reveal_anim")
// TODO(b/280783617): Tracking Bug
@Keep
@@ -182,15 +182,12 @@
/** Flag to control the migration of face auth to modern architecture. */
// TODO(b/262838215): Tracking bug
- @JvmField val FACE_AUTH_REFACTOR = unreleasedFlag("face_auth_refactor", teamfood = true)
+ @JvmField val FACE_AUTH_REFACTOR = releasedFlag("face_auth_refactor")
/** Flag to control the revamp of keyguard biometrics progress animation */
// TODO(b/244313043): Tracking bug
@JvmField val BIOMETRICS_ANIMATION_REVAMP = unreleasedFlag("biometrics_animation_revamp")
- // TODO(b/262780002): Tracking Bug
- @JvmField val REVAMPED_WALLPAPER_UI = releasedFlag("revamped_wallpaper_ui")
-
// flag for controlling auto pin confirmation and material u shapes in bouncer
@JvmField
val AUTO_PIN_CONFIRMATION = releasedFlag("auto_pin_confirmation", "auto_pin_confirmation")
@@ -294,6 +291,11 @@
// TODO(b/291767565): Tracking bug.
@JvmField val MIGRATE_KEYGUARD_STATUS_VIEW = unreleasedFlag("migrate_keyguard_status_view")
+ /** Migrate the status bar view on keyguard from notification panel to keyguard root view. */
+ // TODO(b/299115332): Tracking Bug.
+ @JvmField val MIGRATE_KEYGUARD_STATUS_BAR_VIEW =
+ unreleasedFlag("migrate_keyguard_status_bar_view")
+
/** Enables preview loading animation in the wallpaper picker. */
// TODO(b/274443705): Tracking Bug
@JvmField
@@ -318,6 +320,11 @@
R.bool.flag_stop_pulsing_face_scanning_animation,
"stop_pulsing_face_scanning_animation")
+ /** Flag to use a separate view for the alternate bouncer. */
+ // TODO(b/300440924): Tracking bug
+ @JvmField
+ val ALTERNATE_BOUNCER_REFACTOR: UnreleasedFlag = unreleasedFlag("alternate_bouncer_view")
+
// 300 - power menu
// TODO(b/254512600): Tracking Bug
@JvmField val POWER_MENU_LITE = releasedFlag("power_menu_lite")
@@ -403,17 +410,7 @@
// TODO(b/290676905): Tracking Bug
val NEW_SHADE_CARRIER_GROUP_MOBILE_ICONS =
- unreleasedFlag("new_shade_carrier_group_mobile_icons", teamfood = true)
-
- // 700 - dialer/calls
- // TODO(b/254512734): Tracking Bug
- val ONGOING_CALL_STATUS_BAR_CHIP = releasedFlag("ongoing_call_status_bar_chip")
-
- // TODO(b/254512681): Tracking Bug
- val ONGOING_CALL_IN_IMMERSIVE = releasedFlag("ongoing_call_in_immersive")
-
- // TODO(b/254512753): Tracking Bug
- val ONGOING_CALL_IN_IMMERSIVE_CHIP_TAP = releasedFlag("ongoing_call_in_immersive_chip_tap")
+ releasedFlag("new_shade_carrier_group_mobile_icons")
// 800 - general visual/theme
@JvmField val MONET = resourceBooleanFlag(R.bool.flag_monet, "monet")
@@ -766,8 +763,7 @@
// TODO(b/285174336): Tracking Bug
@JvmField
- val USE_REPOS_FOR_BOUNCER_SHOWING =
- unreleasedFlag("use_repos_for_bouncer_showing", teamfood = true)
+ val USE_REPOS_FOR_BOUNCER_SHOWING = releasedFlag("use_repos_for_bouncer_showing")
// 3100 - Haptic interactions
@@ -783,11 +779,11 @@
/** Enable the Compose implementation of the PeopleSpaceActivity. */
@JvmField
- val COMPOSE_PEOPLE_SPACE = unreleasedFlag("compose_people_space", teamfood = true)
+ val COMPOSE_PEOPLE_SPACE = releasedFlag("compose_people_space")
/** Enable the Compose implementation of the Quick Settings footer actions. */
@JvmField
- val COMPOSE_QS_FOOTER_ACTIONS = unreleasedFlag("compose_qs_footer_actions", teamfood = true)
+ val COMPOSE_QS_FOOTER_ACTIONS = releasedFlag("compose_qs_footer_actions")
/** Enable the share wifi button in Quick Settings internet dialog. */
@JvmField
@@ -804,4 +800,9 @@
/** Enable showing a dialog when clicking on Quick Settings bluetooth tile. */
@JvmField
val BLUETOOTH_QS_TILE_DIALOG = unreleasedFlag("bluetooth_qs_tile_dialog")
+
+ // TODO(b/300995746): Tracking Bug
+ /** Enable communal hub features. */
+ @JvmField
+ val COMMUNAL_HUB = resourceBooleanFlag(R.bool.config_communalServiceEnabled, "communal_hub")
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index 257006e..22bcf0a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -18,10 +18,8 @@
package com.android.systemui.keyguard
import android.content.Context
-import android.content.res.Configuration
import android.view.LayoutInflater
import android.view.View
-import android.view.ViewGroup
import com.android.keyguard.KeyguardStatusView
import com.android.keyguard.KeyguardStatusViewController
import com.android.keyguard.LockIconView
@@ -29,38 +27,21 @@
import com.android.keyguard.dagger.KeyguardStatusViewComponent
import com.android.systemui.CoreStartable
import com.android.systemui.R
-import com.android.systemui.communal.ui.adapter.CommunalWidgetViewAdapter
-import com.android.systemui.communal.ui.binder.CommunalWidgetViewBinder
-import com.android.systemui.communal.ui.viewmodel.CommunalWidgetViewModel
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
-import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
-import com.android.systemui.keyguard.ui.binder.KeyguardAmbientIndicationAreaViewBinder
import com.android.systemui.keyguard.ui.binder.KeyguardBlueprintViewBinder
import com.android.systemui.keyguard.ui.binder.KeyguardIndicationAreaBinder
-import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder
import com.android.systemui.keyguard.ui.binder.KeyguardRootViewBinder
-import com.android.systemui.keyguard.ui.binder.KeyguardSettingsViewBinder
import com.android.systemui.keyguard.ui.view.KeyguardIndicationArea
import com.android.systemui.keyguard.ui.view.KeyguardRootView
import com.android.systemui.keyguard.ui.view.layout.KeyguardBlueprintCommandListener
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardAmbientIndicationViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBlueprintViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordancesCombinedViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardSettingsMenuViewModel
import com.android.systemui.keyguard.ui.viewmodel.OccludingAppDeviceEntryMessageViewModel
-import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.plugins.FalsingManager
import com.android.systemui.shade.NotificationShadeWindowView
import com.android.systemui.statusbar.KeyguardIndicationController
-import com.android.systemui.statusbar.VibratorHelper
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
-import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
-import com.android.systemui.statusbar.notification.stack.ui.viewbinder.SharedNotificationContainerBinder
-import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
import javax.inject.Inject
@@ -74,30 +55,17 @@
@Inject
constructor(
private val keyguardRootView: KeyguardRootView,
- private val sharedNotificationContainer: SharedNotificationContainer,
private val keyguardRootViewModel: KeyguardRootViewModel,
private val keyguardIndicationAreaViewModel: KeyguardIndicationAreaViewModel,
- private val sharedNotificationContainerViewModel: SharedNotificationContainerViewModel,
- private val keyguardAmbientIndicationViewModel: KeyguardAmbientIndicationViewModel,
private val notificationShadeWindowView: NotificationShadeWindowView,
private val featureFlags: FeatureFlags,
private val indicationController: KeyguardIndicationController,
- private val keyguardQuickAffordancesCombinedViewModel:
- KeyguardQuickAffordancesCombinedViewModel,
- private val falsingManager: FalsingManager,
- private val vibratorHelper: VibratorHelper,
private val keyguardStateController: KeyguardStateController,
- private val keyguardSettingsMenuViewModel: KeyguardSettingsMenuViewModel,
- private val activityStarter: ActivityStarter,
private val occludingAppDeviceEntryMessageViewModel: OccludingAppDeviceEntryMessageViewModel,
private val chipbarCoordinator: ChipbarCoordinator,
private val keyguardBlueprintCommandListener: KeyguardBlueprintCommandListener,
private val keyguardBlueprintViewModel: KeyguardBlueprintViewModel,
private val keyguardStatusViewComponentFactory: KeyguardStatusViewComponent.Factory,
- private val keyguardBlueprintInteractor: KeyguardBlueprintInteractor,
- private val communalWidgetViewModel: CommunalWidgetViewModel,
- private val communalWidgetViewAdapter: CommunalWidgetViewAdapter,
- private val notificationStackScrollerLayoutController: NotificationStackScrollLayoutController,
private val context: Context,
private val keyguardIndicationController: KeyguardIndicationController,
private val lockIconViewController: LockIconViewController,
@@ -105,10 +73,7 @@
private var rootViewHandle: DisposableHandle? = null
private var indicationAreaHandle: DisposableHandle? = null
- private var leftShortcutHandle: KeyguardQuickAffordanceViewBinder.Binding? = null
- private var rightShortcutHandle: KeyguardQuickAffordanceViewBinder.Binding? = null
- private var ambientIndicationAreaHandle: KeyguardAmbientIndicationAreaViewBinder.Binding? = null
- private var settingsPopupMenuHandle: DisposableHandle? = null
+
var keyguardStatusViewController: KeyguardStatusViewController? = null
get() {
if (field == null) {
@@ -127,52 +92,12 @@
override fun start() {
bindKeyguardRootView()
- if (featureFlags.isEnabled(Flags.LAZY_INFLATE_KEYGUARD)) {
- keyguardRootView.removeAllViews()
- initializeViews()
- } else {
- val notificationPanel =
- notificationShadeWindowView.requireViewById(R.id.notification_panel) as ViewGroup
- unbindKeyguardBottomArea(notificationPanel)
- bindIndicationArea()
- bindLockIconView(notificationPanel)
- bindKeyguardStatusView(notificationPanel)
- setupNotificationStackScrollLayout(notificationPanel)
- bindLeftShortcut()
- bindRightShortcut()
- bindAmbientIndicationArea()
- bindSettingsPopupMenu()
- bindCommunalWidgetArea()
- }
+ initializeViews()
KeyguardBlueprintViewBinder.bind(keyguardRootView, keyguardBlueprintViewModel)
keyguardBlueprintCommandListener.start()
}
- fun setupNotificationStackScrollLayout(legacyParent: ViewGroup) {
- if (featureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
- // This moves the existing NSSL view to a different parent, as the controller is a
- // singleton and recreating it has other bad side effects
- val nssl =
- legacyParent.requireViewById<View>(R.id.notification_stack_scroller).also {
- (it.getParent() as ViewGroup).removeView(it)
- }
- sharedNotificationContainer.addNotificationStackScrollLayout(nssl)
- SharedNotificationContainerBinder.bind(
- sharedNotificationContainer,
- sharedNotificationContainerViewModel,
- notificationStackScrollerLayoutController,
- )
- }
- }
-
- override fun onConfigurationChanged(newConfig: Configuration?) {
- super.onConfigurationChanged(newConfig)
- leftShortcutHandle?.onConfigurationChanged()
- rightShortcutHandle?.onConfigurationChanged()
- ambientIndicationAreaHandle?.onConfigurationChanged()
- }
-
fun bindIndicationArea() {
indicationAreaHandle?.dispose()
@@ -213,135 +138,6 @@
)
}
- private fun bindLockIconView(legacyParent: ViewGroup) {
- if (featureFlags.isEnabled(Flags.MIGRATE_LOCK_ICON)) {
- legacyParent.requireViewById<View>(R.id.lock_icon_view).let {
- legacyParent.removeView(it)
- }
- } else {
- keyguardRootView.findViewById<View?>(R.id.lock_icon_view)?.let {
- keyguardRootView.removeView(it)
- }
- legacyParent.requireViewById<LockIconView>(R.id.lock_icon_view).let {
- lockIconViewController.setLockIconView(it)
- }
- }
- }
-
- private fun bindAmbientIndicationArea() {
- if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
- ambientIndicationAreaHandle?.destroy()
- ambientIndicationAreaHandle =
- KeyguardAmbientIndicationAreaViewBinder.bind(
- notificationShadeWindowView,
- keyguardAmbientIndicationViewModel,
- keyguardRootViewModel,
- )
- } else {
- keyguardRootView.findViewById<View?>(R.id.ambient_indication_container)?.let {
- keyguardRootView.removeView(it)
- }
- }
- }
-
- private fun bindSettingsPopupMenu() {
- if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
- settingsPopupMenuHandle?.dispose()
- settingsPopupMenuHandle =
- KeyguardSettingsViewBinder.bind(
- keyguardRootView,
- keyguardSettingsMenuViewModel,
- vibratorHelper,
- activityStarter,
- )
- } else {
- keyguardRootView.findViewById<View?>(R.id.keyguard_settings_button)?.let {
- keyguardRootView.removeView(it)
- }
- }
- }
-
- private fun unbindKeyguardBottomArea(legacyParent: ViewGroup) {
- if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
- legacyParent.requireViewById<View>(R.id.keyguard_bottom_area).let {
- legacyParent.removeView(it)
- }
- }
- }
-
- private fun bindLeftShortcut() {
- leftShortcutHandle?.destroy()
- if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
- leftShortcutHandle =
- KeyguardQuickAffordanceViewBinder.bind(
- keyguardRootView.requireViewById(R.id.start_button),
- keyguardQuickAffordancesCombinedViewModel.startButton,
- keyguardRootViewModel.alpha,
- falsingManager,
- vibratorHelper,
- ) {
- indicationController.showTransientIndication(it)
- }
- } else {
- keyguardRootView.findViewById<View?>(R.id.start_button)?.let {
- keyguardRootView.removeView(it)
- }
- }
- }
-
- private fun bindRightShortcut() {
- rightShortcutHandle?.destroy()
- if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
- rightShortcutHandle =
- KeyguardQuickAffordanceViewBinder.bind(
- keyguardRootView.requireViewById(R.id.end_button),
- keyguardQuickAffordancesCombinedViewModel.endButton,
- keyguardRootViewModel.alpha,
- falsingManager,
- vibratorHelper,
- ) {
- indicationController.showTransientIndication(it)
- }
- } else {
- keyguardRootView.findViewById<View?>(R.id.end_button)?.let {
- keyguardRootView.removeView(it)
- }
- }
- }
-
- fun bindKeyguardStatusView(legacyParent: ViewGroup) {
- // At startup, 2 views with the ID `R.id.keyguard_status_view` will be available.
- // Disable one of them
- if (featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) {
- legacyParent.findViewById<View>(R.id.keyguard_status_view)?.let {
- legacyParent.removeView(it)
- }
-
- val keyguardStatusView = keyguardRootView.addStatusView()
- val statusViewComponent = keyguardStatusViewComponentFactory.build(keyguardStatusView)
- val controller = statusViewComponent.getKeyguardStatusViewController()
- controller.init()
- keyguardStatusViewController = controller
- } else {
- keyguardRootView.findViewById<View?>(R.id.keyguard_status_view)?.let {
- keyguardRootView.removeView(it)
- }
- }
- }
-
- private fun bindCommunalWidgetArea() {
- if (!featureFlags.isEnabled(Flags.WIDGET_ON_KEYGUARD)) {
- return
- }
-
- CommunalWidgetViewBinder.bind(
- keyguardRootView,
- communalWidgetViewModel,
- communalWidgetViewAdapter,
- keyguardBlueprintInteractor,
- )
- }
-
/**
* Temporary, to allow NotificationPanelViewController to use the same instance while code is
* migrated: b/288242803
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 9915720..9241776 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -764,13 +764,6 @@
}
}
}
-
- @Override
- public void onStrongAuthStateChanged(int userId) {
- if (mLockPatternUtils.isUserInLockdown(KeyguardUpdateMonitor.getCurrentUser())) {
- doKeyguardLocked(null);
- }
- }
};
ViewMediatorCallback mViewMediatorCallback = new ViewMediatorCallback() {
@@ -2193,9 +2186,8 @@
* Enable the keyguard if the settings are appropriate.
*/
private void doKeyguardLocked(Bundle options) {
- // if another app is disabling us, don't show unless we're in lockdown mode
- if (!mExternallyEnabled
- && !mLockPatternUtils.isUserInLockdown(KeyguardUpdateMonitor.getCurrentUser())) {
+ // if another app is disabling us, don't show
+ if (!mExternallyEnabled) {
if (DEBUG) Log.d(TAG, "doKeyguard: not showing because externally disabled");
mNeedToReshowWhenReenabled = true;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index 13dfe24..03c166c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -37,6 +37,7 @@
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.classifier.FalsingModule;
+import com.android.systemui.communal.data.repository.CommunalRepositoryModule;
import com.android.systemui.communal.data.repository.CommunalWidgetRepositoryModule;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
@@ -93,6 +94,7 @@
KeyguardStatusViewComponent.class,
KeyguardUserSwitcherComponent.class},
includes = {
+ CommunalRepositoryModule.class,
CommunalWidgetRepositoryModule.class,
FalsingModule.class,
KeyguardDataQuickAffordanceModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt
index af0abdf..5e5caba 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt
@@ -34,7 +34,7 @@
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.withContext
@SysUISingleton
@@ -58,16 +58,21 @@
get() = R.drawable.ic_camera
override val lockScreenState: Flow<KeyguardQuickAffordanceConfig.LockScreenState>
- get() =
- flowOf(
- KeyguardQuickAffordanceConfig.LockScreenState.Visible(
- icon =
- Icon.Resource(
- R.drawable.ic_camera,
- ContentDescription.Resource(R.string.accessibility_camera_button)
- )
- )
+ get() = flow {
+ emit(
+ if (isLaunchable()) {
+ KeyguardQuickAffordanceConfig.LockScreenState.Visible(
+ icon =
+ Icon.Resource(
+ R.drawable.ic_camera,
+ ContentDescription.Resource(R.string.accessibility_camera_button)
+ )
+ )
+ } else {
+ KeyguardQuickAffordanceConfig.LockScreenState.Hidden
+ }
)
+ }
override suspend fun getPickerScreenState(): KeyguardQuickAffordanceConfig.PickerScreenState {
return if (isLaunchable()) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
index 5d7a3d4..23f50ea 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
@@ -34,6 +34,7 @@
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.wallet.controller.QuickAccessWalletController
+import com.android.systemui.wallet.util.getPaymentCards
import javax.inject.Inject
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
@@ -60,7 +61,7 @@
val callback =
object : QuickAccessWalletClient.OnWalletCardsRetrievedCallback {
override fun onWalletCardsRetrieved(response: GetWalletCardsResponse) {
- val hasCards = response?.walletCards?.isNotEmpty() == true
+ val hasCards = getPaymentCards(response.walletCards)?.isNotEmpty() == true
trySendWithFailureLogging(
state(
isFeatureEnabled = isWalletAvailable(),
@@ -135,7 +136,7 @@
object : QuickAccessWalletClient.OnWalletCardsRetrievedCallback {
override fun onWalletCardsRetrieved(response: GetWalletCardsResponse) {
continuation.resumeWith(
- Result.success(response?.walletCards ?: emptyList())
+ Result.success(getPaymentCards(response.walletCards) ?: emptyList())
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
index 6db21b2..233acd9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
@@ -54,13 +54,13 @@
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.user.data.model.SelectionStatus
import com.android.systemui.user.data.repository.UserRepository
+import com.google.errorprone.annotations.CompileTimeConstant
import java.io.PrintWriter
import java.util.Arrays
import java.util.stream.Collectors
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.delay
@@ -112,33 +112,27 @@
fun setLockedOut(isLockedOut: Boolean)
/**
- * Cancel current face authentication and prevent it from running until [resumeFaceAuth] is
- * invoked.
- */
- fun pauseFaceAuth()
-
- /**
- * Allow face auth paused using [pauseFaceAuth] to run again. The next invocation to
- * [authenticate] will run as long as other gating conditions don't stop it from running.
- */
- fun resumeFaceAuth()
-
- /**
- * Trigger face authentication.
+ * Request face authentication or detection to be run.
*
* [uiEvent] provided should be logged whenever face authentication runs. Invocation should be
* ignored if face authentication is already running. Results should be propagated through
* [authenticationStatus]
*
* Run only face detection when [fallbackToDetection] is true and [canRunFaceAuth] is false.
+ *
+ * Method returns immediately and the face auth request is processed as soon as possible.
*/
- suspend fun authenticate(uiEvent: FaceAuthUiEvent, fallbackToDetection: Boolean = false)
+ fun requestAuthenticate(uiEvent: FaceAuthUiEvent, fallbackToDetection: Boolean = false)
/** Stop currently running face authentication or detection. */
fun cancel()
}
-@OptIn(ExperimentalCoroutinesApi::class)
+private data class AuthenticationRequest(
+ val uiEvent: FaceAuthUiEvent,
+ val fallbackToDetection: Boolean
+)
+
@SysUISingleton
class DeviceEntryFaceAuthRepositoryImpl
@Inject
@@ -171,6 +165,8 @@
private var faceAcquiredInfoIgnoreList: Set<Int>
private var retryCount = 0
+ private var pendingAuthenticateRequest = MutableStateFlow<AuthenticationRequest?>(null)
+
private var cancelNotReceivedHandlerJob: Job? = null
private var halErrorRetryJob: Job? = null
@@ -193,15 +189,6 @@
override val isAuthRunning: StateFlow<Boolean>
get() = _isAuthRunning
- private val faceAuthPaused = MutableStateFlow(false)
- override fun pauseFaceAuth() {
- faceAuthPaused.value = true
- }
-
- override fun resumeFaceAuth() {
- faceAuthPaused.value = false
- }
-
private val keyguardSessionId: InstanceId?
get() = sessionTracker.getSessionId(StatusBarManager.SESSION_KEYGUARD)
@@ -213,6 +200,8 @@
override val isAuthenticated: Flow<Boolean>
get() = _isAuthenticated
+ private var cancellationInProgress = MutableStateFlow(false)
+
override val isBypassEnabled: Flow<Boolean> =
keyguardBypassController?.let {
conflatedCallbackFlow {
@@ -302,6 +291,7 @@
observeFaceDetectGatingChecks()
observeFaceAuthResettingConditions()
listenForSchedulingWatchdog()
+ processPendingAuthRequests()
} else {
canRunFaceAuth = MutableStateFlow(false).asStateFlow()
canRunDetection = MutableStateFlow(false).asStateFlow()
@@ -338,6 +328,7 @@
)
.onEach { anyOfThemIsTrue ->
if (anyOfThemIsTrue) {
+ clearPendingAuthRequest("Resetting auth status")
_isAuthenticated.value = false
retryCount = 0
halErrorRetryJob?.cancel()
@@ -346,6 +337,15 @@
.launchIn(applicationScope)
}
+ private fun clearPendingAuthRequest(@CompileTimeConstant loggingContext: String) {
+ faceAuthLogger.clearingPendingAuthRequest(
+ loggingContext,
+ pendingAuthenticateRequest.value?.uiEvent,
+ pendingAuthenticateRequest.value?.fallbackToDetection
+ )
+ pendingAuthenticateRequest.value = null
+ }
+
private fun observeFaceDetectGatingChecks() {
canRunDetection
.onEach {
@@ -378,7 +378,6 @@
biometricSettingsRepository.isFaceAuthEnrolledAndEnabled,
"isFaceAuthEnrolledAndEnabled"
),
- Pair(faceAuthPaused.isFalse(), "faceAuthIsNotPaused"),
Pair(keyguardRepository.isKeyguardGoingAway.isFalse(), "keyguardNotGoingAway"),
Pair(
keyguardRepository.wakefulness.map { it.isStartingToSleep() }.isFalse(),
@@ -402,7 +401,13 @@
biometricSettingsRepository.isCurrentUserInLockdown.isFalse(),
"userHasNotLockedDownDevice"
),
- Pair(keyguardRepository.isKeyguardShowing, "isKeyguardShowing")
+ Pair(keyguardRepository.isKeyguardShowing, "isKeyguardShowing"),
+ Pair(
+ userRepository.selectedUser
+ .map { it.selectionStatus == SelectionStatus.SELECTION_IN_PROGRESS }
+ .isFalse(),
+ "userSwitchingInProgress"
+ )
)
}
@@ -440,9 +445,6 @@
}
_authenticationStatus.value = errorStatus
_isAuthenticated.value = false
- if (errorStatus.isCancellationError()) {
- handleFaceCancellationError()
- }
if (errorStatus.isHardwareError()) {
faceAuthLogger.hardwareError(errorStatus)
handleFaceHardwareError()
@@ -471,16 +473,6 @@
}
}
- private fun handleFaceCancellationError() {
- applicationScope.launch {
- faceAuthRequestedWhileCancellation?.let {
- faceAuthLogger.launchingQueuedFaceAuthRequest(it)
- authenticate(it)
- }
- faceAuthRequestedWhileCancellation = null
- }
- }
-
private fun handleFaceHardwareError() {
if (retryCount < HAL_ERROR_RETRY_MAX) {
retryCount++
@@ -490,7 +482,7 @@
delay(HAL_ERROR_RETRY_TIMEOUT)
if (retryCount < HAL_ERROR_RETRY_MAX) {
faceAuthLogger.attemptingRetryAfterHardwareError(retryCount)
- authenticate(
+ requestAuthenticate(
FaceAuthUiEvent.FACE_AUTH_TRIGGERED_RETRY_AFTER_HW_UNAVAILABLE,
fallbackToDetection = false
)
@@ -501,7 +493,7 @@
private fun onFaceAuthRequestCompleted() {
cancelNotReceivedHandlerJob?.cancel()
- cancellationInProgress = false
+ cancellationInProgress.value = false
_isAuthRunning.value = false
authCancellationSignal = null
}
@@ -512,24 +504,60 @@
_detectionStatus.value = FaceDetectionStatus(sensorId, userId, isStrong)
}
- private var cancellationInProgress = false
- private var faceAuthRequestedWhileCancellation: FaceAuthUiEvent? = null
+ override fun requestAuthenticate(uiEvent: FaceAuthUiEvent, fallbackToDetection: Boolean) {
+ if (pendingAuthenticateRequest.value != null) {
+ faceAuthLogger.ignoredFaceAuthTrigger(
+ pendingAuthenticateRequest.value?.uiEvent,
+ "Previously queued trigger skipped due to new request"
+ )
+ }
+ faceAuthLogger.queueingRequest(uiEvent, fallbackToDetection)
+ pendingAuthenticateRequest.value = AuthenticationRequest(uiEvent, fallbackToDetection)
+ }
- override suspend fun authenticate(uiEvent: FaceAuthUiEvent, fallbackToDetection: Boolean) {
+ private fun processPendingAuthRequests() {
+ combine(
+ pendingAuthenticateRequest,
+ canRunFaceAuth,
+ canRunDetection,
+ cancellationInProgress,
+ ) { pending, canRunAuth, canRunDetect, cancelInProgress ->
+ if (
+ pending != null &&
+ !(canRunAuth || (canRunDetect && pending.fallbackToDetection)) ||
+ cancelInProgress
+ ) {
+ faceAuthLogger.notProcessingRequestYet(
+ pending?.uiEvent,
+ canRunAuth,
+ canRunDetect,
+ cancelInProgress
+ )
+ return@combine null
+ } else {
+ return@combine pending
+ }
+ }
+ .onEach {
+ it?.let {
+ faceAuthLogger.processingRequest(it.uiEvent, it.fallbackToDetection)
+ clearPendingAuthRequest("Authenticate was invoked")
+ authenticate(it.uiEvent, it.fallbackToDetection)
+ }
+ }
+ .flowOn(mainDispatcher)
+ .launchIn(applicationScope)
+ }
+
+ private suspend fun authenticate(uiEvent: FaceAuthUiEvent, fallbackToDetection: Boolean) {
if (_isAuthRunning.value) {
faceAuthLogger.ignoredFaceAuthTrigger(uiEvent, "face auth is currently running")
return
}
- if (cancellationInProgress) {
- faceAuthLogger.queuingRequestWhileCancelling(
- faceAuthRequestedWhileCancellation,
- uiEvent
- )
- faceAuthRequestedWhileCancellation = uiEvent
+ if (cancellationInProgress.value) {
+ faceAuthLogger.ignoredFaceAuthTrigger(uiEvent, "cancellation in progress")
return
- } else {
- faceAuthRequestedWhileCancellation = null
}
if (canRunFaceAuth.value) {
@@ -553,12 +581,19 @@
FaceAuthenticateOptions.Builder().setUserId(currentUserId).build()
)
}
- } else if (fallbackToDetection && canRunDetection.value) {
- faceAuthLogger.ignoredFaceAuthTrigger(
- uiEvent,
- "face auth gating check is false, falling back to detection."
- )
- detect()
+ } else if (canRunDetection.value) {
+ if (fallbackToDetection) {
+ faceAuthLogger.ignoredFaceAuthTrigger(
+ uiEvent,
+ "face auth gating check is false, falling back to detection."
+ )
+ detect()
+ } else {
+ faceAuthLogger.ignoredFaceAuthTrigger(
+ uiEvent = uiEvent,
+ "face auth gating check is false and fallback to detection is not requested"
+ )
+ }
} else {
faceAuthLogger.ignoredFaceAuthTrigger(
uiEvent,
@@ -608,13 +643,13 @@
faceAuthLogger.cancelSignalNotReceived(
_isAuthRunning.value,
_isLockedOut.value,
- cancellationInProgress,
- faceAuthRequestedWhileCancellation
+ cancellationInProgress.value,
+ pendingAuthenticateRequest.value?.uiEvent
)
_authenticationStatus.value = ErrorFaceAuthenticationStatus.cancelNotReceivedError()
onFaceAuthRequestCompleted()
}
- cancellationInProgress = true
+ cancellationInProgress.value = true
_isAuthRunning.value = false
}
@@ -647,9 +682,7 @@
" supportsFaceDetection: " +
"${faceManager?.sensorPropertiesInternal?.firstOrNull()?.supportsFaceDetection}"
)
- pw.println(
- " faceAuthRequestedWhileCancellation: ${faceAuthRequestedWhileCancellation?.reason}"
- )
+ pw.println(" _pendingAuthenticateRequest: ${pendingAuthenticateRequest.value}")
pw.println(" authCancellationSignal: $authCancellationSignal")
pw.println(" detectCancellationSignal: $detectCancellationSignal")
pw.println(" faceAcquiredInfoIgnoreList: $faceAcquiredInfoIgnoreList")
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/NoopDeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/NoopDeviceEntryFaceAuthRepository.kt
index 46135fa..c8cb9e6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/NoopDeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/NoopDeviceEntryFaceAuthRepository.kt
@@ -56,9 +56,6 @@
get() = emptyFlow()
override fun setLockedOut(isLockedOut: Boolean) = Unit
- override fun pauseFaceAuth() = Unit
-
- override fun resumeFaceAuth() = Unit
/**
* Trigger face authentication.
@@ -69,7 +66,7 @@
*
* Run only face detection when [fallbackToDetection] is true and [canRunFaceAuth] is false.
*/
- override suspend fun authenticate(uiEvent: FaceAuthUiEvent, fallbackToDetection: Boolean) {}
+ override fun requestAuthenticate(uiEvent: FaceAuthUiEvent, fallbackToDetection: Boolean) = Unit
/** Stop currently running face authentication or detection. */
override fun cancel() {}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractor.kt
index ca430da..a257f52 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractor.kt
@@ -172,7 +172,6 @@
private fun isFeatureEnabled(): Boolean {
return featureFlags.isEnabled(Flags.LOCK_SCREEN_LONG_PRESS_ENABLED) &&
- featureFlags.isEnabled(Flags.REVAMPED_WALLPAPER_UI) &&
appContext.resources.getBoolean(R.bool.long_press_keyguard_customize_lockscreen_enabled)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
index 9c796f8..7f43cbc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
@@ -380,10 +380,6 @@
suspend fun getPickerFlags(): List<KeyguardPickerFlag> {
return listOf(
KeyguardPickerFlag(
- name = Contract.FlagsTable.FLAG_NAME_REVAMPED_WALLPAPER_UI,
- value = featureFlags.isEnabled(Flags.REVAMPED_WALLPAPER_UI),
- ),
- KeyguardPickerFlag(
name = Contract.FlagsTable.FLAG_NAME_CUSTOM_LOCK_SCREEN_QUICK_AFFORDANCES_ENABLED,
value =
!isFeatureDisabledByDevicePolicy() &&
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt
index ccc2080..f0df3a2e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt
@@ -52,8 +52,6 @@
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
import kotlinx.coroutines.yield
/**
@@ -144,14 +142,11 @@
.onEach { (previous, curr) ->
val wasSwitching = previous.selectionStatus == SelectionStatus.SELECTION_IN_PROGRESS
val isSwitching = curr.selectionStatus == SelectionStatus.SELECTION_IN_PROGRESS
- if (!wasSwitching && isSwitching) {
- repository.pauseFaceAuth()
- } else if (wasSwitching && !isSwitching) {
+ if (wasSwitching && !isSwitching) {
val lockoutMode = facePropertyRepository.getLockoutMode(curr.userInfo.id)
repository.setLockedOut(
lockoutMode == LockoutMode.PERMANENT || lockoutMode == LockoutMode.TIMED
)
- repository.resumeFaceAuth()
yield()
runFaceAuth(
FaceAuthUiEvent.FACE_AUTH_UPDATED_USER_SWITCHING,
@@ -232,12 +227,8 @@
)
} else {
faceAuthenticationStatusOverride.value = null
- applicationScope.launch {
- withContext(mainDispatcher) {
- faceAuthenticationLogger.authRequested(uiEvent)
- repository.authenticate(uiEvent, fallbackToDetection = fallbackToDetect)
- }
- }
+ faceAuthenticationLogger.authRequested(uiEvent)
+ repository.requestAuthenticate(uiEvent, fallbackToDetection = fallbackToDetect)
}
} else {
faceAuthenticationLogger.ignoredFaceAuthTrigger(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt
index a948741..f2b28d9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt
@@ -19,123 +19,14 @@
import android.content.Context
import android.util.AttributeSet
-import android.view.LayoutInflater
-import android.view.View
-import android.widget.ImageView
import androidx.constraintlayout.widget.ConstraintLayout
-import androidx.core.content.res.ResourcesCompat
-import com.android.keyguard.KeyguardStatusView
-import com.android.keyguard.LockIconView
-import com.android.systemui.R
-import com.android.systemui.animation.view.LaunchableImageView
/** Provides a container for all keyguard ui content. */
class KeyguardRootView(
context: Context,
- private val attrs: AttributeSet?,
+ attrs: AttributeSet?,
) :
ConstraintLayout(
context,
attrs,
- ) {
-
- private var statusView: KeyguardStatusView? = null
-
- init {
- addIndicationTextArea()
- addLockIconView()
- addAmbientIndicationArea()
- addLeftShortcut()
- addRightShortcut()
- addSettingsPopupMenu()
- addStatusView()
- }
-
- private fun addIndicationTextArea() {
- val view = KeyguardIndicationArea(context, attrs)
- addView(view)
- }
-
- private fun addLockIconView() {
- val view = LockIconView(context, attrs).apply { id = R.id.lock_icon_view }
- addView(view)
- }
-
- private fun addAmbientIndicationArea() {
- LayoutInflater.from(context).inflate(R.layout.ambient_indication, this)
- }
-
- private fun addLeftShortcut() {
- val padding = resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_padding)
- val view =
- LaunchableImageView(context, attrs).apply {
- id = R.id.start_button
- scaleType = ImageView.ScaleType.FIT_CENTER
- background =
- ResourcesCompat.getDrawable(
- context.resources,
- R.drawable.keyguard_bottom_affordance_bg,
- context.theme
- )
- foreground =
- ResourcesCompat.getDrawable(
- context.resources,
- R.drawable.keyguard_bottom_affordance_selected_border,
- context.theme
- )
- visibility = View.INVISIBLE
- setPadding(padding, padding, padding, padding)
- }
- addView(view)
- }
-
- private fun addRightShortcut() {
- val padding = resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_padding)
- val view =
- LaunchableImageView(context, attrs).apply {
- id = R.id.end_button
- scaleType = ImageView.ScaleType.FIT_CENTER
- background =
- ResourcesCompat.getDrawable(
- context.resources,
- R.drawable.keyguard_bottom_affordance_bg,
- context.theme
- )
- foreground =
- ResourcesCompat.getDrawable(
- context.resources,
- R.drawable.keyguard_bottom_affordance_selected_border,
- context.theme
- )
- visibility = View.INVISIBLE
- setPadding(padding, padding, padding, padding)
- }
- addView(view)
- }
-
- private fun addSettingsPopupMenu() {
- val view =
- LayoutInflater.from(context)
- .inflate(R.layout.keyguard_settings_popup_menu, this, false)
- .apply {
- id = R.id.keyguard_settings_button
- visibility = GONE
- }
- addView(view)
- }
-
- fun addStatusView(): KeyguardStatusView {
- // StatusView may need to be rebuilt on config changes. Remove and reinflate
- statusView?.let { removeView(it) }
- val view =
- (LayoutInflater.from(context).inflate(R.layout.keyguard_status_view, this, false)
- as KeyguardStatusView)
- .apply {
- setClipChildren(false)
- statusView = this
- }
-
- addView(view)
- return view
- }
-}
+ )
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt
index 15bb909..1eeb017 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt
@@ -17,10 +17,7 @@
package com.android.systemui.keyguard.ui.view.layout.blueprints
-import androidx.constraintlayout.widget.ConstraintLayout
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
import com.android.systemui.keyguard.ui.view.layout.sections.AodNotificationIconsSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultAmbientIndicationAreaSection
@@ -29,6 +26,7 @@
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultNotificationStackScrollLayoutSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultSettingsPopupMenuSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultShortcutsSection
+import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusBarSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusViewSection
import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeGuidelines
import javax.inject.Inject
@@ -49,10 +47,10 @@
defaultAmbientIndicationAreaSection: DefaultAmbientIndicationAreaSection,
defaultSettingsPopupMenuSection: DefaultSettingsPopupMenuSection,
defaultStatusViewSection: DefaultStatusViewSection,
+ defaultStatusBarSection: DefaultStatusBarSection,
defaultNotificationStackScrollLayoutSection: DefaultNotificationStackScrollLayoutSection,
splitShadeGuidelines: SplitShadeGuidelines,
aodNotificationIconsSection: AodNotificationIconsSection,
- private val featureFlags: FeatureFlags,
) : KeyguardBlueprint {
override val id: String = DEFAULT
@@ -64,21 +62,12 @@
defaultAmbientIndicationAreaSection,
defaultSettingsPopupMenuSection,
defaultStatusViewSection,
+ defaultStatusBarSection,
defaultNotificationStackScrollLayoutSection,
splitShadeGuidelines,
aodNotificationIconsSection,
)
- override fun replaceViews(
- previousBlueprint: KeyguardBlueprint?,
- constraintLayout: ConstraintLayout,
- bindData: Boolean
- ) {
- if (featureFlags.isEnabled(Flags.LAZY_INFLATE_KEYGUARD)) {
- super.replaceViews(previousBlueprint, constraintLayout, bindData)
- }
- }
-
companion object {
const val DEFAULT = "default"
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt
index 6534dcf..a9885fc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt
@@ -26,6 +26,7 @@
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultLockIconSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultNotificationStackScrollLayoutSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultSettingsPopupMenuSection
+import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusBarSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusViewSection
import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeGuidelines
import javax.inject.Inject
@@ -41,6 +42,7 @@
defaultSettingsPopupMenuSection: DefaultSettingsPopupMenuSection,
alignShortcutsToUdfpsSection: AlignShortcutsToUdfpsSection,
defaultStatusViewSection: DefaultStatusViewSection,
+ defaultStatusBarSection: DefaultStatusBarSection,
splitShadeGuidelines: SplitShadeGuidelines,
defaultNotificationStackScrollLayoutSection: DefaultNotificationStackScrollLayoutSection,
aodNotificationIconsSection: AodNotificationIconsSection,
@@ -55,6 +57,7 @@
defaultSettingsPopupMenuSection,
alignShortcutsToUdfpsSection,
defaultStatusViewSection,
+ defaultStatusBarSection,
defaultNotificationStackScrollLayoutSection,
splitShadeGuidelines,
aodNotificationIconsSection,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusBarSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusBarSection.kt
new file mode 100644
index 0000000..d6c69b3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusBarSection.kt
@@ -0,0 +1,103 @@
+/*
+ * 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.systemui.keyguard.ui.view.layout.sections
+
+import android.content.Context
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.constraintlayout.widget.ConstraintSet
+import androidx.constraintlayout.widget.ConstraintSet.END
+import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
+import androidx.constraintlayout.widget.ConstraintSet.START
+import androidx.constraintlayout.widget.ConstraintSet.TOP
+import com.android.keyguard.dagger.KeyguardStatusBarViewComponent
+import com.android.systemui.R
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.shared.model.KeyguardSection
+import com.android.systemui.shade.NotificationPanelView
+import com.android.systemui.shade.ShadeViewStateProvider
+import com.android.systemui.statusbar.phone.KeyguardStatusBarView
+import com.android.systemui.util.Utils
+import javax.inject.Inject
+
+/** A section for the status bar displayed at the top of the lockscreen. */
+class DefaultStatusBarSection
+@Inject
+constructor(
+ private val context: Context,
+ private val featureFlags: FeatureFlags,
+ private val notificationPanelView: NotificationPanelView,
+ private val keyguardStatusBarViewComponentFactory: KeyguardStatusBarViewComponent.Factory,
+) : KeyguardSection() {
+
+ private val statusBarViewId = R.id.keyguard_header
+
+ override fun addViews(constraintLayout: ConstraintLayout) {
+ if (!featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_BAR_VIEW)) {
+ return
+ }
+
+ notificationPanelView.findViewById<View>(statusBarViewId)?.let {
+ (it.parent as ViewGroup).removeView(it)
+ }
+
+ val view =
+ LayoutInflater.from(constraintLayout.context)
+ .inflate(R.layout.keyguard_status_bar, constraintLayout, false)
+ as KeyguardStatusBarView
+
+ constraintLayout.addView(view)
+ }
+
+ override fun bindData(constraintLayout: ConstraintLayout) {
+ if (!featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_BAR_VIEW)) {
+ return
+ }
+
+ val statusBarView =
+ constraintLayout.findViewById<KeyguardStatusBarView>(statusBarViewId) ?: return
+
+ val provider =
+ object : ShadeViewStateProvider {
+ override val lockscreenShadeDragProgress: Float = 0f
+ override val panelViewExpandedHeight: Float = 0f
+ override fun shouldHeadsUpBeVisible(): Boolean {
+ return false
+ }
+ }
+ val statusBarViewComponent =
+ keyguardStatusBarViewComponentFactory.build(statusBarView, provider)
+ val controller = statusBarViewComponent.keyguardStatusBarViewController
+ controller.init()
+ }
+
+ override fun applyConstraints(constraintSet: ConstraintSet) {
+ constraintSet.apply {
+ constrainHeight(statusBarViewId, Utils.getStatusBarHeaderHeightKeyguard(context))
+ connect(statusBarViewId, TOP, PARENT_ID, TOP)
+ connect(statusBarViewId, START, PARENT_ID, START)
+ connect(statusBarViewId, END, PARENT_ID, END)
+ }
+ }
+
+ override fun removeViews(constraintLayout: ConstraintLayout) {
+ constraintLayout.removeView(statusBarViewId)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt
index c7b0cb9..1cb10bd 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt
@@ -91,7 +91,7 @@
it.requireViewById<ViewGroup>(R.id.status_view_media_container)
)
keyguardViewConfigurator.get().keyguardStatusViewController = controller
- notificationPanelViewController.get().updateStatusBarViewController()
+ notificationPanelViewController.get().updateStatusViewController()
}
}
}
@@ -100,6 +100,8 @@
constraintSet.apply {
constrainWidth(statusViewId, MATCH_CONSTRAINT)
constrainHeight(statusViewId, WRAP_CONTENT)
+ // TODO(b/296122465): Constrain to the top of [DefaultStatusBarSection] and remove the
+ // extra margin below.
connect(statusViewId, TOP, PARENT_ID, TOP)
connect(statusViewId, START, PARENT_ID, START)
connect(statusViewId, END, PARENT_ID, END)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
index 05c9323..91b3357 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
@@ -17,27 +17,50 @@
package com.android.systemui.keyguard.ui.viewmodel
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
+import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.scene.shared.model.SceneKey
import javax.inject.Inject
-import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
/** Models UI state and handles user input for the lockscreen scene. */
@SysUISingleton
class LockscreenSceneViewModel
@Inject
constructor(
+ @Application applicationScope: CoroutineScope,
authenticationInteractor: AuthenticationInteractor,
+ communalInteractor: CommunalInteractor,
val longPress: KeyguardLongPressViewModel,
) {
/** The key of the scene we should switch to when swiping up. */
- val upDestinationSceneKey: Flow<SceneKey> =
- authenticationInteractor.isUnlocked.map { isUnlocked ->
- if (isUnlocked) {
- SceneKey.Gone
- } else {
- SceneKey.Bouncer
- }
+ val upDestinationSceneKey: StateFlow<SceneKey> =
+ authenticationInteractor.isUnlocked
+ .map { isUnlocked -> upDestinationSceneKey(isUnlocked) }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = upDestinationSceneKey(authenticationInteractor.isUnlocked.value),
+ )
+
+ private fun upDestinationSceneKey(isUnlocked: Boolean): SceneKey {
+ return if (isUnlocked) {
+ SceneKey.Gone
+ } else {
+ SceneKey.Bouncer
+ }
+ }
+
+ /** The key of the scene we should switch to when swiping left. */
+ val leftDestinationSceneKey: SceneKey? =
+ if (communalInteractor.isCommunalEnabled) {
+ SceneKey.Communal
+ } else {
+ null
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt b/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
index 66067b1..8143f99 100644
--- a/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
@@ -29,36 +29,18 @@
constructor(
@FaceAuthLog private val logBuffer: LogBuffer,
) {
- fun ignoredFaceAuthTrigger(uiEvent: FaceAuthUiEvent, ignoredReason: String) {
+ fun ignoredFaceAuthTrigger(uiEvent: FaceAuthUiEvent?, ignoredReason: String) {
logBuffer.log(
TAG,
DEBUG,
{
- str1 = uiEvent.reason
+ str1 = "${uiEvent?.reason}"
str2 = ignoredReason
},
{ "Ignoring trigger because $str2, Trigger reason: $str1" }
)
}
- fun queuingRequestWhileCancelling(
- alreadyQueuedRequest: FaceAuthUiEvent?,
- newRequest: FaceAuthUiEvent
- ) {
- logBuffer.log(
- TAG,
- DEBUG,
- {
- str1 = alreadyQueuedRequest?.reason
- str2 = newRequest.reason
- },
- {
- "Face auth requested while previous request is being cancelled, " +
- "already queued request: $str1 queueing the new request: $str2"
- }
- )
- }
-
fun authenticating(uiEvent: FaceAuthUiEvent) {
logBuffer.log(TAG, DEBUG, { str1 = uiEvent.reason }, { "Running authenticate for $str1" })
}
@@ -161,15 +143,6 @@
)
}
- fun launchingQueuedFaceAuthRequest(faceAuthRequestedWhileCancellation: FaceAuthUiEvent?) {
- logBuffer.log(
- TAG,
- DEBUG,
- { str1 = "${faceAuthRequestedWhileCancellation?.reason}" },
- { "Received cancellation error and starting queued face auth request: $str1" }
- )
- }
-
fun faceAuthSuccess(result: FaceManager.AuthenticationResult) {
logBuffer.log(
TAG,
@@ -182,31 +155,10 @@
)
}
- fun observedConditionChanged(newValue: Boolean, context: String) {
- logBuffer.log(
- TAG,
- DEBUG,
- {
- bool1 = newValue
- str1 = context
- },
- { "Observed condition changed: $str1, new value: $bool1" }
- )
- }
-
fun canFaceAuthRunChanged(canRun: Boolean) {
logBuffer.log(TAG, DEBUG, { bool1 = canRun }, { "canFaceAuthRun value changed to $bool1" })
}
- fun canRunDetectionChanged(canRunDetection: Boolean) {
- logBuffer.log(
- TAG,
- DEBUG,
- { bool1 = canRunDetection },
- { "canRunDetection value changed to $bool1" }
- )
- }
-
fun cancellingFaceAuth() {
logBuffer.log(TAG, DEBUG, "cancelling face auth because a gating condition became false")
}
@@ -236,7 +188,7 @@
logBuffer.log(
TAG,
DEBUG,
- { str1 = "$uiEvent" },
+ { str1 = uiEvent.reason },
{ "Requesting face auth for trigger: $str1" }
)
}
@@ -269,4 +221,77 @@
fun faceLockedOut(@CompileTimeConstant reason: String) {
logBuffer.log(TAG, DEBUG, "Face auth has been locked out: $reason")
}
+
+ fun queueingRequest(uiEvent: FaceAuthUiEvent, fallbackToDetection: Boolean) {
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ {
+ str1 = "$uiEvent"
+ bool1 = fallbackToDetection
+ },
+ { "Queueing $str1 request for face auth, fallbackToDetection: $bool1" }
+ )
+ }
+
+ fun notProcessingRequestYet(
+ uiEvent: FaceAuthUiEvent?,
+ canRunAuth: Boolean,
+ canRunDetect: Boolean,
+ cancelInProgress: Boolean
+ ) {
+ uiEvent?.let {
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ {
+ str1 = uiEvent.reason
+ bool1 = canRunAuth
+ bool2 = canRunDetect
+ bool3 = cancelInProgress
+ },
+ {
+ "Waiting to process request: reason: $str1, " +
+ "canRunAuth: $bool1, " +
+ "canRunDetect: $bool2, " +
+ "cancelInProgress: $bool3"
+ }
+ )
+ }
+ }
+
+ fun processingRequest(uiEvent: FaceAuthUiEvent?, fallbackToDetection: Boolean) {
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ {
+ str1 = "${uiEvent?.reason}"
+ bool1 = fallbackToDetection
+ },
+ { "Processing face auth request: $str1, fallbackToDetect: $bool1" }
+ )
+ }
+
+ fun clearingPendingAuthRequest(
+ @CompileTimeConstant loggingContext: String,
+ uiEvent: FaceAuthUiEvent?,
+ fallbackToDetection: Boolean?
+ ) {
+ uiEvent?.let {
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ {
+ str1 = uiEvent.reason
+ str2 = "$fallbackToDetection"
+ str3 = loggingContext
+ },
+ {
+ "Clearing pending auth: $str1, " +
+ "fallbackToDetection: $str2, " +
+ "reason: $str3"
+ }
+ )
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
index 60fd104..be48756 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
@@ -91,7 +91,13 @@
public override fun onCreate(bundle: Bundle?) {
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
- component = componentFactory.create(activity = this, view = this, resultHandler = this)
+ component =
+ componentFactory.create(
+ hostUserHandle = hostUserHandle,
+ callingPackage = callingPackage,
+ view = this,
+ resultHandler = this
+ )
component.lifecycleObservers.forEach { lifecycle.addObserver(it) }
// Create a separate configuration controller for this activity as the configuration
@@ -274,7 +280,9 @@
recentsViewController.hasRecentTasks
}
- override fun shouldShowContentPreviewWhenEmpty() = shouldShowContentPreview()
+ override fun shouldShowStickyContentPreviewWhenEmpty() = shouldShowContentPreview()
+
+ override fun shouldShowServiceTargets() = false
private fun hasWorkProfile() = mMultiProfilePagerAdapter.count > 1
@@ -286,6 +294,18 @@
override fun createContentPreviewView(parent: ViewGroup): ViewGroup =
recentsViewController.createView(parent)
+ private val hostUserHandle: UserHandle
+ get() {
+ val extras =
+ intent.extras
+ ?: error("MediaProjectionAppSelectorActivity should be launched with extras")
+ return extras.getParcelable(EXTRA_HOST_APP_USER_HANDLE)
+ ?: error(
+ "MediaProjectionAppSelectorActivity should be provided with " +
+ "$EXTRA_HOST_APP_USER_HANDLE extra"
+ )
+ }
+
companion object {
const val TAG = "MediaProjectionAppSelectorActivity"
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
index 11538fa..33d9cc3 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
@@ -24,7 +24,6 @@
import com.android.launcher3.icons.IconFactory
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.media.MediaProjectionAppSelectorActivity
-import com.android.systemui.media.MediaProjectionAppSelectorActivity.Companion.EXTRA_HOST_APP_USER_HANDLE
import com.android.systemui.media.MediaProjectionPermissionActivity
import com.android.systemui.mediaprojection.appselector.data.ActivityTaskManagerLabelLoader
import com.android.systemui.mediaprojection.appselector.data.ActivityTaskManagerThumbnailLoader
@@ -118,29 +117,8 @@
@Provides
@MediaProjectionAppSelector
@MediaProjectionAppSelectorScope
- fun provideCallerPackageName(activity: MediaProjectionAppSelectorActivity): String? =
- activity.callingPackage
-
- @Provides
- @MediaProjectionAppSelector
- @MediaProjectionAppSelectorScope
- fun bindConfigurationController(
- activity: MediaProjectionAppSelectorActivity
- ): ConfigurationController = ConfigurationControllerImpl(activity)
-
- @Provides
- @HostUserHandle
- @MediaProjectionAppSelectorScope
- fun hostUserHandle(activity: MediaProjectionAppSelectorActivity): UserHandle {
- val extras =
- activity.intent.extras
- ?: error("MediaProjectionAppSelectorActivity should be launched with extras")
- return extras.getParcelable(EXTRA_HOST_APP_USER_HANDLE)
- ?: error(
- "MediaProjectionAppSelectorActivity should be provided with " +
- "$EXTRA_HOST_APP_USER_HANDLE extra"
- )
- }
+ fun bindConfigurationController(context: Context): ConfigurationController =
+ ConfigurationControllerImpl(context)
@Provides fun bindIconFactory(context: Context): IconFactory = IconFactory.obtain(context)
@@ -161,7 +139,8 @@
interface Factory {
/** Create a factory to inject the activity into the graph */
fun create(
- @BindsInstance activity: MediaProjectionAppSelectorActivity,
+ @BindsInstance @HostUserHandle hostUserHandle: UserHandle,
+ @BindsInstance @MediaProjectionAppSelector callingPackage: String?,
@BindsInstance view: MediaProjectionAppSelectorView,
@BindsInstance resultHandler: MediaProjectionAppSelectorResultHandler,
): MediaProjectionAppSelectorComponent
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt
index 9b9d561..0c77054 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt
@@ -28,7 +28,6 @@
import android.view.WindowManager
import androidx.core.content.getSystemService
import androidx.core.content.res.use
-import com.android.internal.R as AndroidR
import com.android.systemui.R
import com.android.systemui.mediaprojection.appselector.data.RecentTask
import com.android.systemui.shared.recents.model.ThumbnailData
@@ -147,25 +146,14 @@
previewRect.set(0, 0, thumbnailData.thumbnail.width, thumbnailData.thumbnail.height)
val currentRotation: Int = display.rotation
- val displayWidthPx = windowMetrics.bounds.width()
- val displayHeightPx = windowMetrics.bounds.height()
val isRtl = layoutDirection == LAYOUT_DIRECTION_RTL
val isLargeScreen = isLargeScreen(context)
- val taskbarSize =
- if (isLargeScreen) {
- resources.getDimensionPixelSize(AndroidR.dimen.taskbar_frame_height)
- } else {
- 0
- }
previewPositionHelper.updateThumbnailMatrix(
previewRect,
thumbnailData,
measuredWidth,
measuredHeight,
- displayWidthPx,
- displayHeightPx,
- taskbarSize,
isLargeScreen,
currentRotation,
isRtl
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index 5a42028..a601d7f 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 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.
@@ -16,483 +16,58 @@
package com.android.systemui.navigationbar;
-import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
-import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_GESTURE;
-import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
-import static com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler.DEBUG_MISSING_GESTURE_TAG;
-import static com.android.systemui.shared.recents.utilities.Utilities.isLargeScreen;
-
-import android.content.Context;
-import android.content.pm.ActivityInfo;
-import android.content.res.Configuration;
-import android.hardware.display.DisplayManager;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.RemoteException;
-import android.os.Trace;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.util.Log;
-import android.util.SparseArray;
-import android.view.Display;
-import android.view.IWindowManager;
-import android.view.View;
-import android.view.WindowManagerGlobal;
-
-import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.RegisterStatusBarResult;
-import com.android.settingslib.applications.InterestingConfigChanges;
-import com.android.systemui.Dumpable;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
-import com.android.systemui.model.SysUiState;
-import com.android.systemui.recents.OverviewProxyService;
-import com.android.systemui.settings.DisplayTracker;
-import com.android.systemui.shared.system.QuickStepContract;
-import com.android.systemui.shared.system.TaskStackChangeListeners;
-import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.phone.AutoHideController;
-import com.android.systemui.statusbar.phone.BarTransitions.TransitionMode;
-import com.android.systemui.statusbar.phone.LightBarController;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.util.settings.SecureSettings;
-import com.android.wm.shell.back.BackAnimation;
-import com.android.wm.shell.pip.Pip;
-
-import java.io.PrintWriter;
-import java.util.Optional;
-
-import javax.inject.Inject;
+import com.android.systemui.statusbar.phone.BarTransitions;
/** A controller to handle navigation bars. */
-@SysUISingleton
-public class NavigationBarController implements
- ConfigurationController.ConfigurationListener,
- NavigationModeController.ModeChangedListener,
- Dumpable {
-
- private static final String TAG = NavigationBarController.class.getSimpleName();
-
- private final Context mContext;
- private final Handler mHandler;
- private final NavigationBarComponent.Factory mNavigationBarComponentFactory;
- private FeatureFlags mFeatureFlags;
- private final SecureSettings mSecureSettings;
- private final DisplayTracker mDisplayTracker;
- private final DisplayManager mDisplayManager;
- private final TaskbarDelegate mTaskbarDelegate;
- private final NavBarHelper mNavBarHelper;
- private int mNavMode;
- @VisibleForTesting boolean mIsLargeScreen;
-
- /** A displayId - nav bar maps. */
- @VisibleForTesting
- SparseArray<NavigationBar> mNavigationBars = new SparseArray<>();
-
- // Tracks config changes that will actually recreate the nav bar
- private final InterestingConfigChanges mConfigChanges = new InterestingConfigChanges(
- ActivityInfo.CONFIG_FONT_SCALE
- | ActivityInfo.CONFIG_UI_MODE);
-
- @Inject
- public NavigationBarController(Context context,
- OverviewProxyService overviewProxyService,
- NavigationModeController navigationModeController,
- SysUiState sysUiFlagsContainer,
- CommandQueue commandQueue,
- @Main Handler mainHandler,
- ConfigurationController configurationController,
- NavBarHelper navBarHelper,
- TaskbarDelegate taskbarDelegate,
- NavigationBarComponent.Factory navigationBarComponentFactory,
- DumpManager dumpManager,
- AutoHideController autoHideController,
- LightBarController lightBarController,
- TaskStackChangeListeners taskStackChangeListeners,
- Optional<Pip> pipOptional,
- Optional<BackAnimation> backAnimation,
- FeatureFlags featureFlags,
- SecureSettings secureSettings,
- DisplayTracker displayTracker) {
- mContext = context;
- mHandler = mainHandler;
- mNavigationBarComponentFactory = navigationBarComponentFactory;
- mFeatureFlags = featureFlags;
- mSecureSettings = secureSettings;
- mDisplayTracker = displayTracker;
- mDisplayManager = mContext.getSystemService(DisplayManager.class);
- commandQueue.addCallback(mCommandQueueCallbacks);
- configurationController.addCallback(this);
- mConfigChanges.applyNewConfig(mContext.getResources());
- mNavMode = navigationModeController.addListener(this);
- mNavBarHelper = navBarHelper;
- mTaskbarDelegate = taskbarDelegate;
- mTaskbarDelegate.setDependencies(commandQueue, overviewProxyService,
- navBarHelper, navigationModeController, sysUiFlagsContainer,
- dumpManager, autoHideController, lightBarController, pipOptional,
- backAnimation.orElse(null), taskStackChangeListeners);
- mIsLargeScreen = isLargeScreen(mContext);
- dumpManager.registerDumpable(this);
- }
-
- @Override
- public void onConfigChanged(Configuration newConfig) {
- boolean isOldConfigLargeScreen = mIsLargeScreen;
- mIsLargeScreen = isLargeScreen(mContext);
- boolean willApplyConfig = mConfigChanges.applyNewConfig(mContext.getResources());
- boolean largeScreenChanged = mIsLargeScreen != isOldConfigLargeScreen;
- // TODO(b/243765256): Disable this logging once b/243765256 is fixed.
- Log.i(DEBUG_MISSING_GESTURE_TAG, "NavbarController: newConfig=" + newConfig
- + " mTaskbarDelegate initialized=" + mTaskbarDelegate.isInitialized()
- + " willApplyConfigToNavbars=" + willApplyConfig
- + " navBarCount=" + mNavigationBars.size());
- if (mTaskbarDelegate.isInitialized()) {
- mTaskbarDelegate.onConfigurationChanged(newConfig);
- }
- // If we folded/unfolded while in 3 button, show navbar in folded state, hide in unfolded
- if (largeScreenChanged && updateNavbarForTaskbar()) {
- return;
- }
-
- if (willApplyConfig) {
- for (int i = 0; i < mNavigationBars.size(); i++) {
- recreateNavigationBar(mNavigationBars.keyAt(i));
- }
- } else {
- for (int i = 0; i < mNavigationBars.size(); i++) {
- mNavigationBars.valueAt(i).onConfigurationChanged(newConfig);
- }
- }
- }
-
- @Override
- public void onNavigationModeChanged(int mode) {
- if (mNavMode == mode) {
- return;
- }
- final int oldMode = mNavMode;
- mNavMode = mode;
- updateAccessibilityButtonModeIfNeeded();
-
- mHandler.post(() -> {
- // create/destroy nav bar based on nav mode only in unfolded state
- if (oldMode != mNavMode) {
- updateNavbarForTaskbar();
- }
- for (int i = 0; i < mNavigationBars.size(); i++) {
- NavigationBar navBar = mNavigationBars.valueAt(i);
- if (navBar == null) {
- continue;
- }
- navBar.getView().updateStates();
- }
- });
- }
-
- private void updateAccessibilityButtonModeIfNeeded() {
- final int mode = mSecureSettings.getIntForUser(
- Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
- ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR, UserHandle.USER_CURRENT);
-
- // ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU is compatible under gestural or non-gestural
- // mode, so we don't need to update it.
- if (mode == ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU) {
- return;
- }
-
- // ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR is incompatible under gestural mode. Need to
- // force update to ACCESSIBILITY_BUTTON_MODE_GESTURE.
- if (QuickStepContract.isGesturalMode(mNavMode)
- && mode == ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR) {
- mSecureSettings.putIntForUser(
- Settings.Secure.ACCESSIBILITY_BUTTON_MODE, ACCESSIBILITY_BUTTON_MODE_GESTURE,
- UserHandle.USER_CURRENT);
- // ACCESSIBILITY_BUTTON_MODE_GESTURE is incompatible under non gestural mode. Need to
- // force update to ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR.
- } else if (!QuickStepContract.isGesturalMode(mNavMode)
- && mode == ACCESSIBILITY_BUTTON_MODE_GESTURE) {
- mSecureSettings.putIntForUser(
- Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
- ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR, UserHandle.USER_CURRENT);
- }
- }
-
- private boolean shouldCreateNavBarAndTaskBar(int displayId) {
- final IWindowManager wms = WindowManagerGlobal.getWindowManagerService();
-
- try {
- return wms.hasNavigationBar(displayId);
- } catch (RemoteException e) {
- // Cannot get wms, just return false with warning message.
- Log.w(TAG, "Cannot get WindowManager.");
- return false;
- }
- }
-
- /** @see #initializeTaskbarIfNecessary() */
- private boolean updateNavbarForTaskbar() {
- boolean taskbarShown = initializeTaskbarIfNecessary();
- if (!taskbarShown && mNavigationBars.get(mContext.getDisplayId()) == null) {
- createNavigationBar(mContext.getDisplay(), null, null);
- }
- return taskbarShown;
- }
-
- /** @return {@code true} if taskbar is enabled, false otherwise */
- private boolean initializeTaskbarIfNecessary() {
- // Enable for large screens or (phone AND flag is set); assuming phone = !mIsLargeScreen
- boolean taskbarEnabled = (mIsLargeScreen || mFeatureFlags.isEnabled(
- Flags.HIDE_NAVBAR_WINDOW)) && shouldCreateNavBarAndTaskBar(mContext.getDisplayId());
-
- if (taskbarEnabled) {
- Trace.beginSection("NavigationBarController#initializeTaskbarIfNecessary");
- final int displayId = mContext.getDisplayId();
- // Hint to NavBarHelper if we are replacing an existing bar to skip extra work
- mNavBarHelper.setTogglingNavbarTaskbar(mNavigationBars.contains(displayId));
- // Remove navigation bar when taskbar is showing
- removeNavigationBar(displayId);
- mTaskbarDelegate.init(displayId);
- mNavBarHelper.setTogglingNavbarTaskbar(false);
- Trace.endSection();
-
- } else {
- mTaskbarDelegate.destroy();
- }
- return taskbarEnabled;
- }
-
- private final CommandQueue.Callbacks mCommandQueueCallbacks = new CommandQueue.Callbacks() {
- @Override
- public void onDisplayRemoved(int displayId) {
- removeNavigationBar(displayId);
- }
-
- @Override
- public void onDisplayReady(int displayId) {
- Display display = mDisplayManager.getDisplay(displayId);
- mIsLargeScreen = isLargeScreen(mContext);
- createNavigationBar(display, null /* savedState */, null /* result */);
- }
-
- @Override
- public void setNavigationBarLumaSamplingEnabled(int displayId, boolean enable) {
- final NavigationBar navigationBar = getNavigationBar(displayId);
- if (navigationBar != null) {
- navigationBar.setNavigationBarLumaSamplingEnabled(enable);
- }
- }
-
- @Override
- public void showPinningEnterExitToast(boolean entering) {
- int displayId = mContext.getDisplayId();
- final NavigationBarView navBarView = getNavigationBarView(displayId);
- if (navBarView != null) {
- navBarView.showPinningEnterExitToast(entering);
- } else if (displayId == mDisplayTracker.getDefaultDisplayId()
- && mTaskbarDelegate.isInitialized()) {
- mTaskbarDelegate.showPinningEnterExitToast(entering);
- }
- }
-
- @Override
- public void showPinningEscapeToast() {
- int displayId = mContext.getDisplayId();
- final NavigationBarView navBarView = getNavigationBarView(displayId);
- if (navBarView != null) {
- navBarView.showPinningEscapeToast();
- } else if (displayId == mDisplayTracker.getDefaultDisplayId()
- && mTaskbarDelegate.isInitialized()) {
- mTaskbarDelegate.showPinningEscapeToast();
- }
- }
- };
-
- /**
- * Recreates the navigation bar for the given display.
- */
- private void recreateNavigationBar(int displayId) {
- // TODO: Improve this flow so that we don't need to create a new nav bar but just
- // the view
- Bundle savedState = new Bundle();
- NavigationBar bar = mNavigationBars.get(displayId);
- if (bar != null) {
- bar.onSaveInstanceState(savedState);
- }
- removeNavigationBar(displayId);
- createNavigationBar(mDisplayManager.getDisplay(displayId), savedState, null /* result */);
- }
-
- // TODO(b/117478341): I use {@code includeDefaultDisplay} to make this method compatible to
- // CarStatusBar because they have their own nav bar. Think about a better way for it.
+public interface NavigationBarController {
/**
* Creates navigation bars when car/status bar initializes.
+ * <p>
+ * TODO(b/117478341): I use {@code includeDefaultDisplay} to make this method compatible to
+ * CarStatusBar because they have their own nav bar. Think about a better way for it.
*
* @param includeDefaultDisplay {@code true} to create navigation bar on default display.
*/
- public void createNavigationBars(final boolean includeDefaultDisplay,
- RegisterStatusBarResult result) {
- updateAccessibilityButtonModeIfNeeded();
+ void createNavigationBars(
+ boolean includeDefaultDisplay,
+ RegisterStatusBarResult result);
- // Don't need to create nav bar on the default display if we initialize TaskBar.
- final boolean shouldCreateDefaultNavbar = includeDefaultDisplay
- && !initializeTaskbarIfNecessary();
- Display[] displays = mDisplayTracker.getAllDisplays();
- for (Display display : displays) {
- if (shouldCreateDefaultNavbar
- || display.getDisplayId() != mDisplayTracker.getDefaultDisplayId()) {
- createNavigationBar(display, null /* savedState */, result);
- }
- }
- }
-
- /**
- * Adds a navigation bar on default display or an external display if the display supports
- * system decorations.
- *
- * @param display the display to add navigation bar on.
- */
- @VisibleForTesting
- void createNavigationBar(Display display, Bundle savedState, RegisterStatusBarResult result) {
- if (display == null) {
- return;
- }
-
- final int displayId = display.getDisplayId();
- final boolean isOnDefaultDisplay = displayId == mDisplayTracker.getDefaultDisplayId();
-
- if (!shouldCreateNavBarAndTaskBar(displayId)) {
- return;
- }
-
- // We may show TaskBar on the default display for large screen device. Don't need to create
- // navigation bar for this case.
- if (isOnDefaultDisplay && initializeTaskbarIfNecessary()) {
- return;
- }
-
- final Context context = isOnDefaultDisplay
- ? mContext
- : mContext.createDisplayContext(display);
- NavigationBarComponent component = mNavigationBarComponentFactory.create(
- context, savedState);
- NavigationBar navBar = component.getNavigationBar();
- navBar.init();
- mNavigationBars.put(displayId, navBar);
-
- navBar.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
- @Override
- public void onViewAttachedToWindow(View v) {
- if (result != null) {
- navBar.setImeWindowStatus(display.getDisplayId(), result.mImeToken,
- result.mImeWindowVis, result.mImeBackDisposition,
- result.mShowImeSwitcher);
- }
- }
-
- @Override
- public void onViewDetachedFromWindow(View v) {
- v.removeOnAttachStateChangeListener(this);
- }
- });
- }
-
- void removeNavigationBar(int displayId) {
- NavigationBar navBar = mNavigationBars.get(displayId);
- if (navBar != null) {
- navBar.destroyView();
- mNavigationBars.remove(displayId);
- }
- }
+ /** Removes the navigation bar for the given display ID. */
+ void removeNavigationBar(int displayId);
/** @see NavigationBar#checkNavBarModes() */
- public void checkNavBarModes(int displayId) {
- NavigationBar navBar = mNavigationBars.get(displayId);
- if (navBar != null) {
- navBar.checkNavBarModes();
- }
- }
+ void checkNavBarModes(int displayId);
/** @see NavigationBar#finishBarAnimations() */
- public void finishBarAnimations(int displayId) {
- NavigationBar navBar = mNavigationBars.get(displayId);
- if (navBar != null) {
- navBar.finishBarAnimations();
- }
- }
+ void finishBarAnimations(int displayId);
/** @see NavigationBar#touchAutoDim() */
- public void touchAutoDim(int displayId) {
- NavigationBar navBar = mNavigationBars.get(displayId);
- if (navBar != null) {
- navBar.touchAutoDim();
- }
- }
+ void touchAutoDim(int displayId);
/** @see NavigationBar#transitionTo(int, boolean) */
- public void transitionTo(int displayId, @TransitionMode int barMode, boolean animate) {
- NavigationBar navBar = mNavigationBars.get(displayId);
- if (navBar != null) {
- navBar.transitionTo(barMode, animate);
- }
- }
+ void transitionTo(int displayId, @BarTransitions.TransitionMode int barMode, boolean animate);
/** @see NavigationBar#disableAnimationsDuringHide(long) */
- public void disableAnimationsDuringHide(int displayId, long delay) {
- NavigationBar navBar = mNavigationBars.get(displayId);
- if (navBar != null) {
- navBar.disableAnimationsDuringHide(delay);
- }
- }
+ void disableAnimationsDuringHide(int displayId, long delay);
/** @return {@link NavigationBarView} on the default display. */
- public @Nullable NavigationBarView getDefaultNavigationBarView() {
- return getNavigationBarView(mDisplayTracker.getDefaultDisplayId());
- }
+ @Nullable
+ NavigationBarView getDefaultNavigationBarView();
/**
* @param displayId the ID of display which Navigation bar is on
* @return {@link NavigationBarView} on the display with {@code displayId}.
* {@code null} if no navigation bar on that display.
*/
- public @Nullable NavigationBarView getNavigationBarView(int displayId) {
- NavigationBar navBar = getNavigationBar(displayId);
- return (navBar == null) ? null : navBar.getView();
- }
+ @Nullable
+ NavigationBarView getNavigationBarView(int displayId);
- private @Nullable NavigationBar getNavigationBar(int displayId) {
- return mNavigationBars.get(displayId);
- }
-
- public boolean isOverviewEnabled(int displayId) {
- final NavigationBarView navBarView = getNavigationBarView(displayId);
- if (navBarView != null) {
- return navBarView.isOverviewEnabled();
- } else {
- return mTaskbarDelegate.isOverviewEnabled();
- }
- }
+ boolean isOverviewEnabled(int displayId);
/** @return {@link NavigationBar} on the default display. */
@Nullable
- public NavigationBar getDefaultNavigationBar() {
- return mNavigationBars.get(mDisplayTracker.getDefaultDisplayId());
- }
-
- @Override
- public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
- pw.println("mIsLargeScreen=" + mIsLargeScreen);
- pw.println("mNavMode=" + mNavMode);
- for (int i = 0; i < mNavigationBars.size(); i++) {
- if (i > 0) {
- pw.println();
- }
- mNavigationBars.valueAt(i).dump(pw);
- }
- }
+ NavigationBar getDefaultNavigationBar();
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerEmptyImpl.kt
new file mode 100644
index 0000000..e73b078
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerEmptyImpl.kt
@@ -0,0 +1,45 @@
+/*
+ * 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.systemui.navigationbar
+
+import com.android.internal.statusbar.RegisterStatusBarResult
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.phone.BarTransitions
+import javax.inject.Inject
+
+/** A no-op version of [NavigationBarController] for variants like Arc and TV. */
+@SysUISingleton
+class NavigationBarControllerEmptyImpl @Inject constructor() : NavigationBarController {
+ override fun createNavigationBars(
+ includeDefaultDisplay: Boolean,
+ result: RegisterStatusBarResult?,
+ ) {}
+ override fun removeNavigationBar(displayId: Int) {}
+ override fun checkNavBarModes(displayId: Int) {}
+ override fun finishBarAnimations(displayId: Int) {}
+ override fun touchAutoDim(displayId: Int) {}
+ override fun transitionTo(
+ displayId: Int,
+ @BarTransitions.TransitionMode barMode: Int,
+ animate: Boolean,
+ ) {}
+ override fun disableAnimationsDuringHide(displayId: Int, delay: Long) {}
+ override fun getDefaultNavigationBarView(): NavigationBarView? = null
+ override fun getNavigationBarView(displayId: Int): NavigationBarView? = null
+ override fun isOverviewEnabled(displayId: Int) = false
+ override fun getDefaultNavigationBar(): NavigationBar? = null
+}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
new file mode 100644
index 0000000..564e984
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
@@ -0,0 +1,490 @@
+/*
+ * Copyright (C) 2020 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.navigationbar;
+
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_GESTURE;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
+import static com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler.DEBUG_MISSING_GESTURE_TAG;
+import static com.android.systemui.shared.recents.utilities.Utilities.isLargeScreen;
+
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
+import android.hardware.display.DisplayManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.Display;
+import android.view.IWindowManager;
+import android.view.View;
+import android.view.WindowManagerGlobal;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.statusbar.RegisterStatusBarResult;
+import com.android.settingslib.applications.InterestingConfigChanges;
+import com.android.systemui.Dumpable;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
+import com.android.systemui.model.SysUiState;
+import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.settings.DisplayTracker;
+import com.android.systemui.shared.system.QuickStepContract;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
+import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.phone.AutoHideController;
+import com.android.systemui.statusbar.phone.BarTransitions.TransitionMode;
+import com.android.systemui.statusbar.phone.LightBarController;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.util.settings.SecureSettings;
+import com.android.wm.shell.back.BackAnimation;
+import com.android.wm.shell.pip.Pip;
+
+import java.io.PrintWriter;
+import java.util.Optional;
+
+import javax.inject.Inject;
+
+@SysUISingleton
+public class NavigationBarControllerImpl implements
+ ConfigurationController.ConfigurationListener,
+ NavigationModeController.ModeChangedListener,
+ Dumpable, NavigationBarController {
+
+ private static final String TAG = NavigationBarControllerImpl.class.getSimpleName();
+
+ private final Context mContext;
+ private final Handler mHandler;
+ private final NavigationBarComponent.Factory mNavigationBarComponentFactory;
+ private FeatureFlags mFeatureFlags;
+ private final SecureSettings mSecureSettings;
+ private final DisplayTracker mDisplayTracker;
+ private final DisplayManager mDisplayManager;
+ private final TaskbarDelegate mTaskbarDelegate;
+ private final NavBarHelper mNavBarHelper;
+ private int mNavMode;
+ @VisibleForTesting boolean mIsLargeScreen;
+
+ /** A displayId - nav bar maps. */
+ @VisibleForTesting
+ SparseArray<NavigationBar> mNavigationBars = new SparseArray<>();
+
+ // Tracks config changes that will actually recreate the nav bar
+ private final InterestingConfigChanges mConfigChanges = new InterestingConfigChanges(
+ ActivityInfo.CONFIG_FONT_SCALE
+ | ActivityInfo.CONFIG_UI_MODE);
+
+ @Inject
+ public NavigationBarControllerImpl(Context context,
+ OverviewProxyService overviewProxyService,
+ NavigationModeController navigationModeController,
+ SysUiState sysUiFlagsContainer,
+ CommandQueue commandQueue,
+ @Main Handler mainHandler,
+ ConfigurationController configurationController,
+ NavBarHelper navBarHelper,
+ TaskbarDelegate taskbarDelegate,
+ NavigationBarComponent.Factory navigationBarComponentFactory,
+ DumpManager dumpManager,
+ AutoHideController autoHideController,
+ LightBarController lightBarController,
+ TaskStackChangeListeners taskStackChangeListeners,
+ Optional<Pip> pipOptional,
+ Optional<BackAnimation> backAnimation,
+ FeatureFlags featureFlags,
+ SecureSettings secureSettings,
+ DisplayTracker displayTracker) {
+ mContext = context;
+ mHandler = mainHandler;
+ mNavigationBarComponentFactory = navigationBarComponentFactory;
+ mFeatureFlags = featureFlags;
+ mSecureSettings = secureSettings;
+ mDisplayTracker = displayTracker;
+ mDisplayManager = mContext.getSystemService(DisplayManager.class);
+ commandQueue.addCallback(mCommandQueueCallbacks);
+ configurationController.addCallback(this);
+ mConfigChanges.applyNewConfig(mContext.getResources());
+ mNavMode = navigationModeController.addListener(this);
+ mNavBarHelper = navBarHelper;
+ mTaskbarDelegate = taskbarDelegate;
+ mTaskbarDelegate.setDependencies(commandQueue, overviewProxyService,
+ navBarHelper, navigationModeController, sysUiFlagsContainer,
+ dumpManager, autoHideController, lightBarController, pipOptional,
+ backAnimation.orElse(null), taskStackChangeListeners);
+ mIsLargeScreen = isLargeScreen(mContext);
+ dumpManager.registerDumpable(this);
+ }
+
+ @Override
+ public void onConfigChanged(Configuration newConfig) {
+ boolean isOldConfigLargeScreen = mIsLargeScreen;
+ mIsLargeScreen = isLargeScreen(mContext);
+ boolean willApplyConfig = mConfigChanges.applyNewConfig(mContext.getResources());
+ boolean largeScreenChanged = mIsLargeScreen != isOldConfigLargeScreen;
+ // TODO(b/243765256): Disable this logging once b/243765256 is fixed.
+ Log.i(DEBUG_MISSING_GESTURE_TAG, "NavbarController: newConfig=" + newConfig
+ + " mTaskbarDelegate initialized=" + mTaskbarDelegate.isInitialized()
+ + " willApplyConfigToNavbars=" + willApplyConfig
+ + " navBarCount=" + mNavigationBars.size());
+ if (mTaskbarDelegate.isInitialized()) {
+ mTaskbarDelegate.onConfigurationChanged(newConfig);
+ }
+ // If we folded/unfolded while in 3 button, show navbar in folded state, hide in unfolded
+ if (largeScreenChanged && updateNavbarForTaskbar()) {
+ return;
+ }
+
+ if (willApplyConfig) {
+ for (int i = 0; i < mNavigationBars.size(); i++) {
+ recreateNavigationBar(mNavigationBars.keyAt(i));
+ }
+ } else {
+ for (int i = 0; i < mNavigationBars.size(); i++) {
+ mNavigationBars.valueAt(i).onConfigurationChanged(newConfig);
+ }
+ }
+ }
+
+ @Override
+ public void onNavigationModeChanged(int mode) {
+ if (mNavMode == mode) {
+ return;
+ }
+ final int oldMode = mNavMode;
+ mNavMode = mode;
+ updateAccessibilityButtonModeIfNeeded();
+
+ mHandler.post(() -> {
+ // create/destroy nav bar based on nav mode only in unfolded state
+ if (oldMode != mNavMode) {
+ updateNavbarForTaskbar();
+ }
+ for (int i = 0; i < mNavigationBars.size(); i++) {
+ NavigationBar navBar = mNavigationBars.valueAt(i);
+ if (navBar == null) {
+ continue;
+ }
+ navBar.getView().updateStates();
+ }
+ });
+ }
+
+ private void updateAccessibilityButtonModeIfNeeded() {
+ final int mode = mSecureSettings.getIntForUser(
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
+ ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR, UserHandle.USER_CURRENT);
+
+ // ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU is compatible under gestural or non-gestural
+ // mode, so we don't need to update it.
+ if (mode == ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU) {
+ return;
+ }
+
+ // ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR is incompatible under gestural mode. Need to
+ // force update to ACCESSIBILITY_BUTTON_MODE_GESTURE.
+ if (QuickStepContract.isGesturalMode(mNavMode)
+ && mode == ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR) {
+ mSecureSettings.putIntForUser(
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE, ACCESSIBILITY_BUTTON_MODE_GESTURE,
+ UserHandle.USER_CURRENT);
+ // ACCESSIBILITY_BUTTON_MODE_GESTURE is incompatible under non gestural mode. Need to
+ // force update to ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR.
+ } else if (!QuickStepContract.isGesturalMode(mNavMode)
+ && mode == ACCESSIBILITY_BUTTON_MODE_GESTURE) {
+ mSecureSettings.putIntForUser(
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
+ ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR, UserHandle.USER_CURRENT);
+ }
+ }
+
+ private boolean shouldCreateNavBarAndTaskBar(int displayId) {
+ final IWindowManager wms = WindowManagerGlobal.getWindowManagerService();
+
+ try {
+ return wms.hasNavigationBar(displayId);
+ } catch (RemoteException e) {
+ // Cannot get wms, just return false with warning message.
+ Log.w(TAG, "Cannot get WindowManager.");
+ return false;
+ }
+ }
+
+ /** @see #initializeTaskbarIfNecessary() */
+ private boolean updateNavbarForTaskbar() {
+ boolean taskbarShown = initializeTaskbarIfNecessary();
+ if (!taskbarShown && mNavigationBars.get(mContext.getDisplayId()) == null) {
+ createNavigationBar(mContext.getDisplay(), null, null);
+ }
+ return taskbarShown;
+ }
+
+ /** @return {@code true} if taskbar is enabled, false otherwise */
+ private boolean initializeTaskbarIfNecessary() {
+ // Enable for large screens or (phone AND flag is set); assuming phone = !mIsLargeScreen
+ boolean taskbarEnabled = (mIsLargeScreen || mFeatureFlags.isEnabled(
+ Flags.HIDE_NAVBAR_WINDOW)) && shouldCreateNavBarAndTaskBar(mContext.getDisplayId());
+
+ if (taskbarEnabled) {
+ Trace.beginSection("NavigationBarController#initializeTaskbarIfNecessary");
+ final int displayId = mContext.getDisplayId();
+ // Hint to NavBarHelper if we are replacing an existing bar to skip extra work
+ mNavBarHelper.setTogglingNavbarTaskbar(mNavigationBars.contains(displayId));
+ // Remove navigation bar when taskbar is showing
+ removeNavigationBar(displayId);
+ mTaskbarDelegate.init(displayId);
+ mNavBarHelper.setTogglingNavbarTaskbar(false);
+ Trace.endSection();
+
+ } else {
+ mTaskbarDelegate.destroy();
+ }
+ return taskbarEnabled;
+ }
+
+ private final CommandQueue.Callbacks mCommandQueueCallbacks = new CommandQueue.Callbacks() {
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ removeNavigationBar(displayId);
+ }
+
+ @Override
+ public void onDisplayReady(int displayId) {
+ Display display = mDisplayManager.getDisplay(displayId);
+ mIsLargeScreen = isLargeScreen(mContext);
+ createNavigationBar(display, null /* savedState */, null /* result */);
+ }
+
+ @Override
+ public void setNavigationBarLumaSamplingEnabled(int displayId, boolean enable) {
+ final NavigationBar navigationBar = getNavigationBar(displayId);
+ if (navigationBar != null) {
+ navigationBar.setNavigationBarLumaSamplingEnabled(enable);
+ }
+ }
+
+ @Override
+ public void showPinningEnterExitToast(boolean entering) {
+ int displayId = mContext.getDisplayId();
+ final NavigationBarView navBarView = getNavigationBarView(displayId);
+ if (navBarView != null) {
+ navBarView.showPinningEnterExitToast(entering);
+ } else if (displayId == mDisplayTracker.getDefaultDisplayId()
+ && mTaskbarDelegate.isInitialized()) {
+ mTaskbarDelegate.showPinningEnterExitToast(entering);
+ }
+ }
+
+ @Override
+ public void showPinningEscapeToast() {
+ int displayId = mContext.getDisplayId();
+ final NavigationBarView navBarView = getNavigationBarView(displayId);
+ if (navBarView != null) {
+ navBarView.showPinningEscapeToast();
+ } else if (displayId == mDisplayTracker.getDefaultDisplayId()
+ && mTaskbarDelegate.isInitialized()) {
+ mTaskbarDelegate.showPinningEscapeToast();
+ }
+ }
+ };
+
+ /**
+ * Recreates the navigation bar for the given display.
+ */
+ private void recreateNavigationBar(int displayId) {
+ // TODO: Improve this flow so that we don't need to create a new nav bar but just
+ // the view
+ Bundle savedState = new Bundle();
+ NavigationBar bar = mNavigationBars.get(displayId);
+ if (bar != null) {
+ bar.onSaveInstanceState(savedState);
+ }
+ removeNavigationBar(displayId);
+ createNavigationBar(mDisplayManager.getDisplay(displayId), savedState, null /* result */);
+ }
+
+ @Override
+ public void createNavigationBars(final boolean includeDefaultDisplay,
+ RegisterStatusBarResult result) {
+ updateAccessibilityButtonModeIfNeeded();
+
+ // Don't need to create nav bar on the default display if we initialize TaskBar.
+ final boolean shouldCreateDefaultNavbar = includeDefaultDisplay
+ && !initializeTaskbarIfNecessary();
+ Display[] displays = mDisplayTracker.getAllDisplays();
+ for (Display display : displays) {
+ if (shouldCreateDefaultNavbar
+ || display.getDisplayId() != mDisplayTracker.getDefaultDisplayId()) {
+ createNavigationBar(display, null /* savedState */, result);
+ }
+ }
+ }
+
+ /**
+ * Adds a navigation bar on default display or an external display if the display supports
+ * system decorations.
+ *
+ * @param display the display to add navigation bar on.
+ */
+ @VisibleForTesting
+ void createNavigationBar(Display display, Bundle savedState,
+ RegisterStatusBarResult result) {
+ if (display == null) {
+ return;
+ }
+
+ final int displayId = display.getDisplayId();
+ final boolean isOnDefaultDisplay = displayId == mDisplayTracker.getDefaultDisplayId();
+
+ if (!shouldCreateNavBarAndTaskBar(displayId)) {
+ return;
+ }
+
+ // We may show TaskBar on the default display for large screen device. Don't need to create
+ // navigation bar for this case.
+ if (isOnDefaultDisplay && initializeTaskbarIfNecessary()) {
+ return;
+ }
+
+ final Context context = isOnDefaultDisplay
+ ? mContext
+ : mContext.createDisplayContext(display);
+ NavigationBarComponent component = mNavigationBarComponentFactory.create(
+ context, savedState);
+ NavigationBar navBar = component.getNavigationBar();
+ navBar.init();
+ mNavigationBars.put(displayId, navBar);
+
+ navBar.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
+ @Override
+ public void onViewAttachedToWindow(View v) {
+ if (result != null) {
+ navBar.setImeWindowStatus(display.getDisplayId(), result.mImeToken,
+ result.mImeWindowVis, result.mImeBackDisposition,
+ result.mShowImeSwitcher);
+ }
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View v) {
+ v.removeOnAttachStateChangeListener(this);
+ }
+ });
+ }
+
+ @Override
+ public void removeNavigationBar(int displayId) {
+ NavigationBar navBar = mNavigationBars.get(displayId);
+ if (navBar != null) {
+ navBar.destroyView();
+ mNavigationBars.remove(displayId);
+ }
+ }
+
+ @Override
+ public void checkNavBarModes(int displayId) {
+ NavigationBar navBar = mNavigationBars.get(displayId);
+ if (navBar != null) {
+ navBar.checkNavBarModes();
+ }
+ }
+
+ @Override
+ public void finishBarAnimations(int displayId) {
+ NavigationBar navBar = mNavigationBars.get(displayId);
+ if (navBar != null) {
+ navBar.finishBarAnimations();
+ }
+ }
+
+ @Override
+ public void touchAutoDim(int displayId) {
+ NavigationBar navBar = mNavigationBars.get(displayId);
+ if (navBar != null) {
+ navBar.touchAutoDim();
+ }
+ }
+
+ @Override
+ public void transitionTo(int displayId, @TransitionMode int barMode, boolean animate) {
+ NavigationBar navBar = mNavigationBars.get(displayId);
+ if (navBar != null) {
+ navBar.transitionTo(barMode, animate);
+ }
+ }
+
+ @Override
+ public void disableAnimationsDuringHide(int displayId, long delay) {
+ NavigationBar navBar = mNavigationBars.get(displayId);
+ if (navBar != null) {
+ navBar.disableAnimationsDuringHide(delay);
+ }
+ }
+
+ @Override
+ public @Nullable NavigationBarView getDefaultNavigationBarView() {
+ return getNavigationBarView(mDisplayTracker.getDefaultDisplayId());
+ }
+
+ @Override
+ public @Nullable NavigationBarView getNavigationBarView(int displayId) {
+ NavigationBar navBar = getNavigationBar(displayId);
+ return (navBar == null) ? null : navBar.getView();
+ }
+
+ private @Nullable NavigationBar getNavigationBar(int displayId) {
+ return mNavigationBars.get(displayId);
+ }
+
+ @Override
+ public boolean isOverviewEnabled(int displayId) {
+ final NavigationBarView navBarView = getNavigationBarView(displayId);
+ if (navBarView != null) {
+ return navBarView.isOverviewEnabled();
+ } else {
+ return mTaskbarDelegate.isOverviewEnabled();
+ }
+ }
+
+ @Override
+ @Nullable
+ public NavigationBar getDefaultNavigationBar() {
+ return mNavigationBars.get(mDisplayTracker.getDefaultDisplayId());
+ }
+
+ @Override
+ public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
+ pw.println("mIsLargeScreen=" + mIsLargeScreen);
+ pw.println("mNavMode=" + mNavMode);
+ for (int i = 0; i < mNavigationBars.size(); i++) {
+ if (i > 0) {
+ pw.println();
+ }
+ mNavigationBars.valueAt(i).dump(pw);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerModule.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerModule.kt
new file mode 100644
index 0000000..448f280
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerModule.kt
@@ -0,0 +1,27 @@
+/*
+ * 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.systemui.navigationbar
+
+import dagger.Binds
+import dagger.Module
+
+/** A module providing an instance of [NavigationBarController]. */
+@Module
+abstract class NavigationBarControllerModule {
+ @Binds
+ abstract fun navigationBarController(impl: NavigationBarControllerImpl): NavigationBarController
+}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NoopNavigationBarControllerModule.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/NoopNavigationBarControllerModule.kt
new file mode 100644
index 0000000..b59912a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NoopNavigationBarControllerModule.kt
@@ -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.systemui.navigationbar
+
+import dagger.Binds
+import dagger.Module
+
+/** A module providing a no-op instance of [NavigationBarController]. */
+@Module
+abstract class NoopNavigationBarControllerModule {
+ @Binds
+ abstract fun navigationBarController(
+ impl: NavigationBarControllerEmptyImpl
+ ): NavigationBarController
+}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
index 0842fe0..ea8eb36 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
@@ -321,7 +321,7 @@
// When switched to a secondary user, the sysUI is still running in the main user, we will
// need to update the shortcut in the secondary user.
if (user == getCurrentRunningUser()) {
- updateNoteTaskAsUserInternal(user)
+ launchUpdateNoteTaskAsUser(user)
} else {
// TODO(b/278729185): Replace fire and forget service with a bounded service.
val intent = NoteTaskControllerUpdateService.createIntent(context)
@@ -330,23 +330,25 @@
}
@InternalNoteTaskApi
- fun updateNoteTaskAsUserInternal(user: UserHandle) {
- if (!userManager.isUserUnlocked(user)) {
- debugLog { "updateNoteTaskAsUserInternal call but user locked: user=$user" }
- return
- }
+ fun launchUpdateNoteTaskAsUser(user: UserHandle) {
+ applicationScope.launch {
+ if (!userManager.isUserUnlocked(user)) {
+ debugLog { "updateNoteTaskAsUserInternal call but user locked: user=$user" }
+ return@launch
+ }
- val packageName = roleManager.getDefaultRoleHolderAsUser(ROLE_NOTES, user)
- val hasNotesRoleHolder = isEnabled && !packageName.isNullOrEmpty()
+ val packageName = roleManager.getDefaultRoleHolderAsUser(ROLE_NOTES, user)
+ val hasNotesRoleHolder = isEnabled && !packageName.isNullOrEmpty()
- setNoteTaskShortcutEnabled(hasNotesRoleHolder, user)
+ setNoteTaskShortcutEnabled(hasNotesRoleHolder, user)
- if (hasNotesRoleHolder) {
- shortcutManager.enableShortcuts(listOf(SHORTCUT_ID))
- val updatedShortcut = roleManager.createNoteShortcutInfoAsUser(context, user)
- shortcutManager.updateShortcuts(listOf(updatedShortcut))
- } else {
- shortcutManager.disableShortcuts(listOf(SHORTCUT_ID))
+ if (hasNotesRoleHolder) {
+ shortcutManager.enableShortcuts(listOf(SHORTCUT_ID))
+ val updatedShortcut = roleManager.createNoteShortcutInfoAsUser(context, user)
+ shortcutManager.updateShortcuts(listOf(updatedShortcut))
+ } else {
+ shortcutManager.disableShortcuts(listOf(SHORTCUT_ID))
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskControllerUpdateService.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskControllerUpdateService.kt
index 3e352af..486fde1 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskControllerUpdateService.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskControllerUpdateService.kt
@@ -44,7 +44,7 @@
override fun onCreate() {
super.onCreate()
// TODO(b/278729185): Replace fire and forget service with a bounded service.
- controller.updateNoteTaskAsUserInternal(user)
+ controller.launchUpdateNoteTaskAsUser(user)
stopSelf()
}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
index fe1034a..338d3ed 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
@@ -23,6 +23,7 @@
import android.view.KeyEvent
import android.view.KeyEvent.KEYCODE_N
import android.view.KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL
+import android.view.ViewConfiguration
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.dagger.qualifiers.Background
@@ -65,12 +66,6 @@
* [NoteTaskController], ensure custom actions can be triggered (i.e., keyboard shortcut).
*/
private fun initializeHandleSystemKey() {
- val callbacks =
- object : CommandQueue.Callbacks {
- override fun handleSystemKey(key: KeyEvent) {
- key.toNoteTaskEntryPointOrNull()?.let(controller::showNoteTask)
- }
- }
commandQueue.addCallback(callbacks)
}
@@ -134,15 +129,39 @@
controller.updateNoteTaskForCurrentUserAndManagedProfiles()
}
}
-}
-/**
- * Maps a [KeyEvent] to a [NoteTaskEntryPoint]. If the [KeyEvent] does not represent a
- * [NoteTaskEntryPoint], returns null.
- */
-private fun KeyEvent.toNoteTaskEntryPointOrNull(): NoteTaskEntryPoint? =
- when {
- keyCode == KEYCODE_STYLUS_BUTTON_TAIL -> TAIL_BUTTON
- keyCode == KEYCODE_N && isMetaPressed && isCtrlPressed -> KEYBOARD_SHORTCUT
- else -> null
+ /**
+ * Tracks a [KeyEvent], and determines if it should trigger an action to show the note task.
+ * Returns a [NoteTaskEntryPoint] if an action should be taken, and null otherwise.
+ */
+ private fun KeyEvent.toNoteTaskEntryPointOrNull(): NoteTaskEntryPoint? =
+ when {
+ keyCode == KEYCODE_STYLUS_BUTTON_TAIL && isTailButtonNotesGesture() -> TAIL_BUTTON
+ keyCode == KEYCODE_N && isMetaPressed && isCtrlPressed -> KEYBOARD_SHORTCUT
+ else -> null
+ }
+
+ private var lastStylusButtonTailUpEventTime: Long = -MULTI_PRESS_TIMEOUT
+
+ /**
+ * Perform gesture detection for the stylus tail button to make sure we only show the note task
+ * when there is a single press. Long presses and multi-presses are ignored for now.
+ */
+ private fun KeyEvent.isTailButtonNotesGesture(): Boolean {
+ if (keyCode != KEYCODE_STYLUS_BUTTON_TAIL || action != KeyEvent.ACTION_UP) {
+ return false
+ }
+
+ val isMultiPress = (downTime - lastStylusButtonTailUpEventTime) < MULTI_PRESS_TIMEOUT
+ val isLongPress = (eventTime - downTime) >= LONG_PRESS_TIMEOUT
+ lastStylusButtonTailUpEventTime = eventTime
+ // For now, trigger action immediately on UP of a single press, without waiting for
+ // the multi-press timeout to expire.
+ return !isMultiPress && !isLongPress
}
+
+ companion object {
+ val MULTI_PRESS_TIMEOUT = ViewConfiguration.getMultiPressTimeout().toLong()
+ val LONG_PRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout().toLong()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
index 544e6ad..e382eca 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
@@ -20,6 +20,7 @@
import static android.provider.Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT;
import static com.android.systemui.wallet.controller.QuickAccessWalletController.WalletChangeEvent.DEFAULT_PAYMENT_APP_CHANGE;
+import static com.android.systemui.wallet.util.WalletCardUtilsKt.getPaymentCards;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -210,7 +211,7 @@
public void onWalletCardsRetrieved(@NonNull GetWalletCardsResponse response) {
Log.i(TAG, "Successfully retrieved wallet cards.");
mIsWalletUpdating = false;
- List<WalletCard> cards = response.getWalletCards();
+ List<WalletCard> cards = getPaymentCards(response.getWalletCards());
if (cards.isEmpty()) {
Log.d(TAG, "No wallet cards exist.");
mCardViewDrawable = null;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserActionHandler.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserActionHandler.kt
new file mode 100644
index 0000000..e9f907c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserActionHandler.kt
@@ -0,0 +1,30 @@
+package com.android.systemui.qs.tiles.base.actions
+
+import android.content.Intent
+import com.android.internal.jank.InteractionJankMonitor
+import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
+import javax.inject.Inject
+
+/**
+ * Provides a shortcut to start an activity from [QSTileUserActionInteractor]. It supports keyguard
+ * dismissing and tile from-view animations.
+ */
+@SysUISingleton
+class QSTileIntentUserActionHandler
+@Inject
+constructor(private val activityStarter: ActivityStarter) {
+
+ fun handle(userAction: QSTileUserAction, intent: Intent) {
+ val animationController: ActivityLaunchAnimator.Controller? =
+ userAction.view?.let {
+ ActivityLaunchAnimator.Controller.fromView(
+ it,
+ InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE,
+ )
+ }
+ activityStarter.postStartActivityDismissingKeyguard(intent, 0, animationController)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileDataToStateMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileDataToStateMapper.kt
new file mode 100644
index 0000000..d6c9705
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileDataToStateMapper.kt
@@ -0,0 +1,14 @@
+package com.android.systemui.qs.tiles.base.interactor
+
+import androidx.annotation.WorkerThread
+import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileState
+
+interface QSTileDataToStateMapper<DATA_TYPE> {
+
+ /**
+ * Maps [DATA_TYPE] to the [QSTileState] that is then displayed by the View layer. It's called
+ * on a background thread, so it's safe to perform long running operations there.
+ */
+ @WorkerThread fun map(config: QSTileConfig, data: DATA_TYPE): QSTileState
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/BaseQSTileViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/BaseQSTileViewModel.kt
new file mode 100644
index 0000000..c2a75fa
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/BaseQSTileViewModel.kt
@@ -0,0 +1,185 @@
+package com.android.systemui.qs.tiles.base.viewmodel
+
+import androidx.annotation.CallSuper
+import androidx.annotation.VisibleForTesting
+import com.android.internal.util.Preconditions
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataRequest
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.interactor.StateUpdateTrigger
+import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileLifecycle
+import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
+import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
+import com.android.systemui.util.kotlin.sample
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.cancelChildren
+import kotlinx.coroutines.channels.BufferOverflow
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.SharedFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.flow.shareIn
+import kotlinx.coroutines.flow.stateIn
+
+/**
+ * Provides a hassle-free way to implement new tiles according to current System UI architecture
+ * standards. THis ViewModel is cheap to instantiate and does nothing until it's moved to
+ * [QSTileLifecycle.ALIVE] state.
+ */
+abstract class BaseQSTileViewModel<DATA_TYPE>
+@VisibleForTesting
+constructor(
+ final override val config: QSTileConfig,
+ private val userActionInteractor: QSTileUserActionInteractor<DATA_TYPE>,
+ private val tileDataInteractor: QSTileDataInteractor<DATA_TYPE>,
+ private val mapper: QSTileDataToStateMapper<DATA_TYPE>,
+ private val backgroundDispatcher: CoroutineDispatcher,
+ private val tileScope: CoroutineScope,
+) : QSTileViewModel {
+
+ /**
+ * @param config contains all the static information (like TileSpec) about the tile.
+ * @param userActionInteractor encapsulates user input processing logic. Use it to start
+ * activities, show dialogs or otherwise update the tile state.
+ * @param tileDataInteractor provides [DATA_TYPE] and its availability.
+ * @param backgroundDispatcher is used to run the internal [DATA_TYPE] processing and call
+ * interactors methods. This should likely to be @Background CoroutineDispatcher.
+ * @param mapper maps [DATA_TYPE] to the [QSTileState] that is then displayed by the View layer.
+ * It's called in [backgroundDispatcher], so it's safe to perform long running operations
+ * there.
+ */
+ constructor(
+ config: QSTileConfig,
+ userActionInteractor: QSTileUserActionInteractor<DATA_TYPE>,
+ tileDataInteractor: QSTileDataInteractor<DATA_TYPE>,
+ mapper: QSTileDataToStateMapper<DATA_TYPE>,
+ backgroundDispatcher: CoroutineDispatcher,
+ ) : this(
+ config,
+ userActionInteractor,
+ tileDataInteractor,
+ mapper,
+ backgroundDispatcher,
+ CoroutineScope(SupervisorJob())
+ )
+
+ private val userInputs: MutableSharedFlow<QSTileUserAction> =
+ MutableSharedFlow(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
+ private val userIds: MutableSharedFlow<Int> =
+ MutableSharedFlow(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
+ private val forceUpdates: MutableSharedFlow<Unit> =
+ MutableSharedFlow(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
+
+ private lateinit var tileData: SharedFlow<DATA_TYPE>
+
+ override lateinit var state: SharedFlow<QSTileState>
+ override val isAvailable: StateFlow<Boolean> =
+ tileDataInteractor
+ .availability()
+ .flowOn(backgroundDispatcher)
+ .stateIn(
+ tileScope,
+ SharingStarted.WhileSubscribed(),
+ false,
+ )
+
+ private var currentLifeState: QSTileLifecycle = QSTileLifecycle.DEAD
+
+ @CallSuper
+ override fun forceUpdate() {
+ Preconditions.checkState(currentLifeState == QSTileLifecycle.ALIVE)
+ forceUpdates.tryEmit(Unit)
+ }
+
+ @CallSuper
+ override fun onUserIdChanged(userId: Int) {
+ Preconditions.checkState(currentLifeState == QSTileLifecycle.ALIVE)
+ userIds.tryEmit(userId)
+ }
+
+ @CallSuper
+ override fun onActionPerformed(userAction: QSTileUserAction) {
+ Preconditions.checkState(tileData.replayCache.isNotEmpty())
+ Preconditions.checkState(currentLifeState == QSTileLifecycle.ALIVE)
+ userInputs.tryEmit(userAction)
+ }
+
+ @CallSuper
+ override fun onLifecycle(lifecycle: QSTileLifecycle) {
+ when (lifecycle) {
+ QSTileLifecycle.ALIVE -> {
+ Preconditions.checkState(currentLifeState == QSTileLifecycle.DEAD)
+ tileData = createTileDataFlow()
+ state =
+ tileData
+ // TODO(b/299908705): log data and corresponding tile state
+ .map { mapper.map(config, it) }
+ .flowOn(backgroundDispatcher)
+ .shareIn(
+ tileScope,
+ SharingStarted.WhileSubscribed(),
+ replay = 1,
+ )
+ }
+ QSTileLifecycle.DEAD -> {
+ Preconditions.checkState(currentLifeState == QSTileLifecycle.ALIVE)
+ tileScope.coroutineContext.cancelChildren()
+ }
+ }
+ currentLifeState = lifecycle
+ }
+
+ private fun createTileDataFlow(): SharedFlow<DATA_TYPE> =
+ userIds
+ .flatMapLatest { userId ->
+ merge(
+ userInputFlow(),
+ forceUpdates.map { StateUpdateTrigger.ForceUpdate },
+ )
+ .onStart { emit(StateUpdateTrigger.InitialRequest) }
+ .map { trigger -> QSTileDataRequest(userId, trigger) }
+ }
+ .flatMapLatest { request ->
+ // 1) get an updated data source
+ // 2) process user input, possibly triggering new data to be emitted
+ // This handles the case when the data isn't buffered in the interactor
+ // TODO(b/299908705): Log events that trigger data flow to update
+ val dataFlow = tileDataInteractor.tileData(request)
+ if (request.trigger is StateUpdateTrigger.UserAction<*>) {
+ userActionInteractor.handleInput(
+ request.trigger.action,
+ request.trigger.tileData as DATA_TYPE,
+ )
+ }
+ dataFlow
+ }
+ .flowOn(backgroundDispatcher)
+ .shareIn(
+ tileScope,
+ SharingStarted.WhileSubscribed(),
+ replay = 1, // we only care about the most recent value
+ )
+
+ private fun userInputFlow(): Flow<StateUpdateTrigger> {
+ data class StateWithData<T>(val state: QSTileState, val data: T)
+
+ // Skip the input until there is some data
+ return userInputs.sample(
+ state.combine(tileData) { state, data -> StateWithData(state, data) }
+ ) { input, stateWithData ->
+ StateUpdateTrigger.UserAction(input, stateWithData.state, stateWithData.data)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileLifecycle.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileLifecycle.kt
index 39db703..1d5c1bc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileLifecycle.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileLifecycle.kt
@@ -1,6 +1,6 @@
package com.android.systemui.qs.tiles.viewmodel
enum class QSTileLifecycle {
- ON_CREATE,
- ON_DESTROY,
+ ALIVE,
+ DEAD,
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt
index 49077f3..d66d0a1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt
@@ -1,28 +1,35 @@
package com.android.systemui.qs.tiles.viewmodel
-import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.StateFlow
/**
* Represents tiles behaviour logic. This ViewModel is a connection between tile view and data
- * layers.
+ * layers. All direct inheritors must be added to the [QSTileViewModelInterfaceComplianceTest] class
+ * to pass compliance tests.
+ *
+ * All methods of this view model should be considered running on the main thread. This means no
+ * synchronous long running operations are permitted in any method.
*/
interface QSTileViewModel {
/**
- * State of the tile to be shown by the view. Favor reactive consumption over the
- * [StateFlow.value], because there is no guarantee that current value would be available at any
- * time.
+ * State of the tile to be shown by the view. It's guaranteed that it's only accessed between
+ * [QSTileLifecycle.ALIVE] and [QSTileLifecycle.DEAD].
*/
- val state: StateFlow<QSTileState>
+ val state: SharedFlow<QSTileState>
val config: QSTileConfig
- val isAvailable: Flow<Boolean>
+ /**
+ * Specifies whether this device currently supports this tile. This might be called outside of
+ * [QSTileLifecycle.ALIVE] and [QSTileLifecycle.DEAD] bounds (for example in Edit Mode).
+ */
+ val isAvailable: StateFlow<Boolean>
/**
* Handles ViewModel lifecycle. Implementations should be inactive outside of
- * [QSTileLifecycle.ON_CREATE] and [QSTileLifecycle.ON_DESTROY] bounds.
+ * [QSTileLifecycle.ALIVE] and [QSTileLifecycle.DEAD] bounds.
*/
fun onLifecycle(lifecycle: QSTileLifecycle)
@@ -33,9 +40,17 @@
*/
fun onUserIdChanged(userId: Int)
- /** Triggers emit of the new [QSTileState] in [state]. */
+ /** Triggers the emission of the new [QSTileState] in a [state]. */
fun forceUpdate()
/** Notifies underlying logic about user input. */
fun onActionPerformed(userAction: QSTileUserAction)
}
+
+/**
+ * Returns the immediate state of the tile or null if the state haven't been collected yet. Favor
+ * reactive consumption over the [currentState], because there is no guarantee that current value
+ * would be available at any time.
+ */
+val QSTileViewModel.currentState: QSTileState?
+ get() = state.replayCache.lastOrNull()
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index d23beda..4465504 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -104,7 +104,6 @@
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationShadeWindowController;
-import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.StatusBarWindowCallback;
import com.android.systemui.statusbar.policy.CallbackController;
import com.android.systemui.unfold.progress.UnfoldTransitionProgressForwarder;
@@ -117,7 +116,6 @@
import java.util.List;
import java.util.Objects;
import java.util.Optional;
-import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.function.Supplier;
@@ -146,11 +144,11 @@
private final SceneContainerFlags mSceneContainerFlags;
private final Executor mMainExecutor;
private final ShellInterface mShellInterface;
- private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
private final Lazy<ShadeViewController> mShadeViewControllerLazy;
private SysUiState mSysUiState;
private final Handler mHandler;
private final Lazy<NavigationBarController> mNavBarControllerLazy;
+ private final ScreenPinningRequest mScreenPinningRequest;
private final NotificationShadeWindowController mStatusBarWinController;
private final Provider<SceneInteractor> mSceneInteractor;
@@ -185,9 +183,7 @@
@Override
public void startScreenPinning(int taskId) {
verifyCallerAndClearCallingIdentityPostMain("startScreenPinning", () ->
- mCentralSurfacesOptionalLazy.get().ifPresent(
- statusBar -> statusBar.showScreenPinningRequest(taskId,
- false /* allowCancel */)));
+ mScreenPinningRequest.showPrompt(taskId, false /* allowCancel */));
}
@Override
@@ -206,40 +202,38 @@
public void onStatusBarTouchEvent(MotionEvent event) {
verifyCallerAndClearCallingIdentity("onStatusBarTouchEvent", () -> {
// TODO move this logic to message queue
- mCentralSurfacesOptionalLazy.get().ifPresent(centralSurfaces -> {
- if (event.getActionMasked() == ACTION_DOWN) {
- mShadeViewControllerLazy.get().startExpandLatencyTracking();
+ if (event.getActionMasked() == ACTION_DOWN) {
+ mShadeViewControllerLazy.get().startExpandLatencyTracking();
+ }
+ mHandler.post(() -> {
+ int action = event.getActionMasked();
+ if (action == ACTION_DOWN) {
+ mInputFocusTransferStarted = true;
+ mInputFocusTransferStartY = event.getY();
+ mInputFocusTransferStartMillis = event.getEventTime();
+
+ // If scene framework is enabled, set the scene container window to
+ // visible and let the touch "slip" into that window.
+ if (mSceneContainerFlags.isEnabled()) {
+ mSceneInteractor.get().setVisible(true, "swipe down on launcher");
+ } else {
+ mShadeViewControllerLazy.get().startInputFocusTransfer();
+ }
}
- mHandler.post(() -> {
- int action = event.getActionMasked();
- if (action == ACTION_DOWN) {
- mInputFocusTransferStarted = true;
- mInputFocusTransferStartY = event.getY();
- mInputFocusTransferStartMillis = event.getEventTime();
+ if (action == ACTION_UP || action == ACTION_CANCEL) {
+ mInputFocusTransferStarted = false;
- // If scene framework is enabled, set the scene container window to
- // visible and let the touch "slip" into that window.
- if (mSceneContainerFlags.isEnabled()) {
- mSceneInteractor.get().setVisible(true, "swipe down on launcher");
+ if (!mSceneContainerFlags.isEnabled()) {
+ float velocity = (event.getY() - mInputFocusTransferStartY)
+ / (event.getEventTime() - mInputFocusTransferStartMillis);
+ if (action == ACTION_CANCEL) {
+ mShadeViewControllerLazy.get().cancelInputFocusTransfer();
} else {
- centralSurfaces.onInputFocusTransfer(
- mInputFocusTransferStarted, false /* cancel */,
- 0 /* velocity */);
+ mShadeViewControllerLazy.get().finishInputFocusTransfer(velocity);
}
}
- if (action == ACTION_UP || action == ACTION_CANCEL) {
- mInputFocusTransferStarted = false;
-
- if (!mSceneContainerFlags.isEnabled()) {
- float velocity = (event.getY() - mInputFocusTransferStartY)
- / (event.getEventTime() - mInputFocusTransferStartMillis);
- centralSurfaces.onInputFocusTransfer(mInputFocusTransferStarted,
- action == ACTION_CANCEL,
- velocity);
- }
- }
- event.recycle();
- });
+ }
+ event.recycle();
});
});
}
@@ -247,8 +241,7 @@
@Override
public void onStatusBarTrackpadEvent(MotionEvent event) {
verifyCallerAndClearCallingIdentityPostMain("onStatusBarTrackpadEvent", () ->
- mCentralSurfacesOptionalLazy.get().ifPresent(centralSurfaces ->
- centralSurfaces.onStatusBarTrackpadEvent(event)));
+ mShadeViewControllerLazy.get().handleExternalTouch(event));
}
@Override
@@ -493,9 +486,9 @@
notifySystemUiStateFlags(mSysUiState.getFlags());
notifyConnectionChanged();
- if (mLatchForOnUserChanging != null) {
- mLatchForOnUserChanging.countDown();
- mLatchForOnUserChanging = null;
+ if (mDoneUserChanging != null) {
+ mDoneUserChanging.run();
+ mDoneUserChanging = null;
}
}
@@ -551,14 +544,14 @@
}
};
- private CountDownLatch mLatchForOnUserChanging;
+ private Runnable mDoneUserChanging;
private final UserTracker.Callback mUserChangedCallback =
new UserTracker.Callback() {
@Override
public void onUserChanging(int newUser, @NonNull Context userContext,
- CountDownLatch latch) {
+ @NonNull Runnable resultCallback) {
mConnectionBackoffAttempts = 0;
- mLatchForOnUserChanging = latch;
+ mDoneUserChanging = resultCallback;
internalConnectToCurrentUser("User changed");
}
};
@@ -570,8 +563,8 @@
CommandQueue commandQueue,
ShellInterface shellInterface,
Lazy<NavigationBarController> navBarControllerLazy,
- Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
Lazy<ShadeViewController> shadeViewControllerLazy,
+ ScreenPinningRequest screenPinningRequest,
NavigationModeController navModeController,
NotificationShadeWindowController statusBarWinController,
SysUiState sysUiState,
@@ -597,10 +590,10 @@
mSceneContainerFlags = sceneContainerFlags;
mMainExecutor = mainExecutor;
mShellInterface = shellInterface;
- mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
mShadeViewControllerLazy = shadeViewControllerLazy;
mHandler = new Handler();
mNavBarControllerLazy = navBarControllerLazy;
+ mScreenPinningRequest = screenPinningRequest;
mStatusBarWinController = statusBarWinController;
mSceneInteractor = sceneInteractor;
mUserTracker = userTracker;
@@ -771,10 +764,8 @@
public void cleanupAfterDeath() {
if (mInputFocusTransferStarted) {
mHandler.post(() -> {
- mCentralSurfacesOptionalLazy.get().ifPresent(centralSurfaces -> {
- mInputFocusTransferStarted = false;
- centralSurfaces.onInputFocusTransfer(false, true /* cancel */, 0 /* velocity */);
- });
+ mInputFocusTransferStarted = false;
+ mShadeViewControllerLazy.get().cancelInputFocusTransfer();
});
}
startConnectionToCurrentUser();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
index 346acf9..3577c00 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
@@ -53,29 +53,30 @@
import androidx.annotation.NonNull;
+import com.android.systemui.CoreStartable;
import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.navigationbar.NavigationBarView;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shared.system.QuickStepContract;
-import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.util.leak.RotationUtils;
import java.util.ArrayList;
-import java.util.Optional;
import javax.inject.Inject;
import dagger.Lazy;
+@SysUISingleton
public class ScreenPinningRequest implements View.OnClickListener,
- NavigationModeController.ModeChangedListener {
+ NavigationModeController.ModeChangedListener, CoreStartable {
private static final String TAG = "ScreenPinningRequest";
private final Context mContext;
- private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
-
+ private final Lazy<NavigationBarController> mNavigationBarControllerLazy;
private final AccessibilityManager mAccessibilityService;
private final WindowManager mWindowManager;
private final BroadcastDispatcher mBroadcastDispatcher;
@@ -98,12 +99,12 @@
@Inject
public ScreenPinningRequest(
Context context,
- Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
NavigationModeController navigationModeController,
+ Lazy<NavigationBarController> navigationBarControllerLazy,
BroadcastDispatcher broadcastDispatcher,
UserTracker userTracker) {
mContext = context;
- mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
+ mNavigationBarControllerLazy = navigationBarControllerLazy;
mAccessibilityService = (AccessibilityManager)
mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
mWindowManager = (WindowManager)
@@ -113,6 +114,9 @@
mUserTracker = userTracker;
}
+ @Override
+ public void start() {}
+
public void clearPrompt() {
if (mRequestWindow != null) {
mWindowManager.removeView(mRequestWindow);
@@ -144,7 +148,8 @@
mNavBarMode = mode;
}
- public void onConfigurationChanged() {
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
if (mRequestWindow != null) {
mRequestWindow.onConfigurationChanged();
}
@@ -282,15 +287,14 @@
.setVisibility(View.INVISIBLE);
}
- final Optional<CentralSurfaces> centralSurfacesOptional =
- mCentralSurfacesOptionalLazy.get();
- boolean recentsVisible =
- centralSurfacesOptional.map(CentralSurfaces::isOverviewEnabled).orElse(false);
+ int displayId = mContext.getDisplayId();
+ boolean overviewEnabled =
+ mNavigationBarControllerLazy.get().isOverviewEnabled(displayId);
boolean touchExplorationEnabled = mAccessibilityService.isTouchExplorationEnabled();
int descriptionStringResId;
if (QuickStepContract.isGesturalMode(mNavBarMode)) {
descriptionStringResId = R.string.screen_pinning_description_gestural;
- } else if (recentsVisible) {
+ } else if (overviewEnabled) {
mLayout.findViewById(R.id.screen_pinning_recents_group).setVisibility(VISIBLE);
mLayout.findViewById(R.id.screen_pinning_home_bg_light).setVisibility(INVISIBLE);
mLayout.findViewById(R.id.screen_pinning_home_bg).setVisibility(INVISIBLE);
@@ -307,7 +311,7 @@
}
NavigationBarView navigationBarView =
- centralSurfacesOptional.map(CentralSurfaces::getNavigationBarView).orElse(null);
+ mNavigationBarControllerLazy.get().getNavigationBarView(displayId);
if (navigationBarView != null) {
((ImageView) mLayout.findViewById(R.id.screen_pinning_back_icon))
.setImageDrawable(navigationBarView.getBackDrawable());
diff --git a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
index b36ec32..0f3acaf 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
@@ -33,6 +33,7 @@
includes =
[
BouncerSceneModule::class,
+ CommunalSceneModule::class,
EmptySceneModule::class,
GoneSceneModule::class,
LockscreenSceneModule::class,
@@ -65,6 +66,7 @@
sceneKeys =
listOf(
SceneKey.Gone,
+ SceneKey.Communal,
SceneKey.Lockscreen,
SceneKey.Bouncer,
SceneKey.Shade,
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt
index 9a30aa6..3927873 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt
@@ -32,5 +32,16 @@
val fromScene: SceneKey,
val toScene: SceneKey,
val progress: Flow<Float>,
+
+ /**
+ * Whether the transition was originally triggered by user input rather than being
+ * programmatic. If this value is initially true, it will remain true until the transition
+ * fully completes, even if the user input that triggered the transition has ended. Any
+ * sub-transitions launched by this one will inherit this value. For example, if the user
+ * drags a pointer but does not exceed the threshold required to transition to another
+ * scene, this value will remain true after the pointer is no longer touching the screen and
+ * will be true in any transition created to animate back to the original position.
+ */
+ val isUserInputDriven: Boolean,
) : ObservableTransitionState()
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt
index 31597c1..4bc93a8 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt
@@ -16,9 +16,7 @@
package com.android.systemui.scene.shared.model
-import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asStateFlow
/**
* Defines interface for classes that can describe a "scene".
@@ -34,31 +32,26 @@
val key: SceneKey
/**
- * Returns a mapping between [UserAction] and flows that emit a [SceneModel].
+ * The mapping between [UserAction] and destination [SceneModel]s.
*
- * When the scene framework detects the user action, it starts a transition to the scene
- * described by the latest value in the flow that's mapped from that user action.
+ * When the scene framework detects a user action, if the current scene has a map entry for that
+ * user action, the framework starts a transition to the scene in the map.
*
- * Once the [Scene] becomes the current one, the scene framework will invoke this method and set
- * up collectors to watch for new values emitted to each of the flows. If a value is added to
- * the map at a given [UserAction], the framework will set up user input handling for that
- * [UserAction] and, if such a user action is detected, the framework will initiate a transition
- * to that [SceneModel].
+ * Once the [Scene] becomes the current one, the scene framework will read this property and set
+ * up a collector to watch for new mapping values. If every map entry provided by the scene, the
+ * framework will set up user input handling for its [UserAction] and, if such a user action is
+ * detected, initiate a transition to the specified [SceneModel].
*
- * Note that calling this method does _not_ mean that the given user action has occurred.
- * Instead, the method is called before any user action/gesture is detected so that the
- * framework can decide whether to set up gesture/input detectors/listeners for that type of
- * user action.
+ * Note that reading from this method does _not_ mean that any user action has occurred.
+ * Instead, the property is read before any user action/gesture is detected so that the
+ * framework can decide whether to set up gesture/input detectors/listeners in case user actions
+ * of the given types ever occur.
*
* Note that a missing value for a specific [UserAction] means that the user action of the given
* type is not currently active in the scene and should be ignored by the framework, while the
* current scene is this one.
- *
- * The API is designed such that it's possible to emit ever-changing values for each
- * [UserAction] to enable, disable, or change the destination scene of a given user action.
*/
- fun destinationScenes(): StateFlow<Map<UserAction, SceneModel>> =
- MutableStateFlow(emptyMap<UserAction, SceneModel>()).asStateFlow()
+ val destinationScenes: StateFlow<Map<UserAction, SceneModel>>
}
/** Enumerates all scene framework supported user actions. */
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneKey.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneKey.kt
index e7811e3..609d2b9 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneKey.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneKey.kt
@@ -16,7 +16,11 @@
package com.android.systemui.scene.shared.model
-/** Keys of all known scenes. */
+/**
+ * Keys of all known scenes.
+ *
+ * PLEASE KEEP THE KEYS SORTED ALPHABETICALLY.
+ */
sealed class SceneKey(
private val loggingName: String,
) {
@@ -26,6 +30,9 @@
*/
object Bouncer : SceneKey("bouncer")
+ /** The communal scene shows the glanceable hub when device is locked and docked. */
+ object Communal : SceneKey("communal")
+
/**
* "Gone" is not a real scene but rather the absence of scenes when we want to skip showing any
* content from the scene framework.
@@ -35,14 +42,14 @@
/** The lockscreen is the scene that shows when the device is locked. */
object Lockscreen : SceneKey("lockscreen")
+ /** The quick settings scene shows the quick setting tiles. */
+ object QuickSettings : SceneKey("quick_settings")
+
/**
* The shade is the scene whose primary purpose is to show a scrollable list of notifications.
*/
object Shade : SceneKey("shade")
- /** The quick settings scene shows the quick setting tiles. */
- object QuickSettings : SceneKey("quick_settings")
-
override fun toString(): String {
return loggingName
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
index acb6d96..7a0c087 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
@@ -151,10 +151,9 @@
return flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)
? new ScreenRecordPermissionDialog(context, getHostUserHandle(), this,
- activityStarter, dialogLaunchAnimator, mUserContextProvider,
- onStartRecordingClicked)
- : new ScreenRecordDialog(context, this, activityStarter,
- mUserContextProvider, flags, dialogLaunchAnimator, onStartRecordingClicked);
+ activityStarter, mUserContextProvider, onStartRecordingClicked)
+ : new ScreenRecordDialog(context, this, mUserContextProvider,
+ onStartRecordingClicked);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
index 2a21aaa..91e3b60 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
@@ -18,7 +18,6 @@
import static android.app.Activity.RESULT_OK;
-import static com.android.systemui.media.MediaProjectionAppSelectorActivity.EXTRA_CAPTURE_REGION_RESULT_RECEIVER;
import static com.android.systemui.media.MediaProjectionAppSelectorActivity.KEY_CAPTURE_TARGET;
import static com.android.systemui.screenrecord.ScreenRecordingAudioSource.INTERNAL;
import static com.android.systemui.screenrecord.ScreenRecordingAudioSource.MIC;
@@ -28,13 +27,11 @@
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Context;
-import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.ResultReceiver;
import android.view.Gravity;
-import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.ArrayAdapter;
@@ -45,13 +42,7 @@
import androidx.annotation.Nullable;
import com.android.systemui.R;
-import com.android.systemui.animation.ActivityLaunchAnimator;
-import com.android.systemui.animation.DialogLaunchAnimator;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
-import com.android.systemui.media.MediaProjectionAppSelectorActivity;
import com.android.systemui.media.MediaProjectionCaptureTarget;
-import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.statusbar.phone.SystemUIDialog;
@@ -71,23 +62,15 @@
private final UserContextProvider mUserContextProvider;
@Nullable
private final Runnable mOnStartRecordingClicked;
- private final ActivityStarter mActivityStarter;
- private final FeatureFlags mFlags;
- private final DialogLaunchAnimator mDialogLaunchAnimator;
private Switch mTapsSwitch;
private Switch mAudioSwitch;
private Spinner mOptions;
public ScreenRecordDialog(Context context, RecordingController controller,
- ActivityStarter activityStarter, UserContextProvider userContextProvider,
- FeatureFlags flags, DialogLaunchAnimator dialogLaunchAnimator,
- @Nullable Runnable onStartRecordingClicked) {
+ UserContextProvider userContextProvider, @Nullable Runnable onStartRecordingClicked) {
super(context);
mController = controller;
mUserContextProvider = userContextProvider;
- mActivityStarter = activityStarter;
- mDialogLaunchAnimator = dialogLaunchAnimator;
- mFlags = flags;
mOnStartRecordingClicked = onStartRecordingClicked;
}
@@ -120,31 +103,6 @@
dismiss();
});
- if (mFlags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)) {
- TextView appBtn = findViewById(R.id.button_app);
-
- appBtn.setVisibility(View.VISIBLE);
- appBtn.setOnClickListener(v -> {
- Intent intent = new Intent(getContext(), MediaProjectionAppSelectorActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-
- // We can't start activity for result here so we use result receiver to get
- // the selected target to capture
- intent.putExtra(EXTRA_CAPTURE_REGION_RESULT_RECEIVER,
- new CaptureTargetResultReceiver());
-
- ActivityLaunchAnimator.Controller animationController =
- mDialogLaunchAnimator.createActivityLaunchController(appBtn);
-
- if (animationController == null) {
- dismiss();
- }
-
- mActivityStarter.startActivity(intent, /* dismissShade= */ true,
- animationController);
- });
- }
-
mAudioSwitch = findViewById(R.id.screenrecord_audio_switch);
mTapsSwitch = findViewById(R.id.screenrecord_taps_switch);
mOptions = findViewById(R.id.screen_recording_options);
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
index 9c5da10..56d732e 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
@@ -33,7 +33,6 @@
import android.widget.Switch
import androidx.annotation.LayoutRes
import com.android.systemui.R
-import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.media.MediaProjectionAppSelectorActivity
import com.android.systemui.media.MediaProjectionCaptureTarget
import com.android.systemui.plugins.ActivityStarter
@@ -45,7 +44,6 @@
private val hostUserHandle: UserHandle,
private val controller: RecordingController,
private val activityStarter: ActivityStarter,
- private val dialogLaunchAnimator: DialogLaunchAnimator,
private val userContextProvider: UserContextProvider,
private val onStartRecordingClicked: Runnable?
) :
@@ -85,12 +83,7 @@
MediaProjectionAppSelectorActivity.EXTRA_HOST_APP_USER_HANDLE,
hostUserHandle
)
-
- val animationController = dialogLaunchAnimator.createActivityLaunchController(v!!)
- if (animationController == null) {
- dismiss()
- }
- activityStarter.startActivity(intent, /* dismissShade= */ true, animationController)
+ activityStarter.startActivity(intent, /* dismissShade= */ true)
}
dismiss()
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt
index c5bc2fb..03c3f7a 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt
@@ -150,16 +150,18 @@
}
/**
- * Returns a [RequestCallback] that calls [RequestCallback.onFinish] only when all callbacks for
- * id created have finished.
+ * Returns a [RequestCallback] that wraps [originalCallback].
*
- * If any callback created calls [reportError], then following [onFinish] are not considered.
+ * Each [RequestCallback] created with [createCallbackForId] is expected to be used with either
+ * [reportError] or [onFinish]. Once they are both called:
+ * - If any finished with an error, [reportError] of [originalCallback] is called
+ * - Otherwise, [onFinish] is called.
*/
private class MultiResultCallbackWrapper(
private val originalCallback: RequestCallback,
) {
private val idsPending = mutableSetOf<Int>()
- private var errorReported = false
+ private val idsWithErrors = mutableSetOf<Int>()
/**
* Creates a callback for [id].
@@ -172,23 +174,32 @@
idsPending += id
return object : RequestCallback {
override fun reportError() {
- Log.d(TAG, "ReportError id=$id")
- Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP, TAG, id)
- Trace.instantForTrack(Trace.TRACE_TAG_APP, TAG, "reportError id=$id")
- originalCallback.reportError()
- errorReported = true
+ endTrace("reportError id=$id")
+ idsWithErrors += id
+ idsPending -= id
+ reportToOriginalIfNeeded()
}
override fun onFinish() {
- Log.d(TAG, "onFinish id=$id")
- if (errorReported) return
+ endTrace("onFinish id=$id")
idsPending -= id
- Trace.instantForTrack(Trace.TRACE_TAG_APP, TAG, "onFinish id=$id")
- if (idsPending.isEmpty()) {
- Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP, TAG, id)
- originalCallback.onFinish()
- }
+ reportToOriginalIfNeeded()
}
+
+ private fun endTrace(reason: String) {
+ Log.d(TAG, "Finished waiting for id=$id. $reason")
+ Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP, TAG, id)
+ Trace.instantForTrack(Trace.TRACE_TAG_APP, TAG, reason)
+ }
+ }
+ }
+
+ private fun reportToOriginalIfNeeded() {
+ if (idsPending.isNotEmpty()) return
+ if (idsWithErrors.isEmpty()) {
+ originalCallback.onFinish()
+ } else {
+ originalCallback.reportError()
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index 1e8542f..32df5121b 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -99,7 +99,11 @@
/** Informs about coarse grained state of the Controller. */
public interface RequestCallback {
- /** Respond to the current request indicating the screenshot request failed. */
+ /**
+ * Respond to the current request indicating the screenshot request failed.
+ * <p>
+ * After this, the service will be disconnected and all visible UI is removed.
+ */
void reportError();
/** The controller has completed handling this request UI has been removed */
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
index 93a3e90..bd592c9 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
@@ -19,7 +19,6 @@
import android.content.Context
import android.content.pm.UserInfo
import android.os.UserHandle
-import java.util.concurrent.CountDownLatch
import java.util.concurrent.Executor
/**
@@ -68,8 +67,8 @@
interface Callback {
/**
- * Same as {@link onUserChanging(Int, Context, CountDownLatch)} but the latch will be
- * auto-decremented after the completion of this method.
+ * Same as {@link onUserChanging(Int, Context, Runnable)} but the callback will be
+ * called automatically after the completion of this method.
*/
fun onUserChanging(newUser: Int, userContext: Context) {}
@@ -78,12 +77,12 @@
* Override this method to run things while the screen is frozen for the user switch.
* Please use {@link #onUserChanged} if the task doesn't need to push the unfreezing of the
* screen further. Please be aware that code executed in this callback will lengthen the
- * user switch duration. When overriding this method, countDown() MUST be called on the
- * latch once execution is complete.
+ * user switch duration. When overriding this method, resultCallback#run() MUST be called
+ * once the execution is complete.
*/
- fun onUserChanging(newUser: Int, userContext: Context, latch: CountDownLatch) {
+ fun onUserChanging(newUser: Int, userContext: Context, resultCallback: Runnable) {
onUserChanging(newUser, userContext)
- latch.countDown()
+ resultCallback.run()
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
index 5fb3c01..2f87301 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
@@ -33,11 +33,22 @@
import androidx.annotation.WorkerThread
import com.android.systemui.Dumpable
import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.FeatureFlagsClassic
+import com.android.systemui.flags.Flags
import com.android.systemui.util.Assert
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.asCoroutineDispatcher
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.sync.Mutex
import java.io.PrintWriter
import java.lang.ref.WeakReference
import java.util.concurrent.CountDownLatch
import java.util.concurrent.Executor
+import javax.inject.Provider
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
@@ -58,20 +69,26 @@
*/
open class UserTrackerImpl internal constructor(
private val context: Context,
+ private val featureFlagsProvider: Provider<FeatureFlagsClassic>,
private val userManager: UserManager,
private val iActivityManager: IActivityManager,
private val dumpManager: DumpManager,
- private val backgroundHandler: Handler
+ private val appScope: CoroutineScope,
+ private val backgroundContext: CoroutineDispatcher,
+ private val backgroundHandler: Handler,
) : UserTracker, Dumpable, BroadcastReceiver() {
companion object {
private const val TAG = "UserTrackerImpl"
+ private const val USER_CHANGE_THRESHOLD = 5L * 1000 // 5 sec
}
var initialized = false
private set
private val mutex = Any()
+ private val isBackgroundUserSwitchEnabled: Boolean get() =
+ featureFlagsProvider.get().isEnabled(Flags.USER_TRACKER_BACKGROUND_CALLBACKS)
override var userId: Int by SynchronizedDelegate(context.userId)
protected set
@@ -103,6 +120,10 @@
@GuardedBy("callbacks")
private val callbacks: MutableList<DataItem> = ArrayList()
+ private var beforeUserSwitchingJob: Job? = null
+ private var userSwitchingJob: Job? = null
+ private var afterUserSwitchingJob: Job? = null
+
open fun initialize(startingUser: Int) {
if (initialized) {
return
@@ -119,7 +140,7 @@
addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED)
addAction(Intent.ACTION_MANAGED_PROFILE_UNLOCKED)
}
- context.registerReceiverForAllUsers(this, filter, null /* permission */, backgroundHandler)
+ context.registerReceiverForAllUsers(this, filter, null, backgroundHandler)
registerUserSwitchObserver()
@@ -162,16 +183,39 @@
private fun registerUserSwitchObserver() {
iActivityManager.registerUserSwitchObserver(object : UserSwitchObserver() {
override fun onBeforeUserSwitching(newUserId: Int) {
- handleBeforeUserSwitching(newUserId)
+ if (isBackgroundUserSwitchEnabled) {
+ beforeUserSwitchingJob?.cancel()
+ beforeUserSwitchingJob = appScope.launch(backgroundContext) {
+ handleBeforeUserSwitching(newUserId)
+ }
+ } else {
+ handleBeforeUserSwitching(newUserId)
+ }
}
override fun onUserSwitching(newUserId: Int, reply: IRemoteCallback?) {
- handleUserSwitching(newUserId)
- reply?.sendResult(null)
+ if (isBackgroundUserSwitchEnabled) {
+ userSwitchingJob?.cancel()
+ userSwitchingJob = appScope.launch(backgroundContext) {
+ handleUserSwitchingCoroutines(newUserId) {
+ reply?.sendResult(null)
+ }
+ }
+ } else {
+ handleUserSwitching(newUserId)
+ reply?.sendResult(null)
+ }
}
override fun onUserSwitchComplete(newUserId: Int) {
- handleUserSwitchComplete(newUserId)
+ if (isBackgroundUserSwitchEnabled) {
+ afterUserSwitchingJob?.cancel()
+ afterUserSwitchingJob = appScope.launch(backgroundContext) {
+ handleUserSwitchComplete(newUserId)
+ }
+ } else {
+ handleUserSwitchComplete(newUserId)
+ }
}
}, TAG)
}
@@ -195,7 +239,7 @@
val callback = it.callback.get()
if (callback != null) {
it.executor.execute {
- callback.onUserChanging(userId, userContext, latch)
+ callback.onUserChanging(userId, userContext) { latch.countDown() }
}
} else {
latch.countDown()
@@ -205,6 +249,28 @@
}
@WorkerThread
+ protected open suspend fun handleUserSwitchingCoroutines(newUserId: Int, onDone: () -> Unit) =
+ coroutineScope {
+ Assert.isNotMainThread()
+ Log.i(TAG, "Switching to user $newUserId")
+
+ for (callbackDataItem in synchronized(callbacks) { callbacks.toList() }) {
+ val callback: UserTracker.Callback = callbackDataItem.callback.get() ?: continue
+ launch(callbackDataItem.executor.asCoroutineDispatcher()) {
+ val mutex = Mutex(true)
+ val thresholdLogJob = launch(backgroundContext) {
+ delay(USER_CHANGE_THRESHOLD)
+ Log.e(TAG, "Failed to finish $callback in time")
+ }
+ callback.onUserChanging(userId, userContext) { mutex.unlock() }
+ mutex.lock()
+ thresholdLogJob.cancel()
+ }.join()
+ }
+ onDone()
+ }
+
+ @WorkerThread
protected open fun handleUserSwitchComplete(newUserId: Int) {
Assert.isNotMainThread()
Log.i(TAG, "Switched to user $newUserId")
diff --git a/packages/SystemUI/src/com/android/systemui/settings/dagger/MultiUserUtilsModule.java b/packages/SystemUI/src/com/android/systemui/settings/dagger/MultiUserUtilsModule.java
index e9a1dd7..a0dd924 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/dagger/MultiUserUtilsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/dagger/MultiUserUtilsModule.java
@@ -25,8 +25,10 @@
import com.android.systemui.CoreStartable;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Application;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlagsClassic;
import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.settings.DisplayTrackerImpl;
import com.android.systemui.settings.UserContentResolverProvider;
@@ -42,6 +44,11 @@
import dagger.multibindings.ClassKey;
import dagger.multibindings.IntoMap;
+import javax.inject.Provider;
+
+import kotlinx.coroutines.CoroutineDispatcher;
+import kotlinx.coroutines.CoroutineScope;
+
/**
* Dagger Module for classes found within the com.android.systemui.settings package.
*/
@@ -60,14 +67,17 @@
@Provides
static UserTracker provideUserTracker(
Context context,
+ Provider<FeatureFlagsClassic> featureFlagsProvider,
UserManager userManager,
IActivityManager iActivityManager,
DumpManager dumpManager,
+ @Application CoroutineScope appScope,
+ @Background CoroutineDispatcher backgroundDispatcher,
@Background Handler handler
) {
int startingUser = ActivityManager.getCurrentUser();
- UserTrackerImpl tracker = new UserTrackerImpl(context, userManager, iActivityManager,
- dumpManager, handler);
+ UserTrackerImpl tracker = new UserTrackerImpl(context, featureFlagsProvider, userManager,
+ iActivityManager, dumpManager, appScope, backgroundDispatcher, handler);
tracker.initialize(startingUser);
return tracker;
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 15a0972..92ccdb5 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -205,7 +205,6 @@
import com.android.systemui.statusbar.phone.KeyguardBottomAreaViewController;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.KeyguardClockPositionAlgorithm;
-import com.android.systemui.statusbar.phone.KeyguardStatusBarView;
import com.android.systemui.statusbar.phone.KeyguardStatusBarViewController;
import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent;
@@ -381,7 +380,6 @@
private int mMaxAllowedKeyguardNotifications;
private KeyguardQsUserSwitchController mKeyguardQsUserSwitchController;
private KeyguardUserSwitcherController mKeyguardUserSwitcherController;
- private KeyguardStatusBarView mKeyguardStatusBar;
private KeyguardStatusBarViewController mKeyguardStatusBarViewController;
private KeyguardStatusViewController mKeyguardStatusViewController;
private final LockIconViewController mLockIconViewController;
@@ -588,6 +586,8 @@
private boolean mGestureWaitForTouchSlop;
private boolean mIgnoreXTouchSlop;
private boolean mExpandLatencyTracking;
+ private boolean mUseExternalTouch = false;
+
/**
* Whether we're waking up and will play the delayed doze animation in
* {@link NotificationWakeUpCoordinator}. If so, we'll want to keep the clock centered until the
@@ -1035,7 +1035,6 @@
@VisibleForTesting
void onFinishInflate() {
loadDimens();
- mKeyguardStatusBar = mView.findViewById(R.id.keyguard_header);
FrameLayout userAvatarContainer = null;
KeyguardUserSwitcherView keyguardUserSwitcherView = null;
@@ -1053,7 +1052,7 @@
mKeyguardStatusBarViewController =
mKeyguardStatusBarViewComponentFactory.build(
- mKeyguardStatusBar,
+ mView.findViewById(R.id.keyguard_header),
mShadeViewStateProvider)
.getKeyguardStatusBarViewController();
mKeyguardStatusBarViewController.init();
@@ -1226,7 +1225,7 @@
private void updateViewControllers(
FrameLayout userAvatarView,
KeyguardUserSwitcherView keyguardUserSwitcherView) {
- updateStatusBarViewController();
+ updateStatusViewController();
if (mKeyguardUserSwitcherController != null) {
// Try to close the switcher so that callbacks are triggered if necessary.
// Otherwise, NPV can get into a state where some of the views are still hidden
@@ -1257,7 +1256,7 @@
}
/** Updates the StatusBarViewController and updates any that depend on it. */
- public void updateStatusBarViewController() {
+ public void updateStatusViewController() {
// Re-associate the KeyguardStatusViewController
if (mKeyguardStatusViewController != null) {
mKeyguardStatusViewController.onDestroy();
@@ -1407,13 +1406,6 @@
updateViewControllers(userAvatarView, keyguardUserSwitcherView);
- if (mFeatureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW) && !mFeatureFlags.isEnabled(
- Flags.LAZY_INFLATE_KEYGUARD)) {
- attachSplitShadeMediaPlayerContainer(
- mKeyguardViewConfigurator.getKeyguardRootView()
- .findViewById(R.id.status_view_media_container));
- }
-
if (!mFeatureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
// Update keyguard bottom area
int index = mView.indexOfChild(mKeyguardBottomArea);
@@ -1683,6 +1675,10 @@
@ClockSize
private int computeDesiredClockSize() {
+ if (shouldForceSmallClock()) {
+ return SMALL;
+ }
+
if (mSplitShadeEnabled) {
return computeDesiredClockSizeForSplitShade();
}
@@ -1715,6 +1711,13 @@
return LARGE;
}
+ private boolean shouldForceSmallClock() {
+ return mFeatureFlags.isEnabled(Flags.LOCKSCREEN_ENABLE_LANDSCAPE)
+ && !isOnAod()
+ // True on small landscape screens
+ && mResources.getBoolean(R.bool.force_small_clock_on_lockscreen);
+ }
+
private void updateKeyguardStatusViewAlignment(boolean animate) {
boolean shouldBeCentered = shouldKeyguardStatusViewBeCentered();
ConstraintLayout layout;
@@ -1842,6 +1845,9 @@
/** Returns extra space available to show the shelf on lockscreen */
@VisibleForTesting
float getVerticalSpaceForLockscreenShelf() {
+ if (mSplitShadeEnabled) {
+ return 0f;
+ }
final float lockIconPadding = getLockIconPadding();
final float noShelfOverlapBottomPadding =
@@ -2246,7 +2252,10 @@
}
@Override
- public void startWaitingForExpandGesture() {
+ public void startInputFocusTransfer() {
+ if (!mCommandQueue.panelsEnabled()) {
+ return;
+ }
if (!isFullyCollapsed()) {
return;
}
@@ -2256,16 +2265,36 @@
}
@Override
- public void stopWaitingForExpandGesture(boolean cancel, final float velocity) {
+ public void cancelInputFocusTransfer() {
+ if (!mCommandQueue.panelsEnabled()) {
+ return;
+ }
if (mExpectingSynthesizedDown) {
mExpectingSynthesizedDown = false;
- if (cancel) {
- collapse(false /* delayed */, 1.0f /* speedUpFactor */);
- } else {
- // Window never will receive touch events that typically trigger haptic on open.
- maybeVibrateOnOpening(false /* openingWithTouch */);
- fling(velocity > 1f ? 1000f * velocity : 0 /* expand */);
- }
+ collapse(false /* delayed */, 1.0f /* speedUpFactor */);
+ onTrackingStopped(false);
+ }
+ }
+
+ /**
+ * There are two scenarios behind this function call. First, input focus transfer has
+ * successfully happened and this view already received synthetic DOWN event.
+ * (mExpectingSynthesizedDown == false). Do nothing.
+ *
+ * Second, before input focus transfer finished, user may have lifted finger in previous window
+ * and this window never received synthetic DOWN event. (mExpectingSynthesizedDown == true). In
+ * this case, we use the velocity to trigger fling event.
+ */
+ @Override
+ public void finishInputFocusTransfer(final float velocity) {
+ if (!mCommandQueue.panelsEnabled()) {
+ return;
+ }
+ if (mExpectingSynthesizedDown) {
+ mExpectingSynthesizedDown = false;
+ // Window never will receive touch events that typically trigger haptic on open.
+ maybeVibrateOnOpening(false /* openingWithTouch */);
+ fling(velocity > 1f ? 1000f * velocity : 0 /* expand */);
onTrackingStopped(false);
}
}
@@ -2604,6 +2633,7 @@
if (mPanelExpanded != isExpanded) {
mPanelExpanded = isExpanded;
updateSystemUiStateFlags();
+ mShadeRepository.setLegacyExpandedOrAwaitingInputTransfer(mPanelExpanded);
mShadeExpansionStateManager.onShadeExpansionFullyChanged(isExpanded);
if (!isExpanded) {
mQsController.closeQsCustomizer();
@@ -3730,8 +3760,6 @@
expand = true;
mShadeLog.logEndMotionEvent("endMotionEvent: cancel while on keyguard",
forceCancel, expand);
- } else if (mCentralSurfaces.isBouncerShowingOverDream()) {
- expand = false;
} else {
// If we get a cancel, put the shade back to the state it was in when the
// gesture started
@@ -4123,12 +4151,22 @@
/** Sends an external (e.g. Status Bar) intercept touch event to the Shade touch handler. */
boolean handleExternalInterceptTouch(MotionEvent event) {
- return mTouchHandler.onInterceptTouchEvent(event);
+ try {
+ mUseExternalTouch = true;
+ return mTouchHandler.onInterceptTouchEvent(event);
+ } finally {
+ mUseExternalTouch = false;
+ }
}
@Override
public boolean handleExternalTouch(MotionEvent event) {
- return mTouchHandler.onTouchEvent(event);
+ try {
+ mUseExternalTouch = true;
+ return mTouchHandler.onTouchEvent(event);
+ } finally {
+ mUseExternalTouch = false;
+ }
}
@Override
@@ -4715,9 +4753,20 @@
public final class TouchHandler implements View.OnTouchListener, Gefingerpoken {
private long mLastTouchDownTime = -1L;
- /** @see ViewGroup#onInterceptTouchEvent(MotionEvent) */
+ /**
+ * With the shade and lockscreen being separated in the view hierarchy, touch handling now
+ * originates with the parent window through {@link #handleExternalTouch}. This allows for
+ * parity with the legacy hierarchy while not undertaking a massive refactoring of touch
+ * handling.
+ *
+ * @see NotificationShadeWindowViewController#didNotificationPanelInterceptEvent
+ */
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
+ if (mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL) && !mUseExternalTouch) {
+ return false;
+ }
+
mShadeLog.logMotionEvent(event, "NPVC onInterceptTouchEvent");
if (mQsController.disallowTouches()) {
mShadeLog.logMotionEvent(event,
@@ -4870,8 +4919,20 @@
return onTouchEvent(event);
}
+ /**
+ * With the shade and lockscreen being separated in the view hierarchy, touch handling now
+ * originates with the parent window through {@link #handleExternalTouch}. This allows for
+ * parity with the legacy hierarchy while not undertaking a massive refactoring of touch
+ * handling.
+ *
+ * @see NotificationShadeWindowViewController#didNotificationPanelInterceptEvent
+ */
@Override
public boolean onTouchEvent(MotionEvent event) {
+ if (mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL) && !mUseExternalTouch) {
+ return false;
+ }
+
if (event.getAction() == MotionEvent.ACTION_DOWN) {
if (event.getDownTime() == mLastTouchDownTime) {
// An issue can occur when swiping down after unlock, where multiple down
@@ -4891,10 +4952,9 @@
return false;
}
- // Do not allow panel expansion if bouncer is scrimmed or showing over a dream,
+ // Do not allow panel expansion if bouncer is scrimmed,
// otherwise user would be able to pull down QS or expand the shade.
- if (mCentralSurfaces.isBouncerShowingScrimmed()
- || mCentralSurfaces.isBouncerShowingOverDream()) {
+ if (mCentralSurfaces.isBouncerShowingScrimmed()) {
mShadeLog.logMotionEvent(event,
"onTouch: ignore touch, bouncer scrimmed or showing over dream");
return false;
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index 880ba92..96fae14 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -267,6 +267,9 @@
}
mView.setLayoutInsetsController(mNotificationInsetsController);
mView.setInteractionEventHandler(new NotificationShadeWindowView.InteractionEventHandler() {
+ boolean mUseDragDownHelperForTouch = false;
+ boolean mLastInterceptWasDragDownHelper = false;
+
@Override
public Boolean handleDispatchTouchEvent(MotionEvent ev) {
if (mStatusBarViewController == null) { // Fix for b/192490822
@@ -360,10 +363,8 @@
);
// In case we start outside of the view bounds (below the status bar), we need to
- // dispatch
- // the touch manually as the view system can't accommodate for touches outside of
- // the
- // regular view bounds.
+ // dispatch the touch manually as the view system can't accommodate for touches
+ // outside of the regular view bounds.
if (isDown && ev.getY() >= mView.getBottom()) {
mExpandingBelowNotch = true;
expandingBelowNotch = true;
@@ -405,6 +406,15 @@
@Override
public boolean shouldInterceptTouchEvent(MotionEvent ev) {
+ boolean intercepted = shouldInterceptTouchEventInternal(ev);
+ if (intercepted) {
+ mUseDragDownHelperForTouch = mLastInterceptWasDragDownHelper;
+ }
+ return intercepted;
+ }
+
+ private boolean shouldInterceptTouchEventInternal(MotionEvent ev) {
+ mLastInterceptWasDragDownHelper = false;
if (mStatusBarStateController.isDozing() && !mDozeServiceHost.isPulsing()
&& !mDockManager.isDocked()) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
@@ -431,19 +441,36 @@
}
if (mNotificationPanelViewController.isFullyExpanded()
- && mDragDownHelper.isDragDownEnabled()
&& !mService.isBouncerShowing()
&& !mStatusBarStateController.isDozing()) {
- boolean result = mDragDownHelper.onInterceptTouchEvent(ev);
- if (result) {
- if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- mShadeLogger.d("NSWVC: drag down helper intercepted");
+ if (mDragDownHelper.isDragDownEnabled()) {
+ // This handles drag down over lockscreen
+ boolean result = mDragDownHelper.onInterceptTouchEvent(ev);
+ if (mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ if (result) {
+ mLastInterceptWasDragDownHelper = true;
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ mShadeLogger.d("NSWVC: drag down helper intercepted");
+ }
+ } else if (didNotificationPanelInterceptEvent(ev)) {
+ return true;
+ }
+ } else {
+ if (result) {
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ mShadeLogger.d("NSWVC: drag down helper intercepted");
+ }
+ }
+ }
+ return result;
+ } else {
+ // This else handles interactions on the full shade while unlocked
+ if (didNotificationPanelInterceptEvent(ev)) {
+ return true;
}
}
- return result;
- } else {
- return false;
}
+ return false;
}
@Override
@@ -451,7 +478,9 @@
MotionEvent cancellation = MotionEvent.obtain(ev);
cancellation.setAction(MotionEvent.ACTION_CANCEL);
mStackScrollLayout.onInterceptTouchEvent(cancellation);
- mNotificationPanelViewController.handleExternalInterceptTouch(cancellation);
+ if (!mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ mNotificationPanelViewController.handleExternalInterceptTouch(cancellation);
+ }
cancellation.recycle();
}
@@ -461,18 +490,27 @@
if (mStatusBarStateController.isDozing()) {
handled = !mDozeServiceHost.isPulsing();
}
-
if (mStatusBarKeyguardViewManager.onTouch(ev)) {
return true;
}
-
- if (mDragDownHelper.isDragDownEnabled()
- || mDragDownHelper.isDraggingDown()) {
- // we still want to finish our drag down gesture when locking the screen
- return mDragDownHelper.onTouchEvent(ev) || handled;
+ if (mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ if (mLastInterceptWasDragDownHelper && (mDragDownHelper.isDraggingDown())) {
+ // we still want to finish our drag down gesture when locking the screen
+ handled |= mDragDownHelper.onTouchEvent(ev) || handled;
+ }
+ if (!handled && mNotificationPanelViewController.handleExternalTouch(ev)) {
+ return true;
+ }
} else {
- return handled;
+ if (mDragDownHelper.isDragDownEnabled()
+ || mDragDownHelper.isDraggingDown()) {
+ // we still want to finish our drag down gesture when locking the screen
+ return mDragDownHelper.onTouchEvent(ev) || handled;
+ } else {
+ return handled;
+ }
}
+ return handled;
}
@Override
@@ -520,6 +558,20 @@
mDepthController.onPanelExpansionChanged(currentState);
}
+ private boolean didNotificationPanelInterceptEvent(MotionEvent ev) {
+ if (mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ // Since NotificationStackScrollLayout is now a sibling of notification_panel, we need
+ // to also ask NotificationPanelViewController directly, in order to process swipe up
+ // events originating from notifications
+ if (mNotificationPanelViewController.handleExternalInterceptTouch(ev)) {
+ mShadeLogger.d("NSWVC: NPVC intercepted");
+ return true;
+ }
+ }
+
+ return false;
+ }
+
public NotificationShadeWindowView getView() {
return mView;
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
index bdf114e..3873ac4 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
@@ -129,6 +129,9 @@
mView.setStackScroller(notificationStackScrollLayoutController.getView())
mView.setMigratingNSSL(featureFlags.isEnabled(Flags.MIGRATE_NSSL))
+ if (featureFlags.isEnabled(Flags.QS_CONTAINER_GRAPH_OPTIMIZER)){
+ mView.enableGraphOptimization()
+ }
}
public override fun onViewAttached() {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java
index a4e439b..292cf8e 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java
@@ -16,6 +16,8 @@
package com.android.systemui.shade;
+import static androidx.constraintlayout.core.widgets.Optimizer.OPTIMIZATION_GRAPH;
+
import android.app.Fragment;
import android.content.Context;
import android.content.res.Configuration;
@@ -24,7 +26,6 @@
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
-import android.view.ViewGroup.MarginLayoutParams;
import android.view.WindowInsets;
import androidx.annotation.Nullable;
@@ -183,6 +184,10 @@
mIsMigratingNSSL = isMigrating;
}
+ void enableGraphOptimization() {
+ setOptimizationLevel(getOptimizationLevel() | OPTIMIZATION_GRAPH);
+ }
+
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
return TouchLogger.logDispatchTouch("NotificationsQuickSettingsContainer", ev,
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
index ff0d78f8..d653cb4 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
@@ -70,6 +70,7 @@
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.fragments.FragmentHostManager;
import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
import com.android.systemui.media.controls.pipeline.MediaDataManager;
@@ -1776,7 +1777,9 @@
// Dragging down on the lockscreen statusbar should prohibit other interactions
// immediately, otherwise we'll wait on the touchslop. This is to allow
// dragging down to expanded quick settings directly on the lockscreen.
- mPanelView.getParent().requestDisallowInterceptTouchEvent(true);
+ if (!mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ mPanelView.getParent().requestDisallowInterceptTouchEvent(true);
+ }
}
if (mExpansionAnimator != null) {
mInitialHeightOnTouch = mExpansionHeight;
@@ -1819,7 +1822,9 @@
&& Math.abs(h) > Math.abs(x - mInitialTouchX)
&& shouldQuickSettingsIntercept(
mInitialTouchX, mInitialTouchY, h)) {
- mPanelView.getParent().requestDisallowInterceptTouchEvent(true);
+ if (!mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ mPanelView.getParent().requestDisallowInterceptTouchEvent(true);
+ }
mShadeLog.onQsInterceptMoveQsTrackingEnabled(h);
setTracking(true);
traceQsJank(true, false);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
index 9a356ad..19a4ee8 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
@@ -484,11 +484,13 @@
if (largeScreenActive) {
logInstantEvent("Large screen constraints set")
header.setTransition(LARGE_SCREEN_HEADER_TRANSITION_ID)
+ systemIconsHoverContainer.isClickable = true
systemIconsHoverContainer.setOnClickListener { shadeCollapseAction?.run() }
} else {
logInstantEvent("Small screen constraints set")
header.setTransition(HEADER_TRANSITION_ID)
systemIconsHoverContainer.setOnClickListener(null)
+ systemIconsHoverContainer.isClickable = false
}
header.jumpToState(header.startState)
updatePosition()
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt
index b553f0f..6ee6cbf 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt
@@ -43,24 +43,6 @@
/** Cancels the views current animation. */
fun cancelAnimation()
- /** Input focus transfer is about to happen. */
- fun startWaitingForExpandGesture()
-
- /**
- * Called when this view is no longer waiting for input focus transfer.
- *
- * There are two scenarios behind this function call. First, input focus transfer has
- * successfully happened and this view already received synthetic DOWN event.
- * (mExpectingSynthesizedDown == false). Do nothing.
- *
- * Second, before input focus transfer finished, user may have lifted finger in previous window
- * and this window never received synthetic DOWN event. (mExpectingSynthesizedDown == true). In
- * this case, we use the velocity to trigger fling event.
- *
- * @param velocity unit is in px / millis
- */
- fun stopWaitingForExpandGesture(cancel: Boolean, velocity: Float)
-
/** Animates the view from its current alpha to zero then runs the runnable. */
fun fadeOut(startDelayMs: Long, durationMs: Long, endAction: Runnable): ViewPropertyAnimator
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
index b3f6e16..fdc049c 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
@@ -239,10 +239,35 @@
)
fun isFullyExpanded(): Boolean
- /** Sends an external (e.g. Status Bar) touch event to the Shade touch handler. */
+ /**
+ * Sends an external (e.g. Status Bar) touch event to the Shade touch handler.
+ *
+ * This is different from [startInputFocusTransfer] as it doesn't rely on setting the launcher
+ * window slippery to allow the frameworks to route those events after passing the initial
+ * threshold.
+ */
fun handleExternalTouch(event: MotionEvent): Boolean
/**
+ * Triggered when an input focus transfer gesture has started.
+ *
+ * Used to dispatch initial touch events before crossing the threshold to pull down the
+ * notification shade. After that, since the launcher window is set to slippery, input
+ * frameworks take care of routing the events to the notification shade.
+ */
+ fun startInputFocusTransfer()
+
+ /** Triggered when the input focus transfer was cancelled. */
+ fun cancelInputFocusTransfer()
+
+ /**
+ * Triggered when the input focus transfer has finished successfully.
+ *
+ * @param velocity unit is in px / millis
+ */
+ fun finishInputFocusTransfer(velocity: Float)
+
+ /**
* Performs haptic feedback from a view with a haptic feedback constant.
*
* The implementation of this method should use the [android.view.View.performHapticFeedback]
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt
index b8a4101..2ed62dd 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt
@@ -85,6 +85,9 @@
override fun handleExternalTouch(event: MotionEvent): Boolean {
return false
}
+ override fun startInputFocusTransfer() {}
+ override fun cancelInputFocusTransfer() {}
+ override fun finishInputFocusTransfer(velocity: Float) {}
override fun performHapticFeedback(constant: Int) {}
override val shadeHeadsUpTracker = ShadeHeadsUpTrackerEmptyImpl()
diff --git a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
index 52a99af..024c8e3 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
@@ -74,6 +74,21 @@
*/
@Deprecated("Use ShadeInteractor instead") val legacyQsTracking: StateFlow<Boolean>
+ /**
+ * NotificationPanelViewController.mPanelExpanded as a flow. This value is true whenever the
+ * expansion fraction is greater than zero or NPVC is about to accept an input transfer from the
+ * status bar, home screen, or trackpad.
+ */
+ @Deprecated("Use ShadeInteractor instead")
+ val legacyExpandedOrAwaitingInputTransfer: StateFlow<Boolean>
+
+ /**
+ * Sets whether the expansion fraction is greater than zero or NPVC is about to accept an input
+ * transfer from the status bar, home screen, or trackpad.
+ */
+ @Deprecated("Use ShadeInteractor instead")
+ fun setLegacyExpandedOrAwaitingInputTransfer(legacyExpandedOrAwaitingInputTransfer: Boolean)
+
/** Sets whether the user is moving Quick Settings with a pointer */
fun setLegacyQsTracking(legacyQsTracking: Boolean)
@@ -155,6 +170,18 @@
@Deprecated("Use ShadeInteractor instead")
override val legacyQsTracking: StateFlow<Boolean> = _legacyQsTracking.asStateFlow()
+ private val _legacyExpandedOrAwaitingInputTransfer = MutableStateFlow(false)
+ @Deprecated("Use ShadeInteractor instead")
+ override val legacyExpandedOrAwaitingInputTransfer: StateFlow<Boolean> =
+ _legacyExpandedOrAwaitingInputTransfer.asStateFlow()
+
+ @Deprecated("Use ShadeInteractor instead")
+ override fun setLegacyExpandedOrAwaitingInputTransfer(
+ legacyExpandedOrAwaitingInputTransfer: Boolean
+ ) {
+ _legacyExpandedOrAwaitingInputTransfer.value = legacyExpandedOrAwaitingInputTransfer
+ }
+
@Deprecated("Should only be called by NPVC and tests")
override fun setLegacyQsTracking(legacyQsTracking: Boolean) {
_legacyQsTracking.value = legacyQsTracking
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
index 95a072c..251cc16 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
@@ -35,15 +35,19 @@
import javax.inject.Provider
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.currentCoroutineContext
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.isActive
/** Business logic for shade interactions. */
@OptIn(ExperimentalCoroutinesApi::class)
@@ -54,6 +58,7 @@
@Application scope: CoroutineScope,
disableFlagsRepository: DisableFlagsRepository,
sceneContainerFlags: SceneContainerFlags,
+ // TODO(b/300258424) convert to direct reference instead of provider
sceneInteractorProvider: Provider<SceneInteractor>,
keyguardRepository: KeyguardRepository,
userSetupRepository: UserSetupRepository,
@@ -72,7 +77,7 @@
* Whether split shade, the combined notifications and quick settings shade used for large
* screens, is enabled.
*/
- val splitShadeEnabled: Flow<Boolean> =
+ val isSplitShadeEnabled: Flow<Boolean> =
sharedNotificationContainerInteractor.configurationBasedDimensions
.map { dimens -> dimens.useSplitShade }
.distinctUntilChanged()
@@ -87,7 +92,7 @@
keyguardRepository.statusBarState,
repository.legacyShadeExpansion,
repository.qsExpansion,
- splitShadeEnabled
+ isSplitShadeEnabled
) {
lockscreenShadeExpansion,
statusBarState,
@@ -119,18 +124,66 @@
repository.qsExpansion
}
- /** The amount [0-1] either QS or the shade has been opened */
+ /** The amount [0-1] either QS or the shade has been opened. */
val anyExpansion: StateFlow<Float> =
combine(shadeExpansion, qsExpansion) { shadeExp, qsExp -> maxOf(shadeExp, qsExp) }
.stateIn(scope, SharingStarted.Eagerly, 0f)
/** Whether either the shade or QS is expanding from a fully collapsed state. */
- val anyExpanding =
+ val isAnyExpanding: Flow<Boolean> =
anyExpansion
.pairwise(1f)
.map { (prev, curr) -> curr > 0f && curr < 1f && prev < 1f }
.distinctUntilChanged()
+ /**
+ * Whether either the shade or QS is partially or fully expanded, i.e. not fully collapsed. At
+ * this time, this is not simply a matter of checking if either value in shadeExpansion and
+ * qsExpansion is greater than zero, because it includes the legacy concept of whether input
+ * transfer is about to occur. If the scene container flag is enabled, it just checks whether
+ * either expansion value is positive.
+ *
+ * TODO(b/300258424) remove all but the first sentence of this comment
+ */
+ val isAnyExpanded: Flow<Boolean> =
+ if (sceneContainerFlags.isEnabled()) {
+ anyExpansion.map { it > 0f }.distinctUntilChanged()
+ } else {
+ repository.legacyExpandedOrAwaitingInputTransfer
+ }
+
+ /**
+ * Whether the user is expanding or collapsing the shade with user input. This will be true even
+ * if the user's input gesture has ended but a transition they initiated is animating.
+ */
+ val isUserInteractingWithShade: Flow<Boolean> =
+ if (sceneContainerFlags.isEnabled()) {
+ sceneBasedInteracting(sceneInteractorProvider.get(), SceneKey.Shade)
+ } else {
+ userInteractingFlow(repository.legacyShadeTracking, repository.legacyShadeExpansion)
+ }
+
+ /**
+ * Whether the user is expanding or collapsing quick settings with user input. This will be true
+ * even if the user's input gesture has ended but a transition they initiated is still
+ * animating.
+ */
+ val isUserInteractingWithQs: Flow<Boolean> =
+ if (sceneContainerFlags.isEnabled()) {
+ sceneBasedInteracting(sceneInteractorProvider.get(), SceneKey.QuickSettings)
+ } else {
+ userInteractingFlow(repository.legacyQsTracking, repository.qsExpansion)
+ }
+
+ /**
+ * Whether the user is expanding or collapsing either the shade or quick settings with user
+ * input (i.e. dragging a pointer). This will be true even if the user's input gesture had ended
+ * but a transition they initiated is still animating.
+ */
+ val isUserInteracting: Flow<Boolean> =
+ combine(isUserInteractingWithShade, isUserInteractingWithShade) { shade, qs -> shade || qs }
+ .distinctUntilChanged()
+
/** Emits true if the shade can be expanded from QQS to QS and false otherwise. */
val isExpandToQsEnabled: Flow<Boolean> =
combine(
@@ -169,4 +222,42 @@
}
}
.distinctUntilChanged()
+
+ fun sceneBasedInteracting(sceneInteractor: SceneInteractor, sceneKey: SceneKey) =
+ sceneInteractor.transitionState
+ .map { state ->
+ when (state) {
+ is ObservableTransitionState.Idle -> false
+ is ObservableTransitionState.Transition ->
+ state.isUserInputDriven &&
+ (state.toScene == sceneKey || state.fromScene == sceneKey)
+ }
+ }
+ .distinctUntilChanged()
+
+ /**
+ * Return a flow for whether a user is interacting with an expandable shade component using
+ * tracking and expansion flows. NOTE: expansion must be a `StateFlow` to guarantee that
+ * [expansion.first] checks the current value of the flow.
+ */
+ private fun userInteractingFlow(
+ tracking: Flow<Boolean>,
+ expansion: StateFlow<Float>
+ ): Flow<Boolean> {
+ return flow {
+ // initial value is false
+ emit(false)
+ while (currentCoroutineContext().isActive) {
+ // wait for tracking to become true
+ tracking.first { it }
+ emit(true)
+ // wait for tracking to become false
+ tracking.first { !it }
+ // wait for expansion to complete in either direction
+ expansion.first { it <= 0f || it >= 1f }
+ // interaction complete
+ emit(false)
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 6304c1e..670fb12 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -375,15 +375,8 @@
/**
* @see IStatusBar#showTransient(int, int, boolean).
*/
- default void showTransient(int displayId, @InsetsType int types) { }
-
- /**
- * @see IStatusBar#showTransient(int, int, boolean).
- */
default void showTransient(int displayId, @InsetsType int types,
- boolean isGestureOnSystemBar) {
- showTransient(displayId, types);
- }
+ boolean isGestureOnSystemBar) {}
/**
* @see IStatusBar#abortTransient(int, int).
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index 6dd24ea..99297b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -16,8 +16,6 @@
package com.android.systemui.statusbar;
-import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
-
import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_TRANSITION_FROM_AOD;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_TRANSITION_TO_AOD;
@@ -31,13 +29,7 @@
import android.util.FloatProperty;
import android.util.Log;
import android.view.Choreographer;
-import android.view.InsetsFlags;
import android.view.View;
-import android.view.ViewDebug;
-import android.view.WindowInsets;
-import android.view.WindowInsets.Type.InsetsType;
-import android.view.WindowInsetsController.Appearance;
-import android.view.WindowInsetsController.Behavior;
import android.view.animation.Interpolator;
import androidx.annotation.NonNull;
@@ -554,34 +546,6 @@
}
@Override
- public void setSystemBarAttributes(@Appearance int appearance, @Behavior int behavior,
- @InsetsType int requestedVisibleTypes, String packageName) {
- boolean isFullscreen = (requestedVisibleTypes & WindowInsets.Type.statusBars()) == 0
- || (requestedVisibleTypes & WindowInsets.Type.navigationBars()) == 0;
- if (mIsFullscreen != isFullscreen) {
- mIsFullscreen = isFullscreen;
- synchronized (mListeners) {
- for (RankedListener rl : new ArrayList<>(mListeners)) {
- rl.mListener.onFullscreenStateChanged(isFullscreen);
- }
- }
- }
-
- // TODO (b/190543382): Finish the logging logic.
- // This section can be removed if we don't need to print it on logcat.
- if (DEBUG_IMMERSIVE_APPS) {
- boolean dim = (appearance & APPEARANCE_LOW_PROFILE_BARS) != 0;
- String behaviorName = ViewDebug.flagsToString(InsetsFlags.class, "behavior", behavior);
- String requestedVisibleTypesString = WindowInsets.Type.toString(requestedVisibleTypes);
- if (requestedVisibleTypesString.isEmpty()) {
- requestedVisibleTypesString = "none";
- }
- Log.d(TAG, packageName + " dim=" + dim + " behavior=" + behaviorName
- + " requested visible types: " + requestedVisibleTypesString);
- }
- }
-
- @Override
public void setPulsing(boolean pulsing) {
if (mPulsing != pulsing) {
mPulsing = pulsing;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
index 4043fce..aa32d5c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
@@ -20,9 +20,6 @@
import android.annotation.IntDef;
import android.view.View;
-import android.view.WindowInsets.Type.InsetsType;
-import android.view.WindowInsetsController.Appearance;
-import android.view.WindowInsetsController.Behavior;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.phone.CentralSurfaces;
@@ -151,12 +148,6 @@
boolean isKeyguardRequested();
/**
- * Set the system bar attributes
- */
- void setSystemBarAttributes(@Appearance int appearance, @Behavior int behavior,
- @InsetsType int requestedVisibleTypes, String packageName);
-
- /**
* Set pulsing
*/
void setPulsing(boolean visibility);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
index 3dfe068..d058d04 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.dagger;
-import android.app.IActivityManager;
import android.app.WallpaperManager;
import android.content.Context;
import android.hardware.display.DisplayManager;
@@ -38,7 +37,6 @@
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.media.controls.pipeline.MediaDataManager;
-import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.settings.DisplayTracker;
@@ -57,12 +55,10 @@
import com.android.systemui.statusbar.StatusBarStateControllerImpl;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.commandline.CommandRegistry;
-import com.android.systemui.statusbar.gesture.SwipeStatusBarAwayGestureHandler;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.RemoteInputControllerLogger;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
-import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.phone.CentralSurfacesImpl;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
@@ -73,23 +69,15 @@
import com.android.systemui.statusbar.phone.StatusBarIconList;
import com.android.systemui.statusbar.phone.StatusBarNotificationPresenterModule;
import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback;
-import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
-import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallFlags;
-import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallLogger;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.RemoteInputUriController;
-import com.android.systemui.statusbar.window.StatusBarWindowController;
import com.android.systemui.util.concurrency.DelayableExecutor;
-import com.android.systemui.util.time.SystemClock;
import dagger.Binds;
import dagger.Lazy;
import dagger.Module;
import dagger.Provides;
-import java.util.Optional;
-import java.util.concurrent.Executor;
-
/**
* This module provides instances needed to construct {@link CentralSurfacesImpl}. These are moved to
* this separate from {@link CentralSurfacesModule} module so that components that wish to build
@@ -230,51 +218,6 @@
}
/**
- */
- @Provides
- @SysUISingleton
- static OngoingCallController provideOngoingCallController(
- Context context,
- CommonNotifCollection notifCollection,
- SystemClock systemClock,
- ActivityStarter activityStarter,
- @Main Executor mainExecutor,
- IActivityManager iActivityManager,
- OngoingCallLogger logger,
- DumpManager dumpManager,
- StatusBarWindowController statusBarWindowController,
- SwipeStatusBarAwayGestureHandler swipeStatusBarAwayGestureHandler,
- StatusBarStateController statusBarStateController,
- OngoingCallFlags ongoingCallFlags) {
-
- boolean ongoingCallInImmersiveEnabled = ongoingCallFlags.isInImmersiveEnabled();
- Optional<StatusBarWindowController> windowController =
- ongoingCallInImmersiveEnabled
- ? Optional.of(statusBarWindowController)
- : Optional.empty();
- Optional<SwipeStatusBarAwayGestureHandler> gestureHandler =
- ongoingCallInImmersiveEnabled
- ? Optional.of(swipeStatusBarAwayGestureHandler)
- : Optional.empty();
- OngoingCallController ongoingCallController =
- new OngoingCallController(
- context,
- notifCollection,
- ongoingCallFlags,
- systemClock,
- activityStarter,
- mainExecutor,
- iActivityManager,
- logger,
- dumpManager,
- windowController,
- gestureHandler,
- statusBarStateController);
- ongoingCallController.init();
- return ongoingCallController;
- }
-
- /**
* {@link NotificationPanelViewController} implements two interfaces:
* - {@link com.android.systemui.shade.ShadeViewController}, which can be used by any class
* needing access to the shade.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
new file mode 100644
index 0000000..d1464ed
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
@@ -0,0 +1,52 @@
+/*
+ * 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.systemui.statusbar.dagger
+
+import com.android.systemui.CoreStartable
+import com.android.systemui.statusbar.data.repository.StatusBarModeRepository
+import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryImpl
+import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
+
+/**
+ * A module for **only** classes related to the status bar **UI element**. This module specifically
+ * should **not** include:
+ * - Classes in the `statusbar` package that are unrelated to the status bar UI.
+ * - Status bar classes that are already provided by other modules
+ * ([com.android.systemui.statusbar.pipeline.dagger.StatusBarPipelineModule],
+ * [com.android.systemui.statusbar.policy.dagger.StatusBarPolicyModule], etc.).
+ */
+@Module
+abstract class StatusBarModule {
+ @Binds
+ abstract fun bindStatusBarModeRepository(
+ impl: StatusBarModeRepositoryImpl
+ ): StatusBarModeRepository
+
+ @Binds
+ @IntoMap
+ @ClassKey(StatusBarModeRepositoryImpl::class)
+ abstract fun bindStatusBarModeRepositoryStart(impl: StatusBarModeRepositoryImpl): CoreStartable
+
+ @Binds
+ @IntoMap
+ @ClassKey(OngoingCallController::class)
+ abstract fun bindOngoingCallController(impl: OngoingCallController): CoreStartable
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepository.kt
new file mode 100644
index 0000000..9d73071
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepository.kt
@@ -0,0 +1,166 @@
+/*
+ * 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.systemui.statusbar.data.repository
+
+import android.view.WindowInsets
+import android.view.WindowInsetsController
+import com.android.internal.statusbar.LetterboxDetails
+import com.android.internal.view.AppearanceRegion
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.DisplayId
+import com.android.systemui.statusbar.CommandQueue
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+
+/**
+ * A repository for the current mode of the status bar on the homescreen (translucent, transparent,
+ * opaque, lights out, hidden, etc.).
+ *
+ * Note: These status bar modes are status bar *window* states that are sent to us from
+ * WindowManager, not determined internally.
+ */
+interface StatusBarModeRepository {
+ /**
+ * True if the status bar window is showing transiently and will disappear soon, and false
+ * otherwise. ("Otherwise" in this case means the status bar is persistently hidden OR
+ * persistently shown.)
+ *
+ * This behavior is controlled by WindowManager via
+ * [android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE], *not* calculated
+ * internally. SysUI merely obeys the behavior sent to us.
+ */
+ val isTransientShown: StateFlow<Boolean>
+
+ /**
+ * True the focused window is fullscreen (aka immersive) and false otherwise.
+ *
+ * Typically, the only time the status bar window is hidden is when the focused window is
+ * fullscreen.
+ */
+ val isInFullscreenMode: StateFlow<Boolean>
+
+ /**
+ * Requests for the status bar to be shown transiently.
+ *
+ * TODO(b/277764509): Don't allow [CentralSurfaces] to set the transient mode; have it
+ * determined internally instead.
+ */
+ fun showTransient()
+
+ /**
+ * Requests for the status bar to be no longer showing transiently.
+ *
+ * TODO(b/277764509): Don't allow [CentralSurfaces] to set the transient mode; have it
+ * determined internally instead.
+ */
+ fun clearTransient()
+}
+
+@SysUISingleton
+class StatusBarModeRepositoryImpl
+@Inject
+constructor(
+ @Application scope: CoroutineScope,
+ @DisplayId thisDisplayId: Int,
+ private val commandQueue: CommandQueue,
+) : StatusBarModeRepository, CoreStartable {
+
+ private val commandQueueCallback =
+ object : CommandQueue.Callbacks {
+ override fun showTransient(
+ displayId: Int,
+ @WindowInsets.Type.InsetsType types: Int,
+ isGestureOnSystemBar: Boolean,
+ ) {
+ if (isTransientRelevant(displayId, types)) {
+ _isTransientShown.value = true
+ }
+ }
+
+ override fun abortTransient(displayId: Int, @WindowInsets.Type.InsetsType types: Int) {
+ if (isTransientRelevant(displayId, types)) {
+ _isTransientShown.value = false
+ }
+ }
+
+ private fun isTransientRelevant(
+ displayId: Int,
+ @WindowInsets.Type.InsetsType types: Int,
+ ): Boolean {
+ return displayId == thisDisplayId && (types and WindowInsets.Type.statusBars() != 0)
+ }
+
+ override fun onSystemBarAttributesChanged(
+ displayId: Int,
+ @WindowInsetsController.Appearance appearance: Int,
+ appearanceRegions: Array<AppearanceRegion>,
+ navbarColorManagedByIme: Boolean,
+ @WindowInsetsController.Behavior behavior: Int,
+ @WindowInsets.Type.InsetsType requestedVisibleTypes: Int,
+ packageName: String,
+ letterboxDetails: Array<LetterboxDetails>,
+ ) {
+ if (displayId != thisDisplayId) return
+ _originalStatusBarAttributes.value =
+ StatusBarAttributes(
+ requestedVisibleTypes,
+ )
+ }
+ }
+
+ override fun start() {
+ commandQueue.addCallback(commandQueueCallback)
+ }
+
+ private val _isTransientShown = MutableStateFlow(false)
+ override val isTransientShown: StateFlow<Boolean> = _isTransientShown.asStateFlow()
+
+ private val _originalStatusBarAttributes = MutableStateFlow<StatusBarAttributes?>(null)
+
+ override val isInFullscreenMode: StateFlow<Boolean> =
+ _originalStatusBarAttributes
+ .map { params ->
+ val requestedVisibleTypes = params?.requestedVisibleTypes ?: return@map false
+ // When the status bar is not requested visible, we assume we're in fullscreen mode.
+ requestedVisibleTypes and WindowInsets.Type.statusBars() == 0
+ }
+ .stateIn(scope, SharingStarted.Eagerly, false)
+
+ override fun showTransient() {
+ _isTransientShown.value = true
+ }
+
+ override fun clearTransient() {
+ _isTransientShown.value = false
+ }
+
+ /**
+ * Internal class keeping track of the raw status bar attributes received from the callback.
+ * Should never be exposed.
+ */
+ private data class StatusBarAttributes(
+ @WindowInsets.Type.InsetsType val requestedVisibleTypes: Int,
+ )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
index e206141..70ccc4f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
@@ -31,6 +31,7 @@
import com.android.internal.util.NotificationMessagingUtil;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -38,6 +39,7 @@
import com.android.systemui.statusbar.notification.NotificationClicker;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.icon.IconManager;
+import com.android.systemui.statusbar.notification.row.BigPictureIconManager;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController;
import com.android.systemui.statusbar.notification.row.NotifBindPipeline;
@@ -151,6 +153,7 @@
component.getExpandableNotificationRowController();
rowController.init(entry);
entry.setRowController(rowController);
+ maybeSetBigPictureIconManager(row, component);
bindRow(entry, row);
updateRow(entry, row);
inflateContentViews(entry, params, row, callback);
@@ -165,6 +168,7 @@
return;
}
mLogger.logReleasingViews(entry);
+ cancelRunningJobs(entry.getRow());
final RowContentBindParams params = mRowContentBindStage.getStageParams(entry);
params.markContentViewsFreeable(FLAG_CONTENT_VIEW_CONTRACTED);
params.markContentViewsFreeable(FLAG_CONTENT_VIEW_EXPANDED);
@@ -172,6 +176,23 @@
mRowContentBindStage.requestRebind(entry, null);
}
+ private void maybeSetBigPictureIconManager(ExpandableNotificationRow row,
+ ExpandableNotificationRowComponent component) {
+ if (mFeatureFlags.isEnabled(Flags.BIGPICTURE_NOTIFICATION_LAZY_LOADING)) {
+ row.setBigPictureIconManager(component.getBigPictureIconManager());
+ }
+ }
+
+ private void cancelRunningJobs(ExpandableNotificationRow row) {
+ if (row == null) {
+ return;
+ }
+ BigPictureIconManager iconManager = row.getBigPictureIconManager();
+ if (iconManager != null) {
+ iconManager.cancelJobs();
+ }
+ }
+
/**
* Bind row to various controllers and managers. This is only called when the row is first
* created.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 1b790fd..cb21291 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -188,12 +188,6 @@
return super.onInterceptTouchEvent(ev);
}
- /**
- * Called by the TouchHandler when this view is tapped. This will be called for actual taps
- * only, i.e. taps that have been filtered by the FalsingManager.
- */
- public void onTap() {}
-
/** Sets the last action up time this view was touched. */
public void setLastActionUpTime(long eventTime) {
mLastActionUpTime = eventTime;
@@ -227,10 +221,6 @@
mBackgroundNormal.setState(getDrawableState());
}
- void setRippleAllowed(boolean allowed) {
- mBackgroundNormal.setPressedAllowed(allowed);
- }
-
private void updateOutlineAlpha() {
float alpha = NotificationStackScrollLayout.BACKGROUND_ALPHA_DIMMED;
alpha = (alpha + (1.0f - alpha) * mNormalBackgroundVisibilityAmount);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java
index 028cd18..ded5ee4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java
@@ -80,11 +80,7 @@
if (ev.getAction() == MotionEvent.ACTION_UP) {
// If this is a false tap, capture the even so it doesn't result in a click.
- boolean falseTap = mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY);
- if (!falseTap && v instanceof ActivatableNotificationView) {
- ((ActivatableNotificationView) v).onTap();
- }
- return falseTap;
+ return mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY);
}
return result;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BigPictureIconManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BigPictureIconManager.kt
new file mode 100644
index 0000000..88dbb4c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BigPictureIconManager.kt
@@ -0,0 +1,298 @@
+/*
+ * 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.systemui.statusbar.notification.row
+
+import android.annotation.WorkerThread
+import android.app.ActivityManager
+import android.content.Context
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.Icon
+import android.util.Dumpable
+import android.util.Log
+import android.util.Size
+import com.android.internal.R
+import com.android.internal.widget.NotificationDrawableConsumer
+import com.android.internal.widget.NotificationIconManager
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.graphics.ImageLoader
+import com.android.systemui.statusbar.notification.row.BigPictureIconManager.DrawableState.Empty
+import com.android.systemui.statusbar.notification.row.BigPictureIconManager.DrawableState.FullImage
+import com.android.systemui.statusbar.notification.row.BigPictureIconManager.DrawableState.PlaceHolder
+import java.io.PrintWriter
+import javax.inject.Inject
+import kotlin.math.min
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+private const val TAG = "BigPicImageLoader"
+private const val DEBUG = false
+private const val FREE_IMAGE_DELAY_MS = 3000L
+
+/**
+ * A helper class for [com.android.internal.widget.BigPictureNotificationImageView] to lazy-load
+ * images from SysUI. It replaces the placeholder image with the fully loaded one, and vica versa.
+ *
+ * TODO(b/283082473) move the logs to a [com.android.systemui.log.LogBuffer]
+ */
+@SuppressWarnings("DumpableNotRegistered")
+class BigPictureIconManager
+@Inject
+constructor(
+ private val context: Context,
+ private val imageLoader: ImageLoader,
+ @Application private val scope: CoroutineScope,
+ @Main private val mainDispatcher: CoroutineDispatcher,
+ @Background private val bgDispatcher: CoroutineDispatcher
+) : NotificationIconManager, Dumpable {
+
+ private var lastLoadingJob: Job? = null
+ private var drawableConsumer: NotificationDrawableConsumer? = null
+ private var displayedState: DrawableState = Empty(null)
+ private var viewShown = false
+
+ private var maxWidth = getMaxWidth()
+ private var maxHeight = getMaxHeight()
+
+ /**
+ * Called when the displayed state changes of the view.
+ *
+ * @param shown true if the view is shown, and the image needs to be displayed.
+ */
+ fun onViewShown(shown: Boolean) {
+ log("onViewShown:$shown")
+
+ if (this.viewShown != shown) {
+ this.viewShown = shown
+
+ val state = displayedState
+
+ this.lastLoadingJob?.cancel()
+ this.lastLoadingJob =
+ when {
+ state is Empty && shown -> state.icon?.let(::startLoadingJob)
+ state is PlaceHolder && shown -> startLoadingJob(state.icon)
+ state is FullImage && !shown ->
+ startFreeImageJob(state.icon, state.drawableSize)
+ else -> null
+ }
+ }
+ }
+
+ /**
+ * Update the maximum width and height allowed for bitmaps, ex. after a configuration change.
+ */
+ fun updateMaxImageSizes() {
+ log("updateMaxImageSizes")
+ maxWidth = getMaxWidth()
+ maxHeight = getMaxHeight()
+ }
+
+ /** Cancels all currently running jobs. */
+ fun cancelJobs() {
+ lastLoadingJob?.cancel()
+ }
+
+ @WorkerThread
+ override fun updateIcon(drawableConsumer: NotificationDrawableConsumer, icon: Icon?): Runnable {
+ if (this.drawableConsumer != null && this.drawableConsumer != drawableConsumer) {
+ Log.wtf(TAG, "A consumer is already set for this iconManager.")
+ return Runnable {}
+ }
+
+ if (displayedState.iconSameAs(icon)) {
+ // We're already handling this icon, nothing to do here.
+ log("skipping updateIcon for consumer:$drawableConsumer with icon:$icon")
+ return Runnable {}
+ }
+
+ this.drawableConsumer = drawableConsumer
+ this.displayedState = Empty(icon)
+ this.lastLoadingJob?.cancel()
+
+ val drawable = loadImageOrPlaceHolderSync(icon)
+
+ log("icon updated")
+
+ return Runnable { drawableConsumer.setImageDrawable(drawable) }
+ }
+
+ override fun dump(pw: PrintWriter, args: Array<out String>?) {
+ pw.println("BigPictureIconManager ${getDebugString()}")
+ }
+
+ @WorkerThread
+ private fun loadImageOrPlaceHolderSync(icon: Icon?): Drawable? {
+ icon ?: return null
+
+ if (viewShown) {
+ return loadImageSync(icon)
+ }
+
+ return loadPlaceHolderSync(icon) ?: loadImageSync(icon)
+ }
+
+ @WorkerThread
+ private fun loadImageSync(icon: Icon): Drawable? {
+ return imageLoader.loadDrawableSync(icon, context, maxWidth, maxHeight)?.also { drawable ->
+ checkPlaceHolderSizeForDrawable(this.displayedState, drawable)
+ this.displayedState = FullImage(icon, drawable.intrinsicSize)
+ }
+ }
+
+ private fun checkPlaceHolderSizeForDrawable(
+ displayedState: DrawableState,
+ newDrawable: Drawable
+ ) {
+ if (displayedState is PlaceHolder) {
+ val (oldWidth, oldHeight) = displayedState.drawableSize
+ val (newWidth, newHeight) = newDrawable.intrinsicSize
+
+ if (oldWidth != newWidth || oldHeight != newHeight) {
+ Log.e(
+ TAG,
+ "Mismatch in dimensions, when replacing PlaceHolder " +
+ "$oldWidth X $oldHeight with Drawable $newWidth X $newHeight."
+ )
+ }
+ }
+ }
+
+ @WorkerThread
+ private fun loadPlaceHolderSync(icon: Icon): Drawable? {
+ return imageLoader
+ .loadSizeSync(icon, context)
+ ?.resizeToMax(maxWidth, maxHeight) // match the dimensions of the fully loaded drawable
+ ?.let { size -> createPlaceHolder(size) }
+ ?.also { drawable -> this.displayedState = PlaceHolder(icon, drawable.intrinsicSize) }
+ }
+
+ private fun startLoadingJob(icon: Icon): Job =
+ scope.launch {
+ val drawable = withContext(bgDispatcher) { loadImageSync(icon) }
+ withContext(mainDispatcher) { drawableConsumer?.setImageDrawable(drawable) }
+ log("image loaded")
+ }
+
+ private fun startFreeImageJob(icon: Icon, drawableSize: Size): Job =
+ scope.launch {
+ delay(FREE_IMAGE_DELAY_MS)
+ val drawable = createPlaceHolder(drawableSize)
+ displayedState = PlaceHolder(icon, drawable.intrinsicSize)
+ withContext(mainDispatcher) { drawableConsumer?.setImageDrawable(drawable) }
+ log("placeholder loaded")
+ }
+
+ private fun createPlaceHolder(size: Size): Drawable {
+ return PlaceHolderDrawable(width = size.width, height = size.height)
+ }
+
+ private fun isLowRam(): Boolean {
+ return ActivityManager.isLowRamDeviceStatic()
+ }
+
+ private fun getMaxWidth() =
+ context.resources.getDimensionPixelSize(
+ if (isLowRam()) {
+ R.dimen.notification_big_picture_max_width_low_ram
+ } else {
+ R.dimen.notification_big_picture_max_width
+ }
+ )
+
+ private fun getMaxHeight() =
+ context.resources.getDimensionPixelSize(
+ if (isLowRam()) {
+ R.dimen.notification_big_picture_max_height_low_ram
+ } else {
+ R.dimen.notification_big_picture_max_height
+ }
+ )
+
+ private fun log(msg: String) {
+ if (DEBUG) {
+ Log.d(TAG, "$msg state=${getDebugString()}")
+ }
+ }
+
+ private fun getDebugString() =
+ "{ state:$displayedState, hasConsumer:${drawableConsumer != null}, viewShown:$viewShown}"
+
+ private sealed class DrawableState(open val icon: Icon?) {
+ data class Empty(override val icon: Icon?) : DrawableState(icon)
+ data class PlaceHolder(override val icon: Icon, val drawableSize: Size) :
+ DrawableState(icon)
+ data class FullImage(override val icon: Icon, val drawableSize: Size) : DrawableState(icon)
+
+ fun iconSameAs(other: Icon?): Boolean {
+ val displayedIcon = icon
+ return when {
+ displayedIcon == null && other == null -> true
+ displayedIcon != null && other != null -> displayedIcon.sameAs(other)
+ else -> false
+ }
+ }
+ }
+}
+
+/**
+ * @return an image size that conforms to the maxWidth / maxHeight parameters. It can be the same
+ * instance, if the provided size was already small enough.
+ */
+private fun Size.resizeToMax(maxWidth: Int, maxHeight: Int): Size {
+ if (width <= maxWidth && height <= maxHeight) {
+ return this
+ }
+
+ // Calculate the scale factor for both dimensions
+ val wScale =
+ if (maxWidth <= 0) {
+ 1.0f
+ } else {
+ maxWidth.toFloat() / width.toFloat()
+ }
+
+ val hScale =
+ if (maxHeight <= 0) {
+ 1.0f
+ } else {
+ maxHeight.toFloat() / height.toFloat()
+ }
+
+ // Scale down to the smaller scale factor
+ val scale = min(wScale, hScale)
+ if (scale < 1.0f) {
+ val targetWidth = (width * scale).toInt()
+ val targetHeight = (height * scale).toInt()
+
+ return Size(targetWidth, targetHeight)
+ }
+
+ return this
+}
+
+private val Drawable.intrinsicSize
+ get() = Size(/*width=*/ intrinsicWidth, /*height=*/ intrinsicHeight)
+
+private operator fun Size.component1() = width
+
+private operator fun Size.component2() = height
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BigPictureLayoutInflaterFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BigPictureLayoutInflaterFactory.kt
new file mode 100644
index 0000000..e226665
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BigPictureLayoutInflaterFactory.kt
@@ -0,0 +1,52 @@
+/*
+ * 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.systemui.statusbar.notification.row
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.View
+import com.android.internal.widget.BigPictureNotificationImageView
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag
+import javax.inject.Inject
+
+class BigPictureLayoutInflaterFactory @Inject constructor() : NotifRemoteViewsFactory {
+
+ override fun instantiate(
+ row: ExpandableNotificationRow,
+ @InflationFlag layoutType: Int,
+ parent: View?,
+ name: String,
+ context: Context,
+ attrs: AttributeSet
+ ): View? {
+ // Currently the [BigPictureIconManager] only handles one view per notification.
+ // Exclude other layout types for now, to make sure that we set the same iconManager
+ // on only one [BigPictureNotificationImageView].
+ if (layoutType != FLAG_CONTENT_VIEW_EXPANDED) {
+ return null
+ }
+
+ return when (name) {
+ BigPictureNotificationImageView::class.java.name ->
+ BigPictureNotificationImageView(context, attrs).also { view ->
+ view.setIconManager(row.bigPictureIconManager)
+ }
+ else -> null
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index c02382d..acece33 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -376,6 +376,7 @@
private float mTranslationWhenRemoved;
private boolean mWasChildInGroupWhenRemoved;
private NotificationInlineImageResolver mImageResolver;
+ private BigPictureIconManager mBigPictureIconManager;
@Nullable
private OnExpansionChangedListener mExpansionChangedListener;
@Nullable
@@ -592,7 +593,6 @@
mPublicLayout.updateExpandButtons(true);
updateLimits();
updateShelfIconColor();
- updateRippleAllowed();
if (mUpdateSelfBackgroundOnUpdate) {
// Because this is triggered by UiMode change which we already propagated to children,
// we know that child rows will receive the same event, and will update their own
@@ -1355,6 +1355,9 @@
if (mImageResolver != null) {
mImageResolver.updateMaxImageSizes();
}
+ if (mBigPictureIconManager != null) {
+ mBigPictureIconManager.updateMaxImageSizes();
+ }
}
public void onUiModeChanged() {
@@ -1794,6 +1797,16 @@
return mImageResolver;
}
+ public BigPictureIconManager getBigPictureIconManager() {
+ return mBigPictureIconManager;
+ }
+
+ public void setBigPictureIconManager(
+ BigPictureIconManager bigPictureIconManager) {
+ mBigPictureIconManager = bigPictureIconManager;
+ }
+
+
/**
* Resets this view so it can be re-used for an updated notification.
*/
@@ -2556,31 +2569,8 @@
mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf);
}
}
- updateRippleAllowed();
}
- private void updateRippleAllowed() {
- boolean allowed = isOnKeyguard()
- || mEntry.getSbn().getNotification().contentIntent == null;
- setRippleAllowed(allowed);
- }
-
- @Override
- public void onTap() {
- // This notification will expand and animates into the content activity, so we disable the
- // ripple. We will restore its value once the tap/click is actually performed.
- if (mEntry.getSbn().getNotification().contentIntent != null) {
- setRippleAllowed(false);
- }
- }
-
- @Override
- public boolean performClick() {
- // We force-disabled the ripple in onTap. When this method is called, the code drawing the
- // ripple will already have been called so we can restore its value now.
- updateRippleAllowed();
- return super.performClick();
- }
@Override
public int getHeightWithoutLockscreenConstraints() {
@@ -3687,6 +3677,9 @@
pw.println("no viewState!!!");
}
pw.println(getRoundableState().debugString());
+ if (mBigPictureIconManager != null) {
+ mBigPictureIconManager.dump(pw, args);
+ }
dumpBackgroundView(pw, args);
int transientViewCount = mChildrenContainer == null
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
index 647505c..1fd4d12 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
@@ -32,7 +32,6 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.internal.util.ArrayUtils;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
@@ -59,7 +58,6 @@
private int mExpandAnimationWidth = -1;
private int mExpandAnimationHeight = -1;
private int mDrawableAlpha = 255;
- private boolean mIsPressedAllowed;
public NotificationBackgroundView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -153,9 +151,17 @@
public void setTint(int tintColor) {
if (tintColor != 0) {
- mBackground.setColorFilter(tintColor, PorterDuff.Mode.SRC_ATOP);
+ ColorStateList stateList = new ColorStateList(new int[][]{
+ new int[]{com.android.internal.R.attr.state_pressed},
+ new int[]{com.android.internal.R.attr.state_hovered},
+ new int[]{}},
+
+ new int[]{0, 0, tintColor}
+ );
+ mBackground.setTintMode(PorterDuff.Mode.SRC_ATOP);
+ mBackground.setTintList(stateList);
} else {
- mBackground.clearColorFilter();
+ mBackground.setTintList(null);
}
mTintColor = tintColor;
invalidate();
@@ -210,10 +216,6 @@
public void setState(int[] drawableState) {
if (mBackground != null && mBackground.isStateful()) {
- if (!mIsPressedAllowed) {
- drawableState = ArrayUtils.removeInt(drawableState,
- com.android.internal.R.attr.state_pressed);
- }
mBackground.setState(drawableState);
}
}
@@ -267,9 +269,12 @@
return;
}
if (mBackground instanceof LayerDrawable) {
- GradientDrawable gradientDrawable =
- (GradientDrawable) ((LayerDrawable) mBackground).getDrawable(0);
- gradientDrawable.setCornerRadii(mCornerRadii);
+ int numberOfLayers = ((LayerDrawable) mBackground).getNumberOfLayers();
+ for (int i = 0; i < numberOfLayers; i++) {
+ GradientDrawable gradientDrawable =
+ (GradientDrawable) ((LayerDrawable) mBackground).getDrawable(i);
+ gradientDrawable.setCornerRadii(mCornerRadii);
+ }
}
}
@@ -295,10 +300,6 @@
invalidate();
}
- public void setPressedAllowed(boolean allowed) {
- mIsPressedAllowed = allowed;
- }
-
@Override
public void dump(PrintWriter pw, @NonNull String[] args) {
pw.println("mDontModifyCorners: " + mDontModifyCorners);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java
index 867e08b..0239afc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java
@@ -60,12 +60,16 @@
@Named(NOTIF_REMOTEVIEWS_FACTORIES)
static Set<NotifRemoteViewsFactory> provideNotifRemoteViewsFactories(
FeatureFlags featureFlags,
- PrecomputedTextViewFactory precomputedTextViewFactory
+ PrecomputedTextViewFactory precomputedTextViewFactory,
+ BigPictureLayoutInflaterFactory bigPictureLayoutInflaterFactory
) {
final Set<NotifRemoteViewsFactory> replacementFactories = new HashSet<>();
if (featureFlags.isEnabled(Flags.PRECOMPUTED_TEXT)) {
replacementFactories.add(precomputedTextViewFactory);
}
+ if (featureFlags.isEnabled(Flags.BIGPICTURE_NOTIFICATION_LAZY_LOADING)) {
+ replacementFactories.add(bigPictureLayoutInflaterFactory);
+ }
return replacementFactories;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PlaceHolderDrawable.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PlaceHolderDrawable.kt
new file mode 100644
index 0000000..40aa27f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PlaceHolderDrawable.kt
@@ -0,0 +1,33 @@
+package com.android.systemui.statusbar.notification.row
+
+import android.graphics.Canvas
+import android.graphics.ColorFilter
+import android.graphics.PixelFormat
+import android.graphics.drawable.Drawable
+
+class PlaceHolderDrawable(private val width: Int, private val height: Int) : Drawable() {
+
+ companion object {
+ fun createFrom(other: Drawable): PlaceHolderDrawable {
+ return PlaceHolderDrawable(other.intrinsicWidth, other.intrinsicHeight)
+ }
+ }
+
+ override fun getIntrinsicWidth(): Int {
+ return width
+ }
+
+ override fun getIntrinsicHeight(): Int {
+ return height
+ }
+
+ override fun draw(canvas: Canvas) {}
+ override fun setAlpha(alpha: Int) {}
+ override fun setColorFilter(colorFilter: ColorFilter?) {}
+
+ @Suppress("DeprecatedCallableAddReplaceWith")
+ @Deprecated("Deprecated in android.graphics.drawable.Drawable")
+ override fun getOpacity(): Int {
+ return PixelFormat.TRANSPARENT
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PrecomputedTextViewFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PrecomputedTextViewFactory.kt
index 96547db..0c4ffe2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PrecomputedTextViewFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PrecomputedTextViewFactory.kt
@@ -19,7 +19,6 @@
import android.content.Context
import android.util.AttributeSet
import android.view.View
-import android.widget.TextView
import com.android.internal.widget.ConversationLayout
import com.android.internal.widget.ImageFloatingTextView
import com.android.internal.widget.MessagingLayout
@@ -36,8 +35,6 @@
attrs: AttributeSet
): View? {
return when (name) {
- TextView::class.java.name,
- TextView::class.java.simpleName -> PrecomputedTextView(context, attrs)
ImageFloatingTextView::class.java.name ->
PrecomputedImageFloatingTextView(context, attrs)
MessagingLayout::class.java.name ->
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java
index 3588621..0a6a2c8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java
@@ -23,6 +23,7 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
+import com.android.systemui.statusbar.notification.row.BigPictureIconManager;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController;
import com.android.systemui.statusbar.phone.CentralSurfaces;
@@ -68,6 +69,12 @@
ExpandableNotificationRowController getExpandableNotificationRowController();
/**
+ * Creates a BigPictureIconManager.
+ */
+ @NotificationRowScope
+ BigPictureIconManager getBigPictureIconManager();
+
+ /**
* Dagger Module that extracts interesting properties from an ExpandableNotificationRow.
*/
@Module
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/ActivatableNotificationViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/ActivatableNotificationViewBinder.kt
index 54af107..9a54de1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/ActivatableNotificationViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/ActivatableNotificationViewBinder.kt
@@ -82,11 +82,7 @@
}
if (ev.action == MotionEvent.ACTION_UP) {
// If this is a false tap, capture the even so it doesn't result in a click.
- val falseTap: Boolean = falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)
- if (!falseTap && v is ActivatableNotificationView) {
- v.onTap()
- }
- return falseTap
+ return falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)
}
return result
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java
index 175ba15..acd6cc6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java
@@ -28,6 +28,7 @@
import com.android.internal.R;
import com.android.internal.widget.BigPictureNotificationImageView;
import com.android.systemui.statusbar.notification.ImageTransformState;
+import com.android.systemui.statusbar.notification.row.BigPictureIconManager;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
/**
@@ -66,6 +67,17 @@
}
}
+ @Override
+ public void setVisible(boolean visible) {
+ super.setVisible(visible);
+
+ BigPictureIconManager imageManager = mRow.getBigPictureIconManager();
+ if (imageManager != null) {
+ // TODO(b/283082473) call it a bit earlier for true, as soon as the row starts to expand
+ imageManager.onViewShown(visible);
+ }
+ }
+
/**
* Starts or stops the animations in any drawables contained in this BigPicture Notification.
*
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/SharedNotificationContainer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/SharedNotificationContainer.kt
index 688843d..e52d604 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/SharedNotificationContainer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/SharedNotificationContainer.kt
@@ -19,6 +19,7 @@
import android.content.Context
import android.util.AttributeSet
import android.view.View
+import androidx.constraintlayout.core.widgets.Optimizer
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
@@ -45,6 +46,7 @@
private val baseConstraintSet = ConstraintSet()
init {
+ optimizationLevel = optimizationLevel or Optimizer.OPTIMIZATION_GRAPH
baseConstraintSet.apply {
create(R.id.nssl_guideline, VERTICAL)
setGuidelinePercent(R.id.nssl_guideline, 0.5f)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
index cfa481e..3e1f09f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
@@ -25,7 +25,6 @@
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.UserHandle;
-import android.view.MotionEvent;
import android.view.RemoteAnimationAdapter;
import android.view.View;
import android.window.RemoteTransition;
@@ -197,21 +196,6 @@
void onKeyguardViewManagerStatesUpdated();
- /**
- * Used to dispatch initial touch events before crossing the threshold to pull down the
- * notification shade. After that, since the launcher window is set to slippery, input
- * frameworks take care of routing the events to the notification shade.
- */
- void onInputFocusTransfer(boolean start, boolean cancel, float velocity);
-
- /**
- * Dispatches status bar motion event to the notification shade. This is different from
- * {@link #onInputFocusTransfer(boolean, boolean, float)} as it doesn't rely on setting the
- * launcher window slippery to allow the frameworks to route those events after passing the
- * initial threshold.
- */
- default void onStatusBarTrackpadEvent(MotionEvent event) {}
-
/** */
boolean getCommandQueuePanelsEnabled();
@@ -263,14 +247,10 @@
// TODO: Figure out way to remove these.
NavigationBarView getNavigationBarView();
- boolean isOverviewEnabled();
-
void setBouncerShowing(boolean bouncerShowing);
boolean isScreenFullyOff();
- void showScreenPinningRequest(int taskId, boolean allowCancel);
-
@Nullable
Intent getEmergencyActionIntent();
@@ -301,8 +281,6 @@
boolean isBouncerShowingScrimmed();
- boolean isBouncerShowingOverDream();
-
void updateNotificationPanelTouchState();
int getRotation();
@@ -310,10 +288,6 @@
@VisibleForTesting
void setBarStateForTest(int state);
- void showTransientUnchecked();
-
- void clearTransient();
-
void acquireGestureWakeLock(long time);
boolean setAppearance(int appearance);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
index 28bb581..c9db153 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
@@ -36,7 +36,6 @@
import android.util.Slog;
import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
-import android.view.WindowInsets;
import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowInsetsController.Appearance;
import android.view.WindowInsetsController.Behavior;
@@ -59,6 +58,7 @@
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.QSPanelController;
+import com.android.systemui.recents.ScreenPinningRequest;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.CameraLauncher;
import com.android.systemui.shade.QuickSettingsController;
@@ -84,6 +84,7 @@
public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callbacks {
private final CentralSurfaces mCentralSurfaces;
private final Context mContext;
+ private final ScreenPinningRequest mScreenPinningRequest;
private final com.android.systemui.shade.ShadeController mShadeController;
private final CommandQueue mCommandQueue;
private final ShadeViewController mShadeViewController;
@@ -126,6 +127,7 @@
QuickSettingsController quickSettingsController,
Context context,
@Main Resources resources,
+ ScreenPinningRequest screenPinningRequest,
ShadeController shadeController,
CommandQueue commandQueue,
ShadeViewController shadeViewController,
@@ -155,6 +157,7 @@
mCentralSurfaces = centralSurfaces;
mQsController = quickSettingsController;
mContext = context;
+ mScreenPinningRequest = screenPinningRequest;
mShadeController = shadeController;
mCommandQueue = commandQueue;
mShadeViewController = shadeViewController;
@@ -188,17 +191,6 @@
}
@Override
- public void abortTransient(int displayId, @InsetsType int types) {
- if (displayId != mDisplayId) {
- return;
- }
- if ((types & WindowInsets.Type.statusBars()) == 0) {
- return;
- }
- mCentralSurfaces.clearTransient();
- }
-
- @Override
public void addQsTile(ComponentName tile) {
mQSHost.addTile(tile);
}
@@ -483,17 +475,6 @@
}
@Override
- public void showTransient(int displayId, @InsetsType int types, boolean isGestureOnSystemBar) {
- if (displayId != mDisplayId) {
- return;
- }
- if ((types & WindowInsets.Type.statusBars()) == 0) {
- return;
- }
- mCentralSurfaces.showTransientUnchecked();
- }
-
- @Override
public void toggleKeyboardShortcutsMenu(int deviceId) {
mCentralSurfaces.resendMessage(new CentralSurfaces.KeyboardShortcutsMessage(deviceId));
}
@@ -516,7 +497,7 @@
return;
}
// Show screen pinning request, since this comes from an app, show 'no thanks', button.
- mCentralSurfaces.showScreenPinningRequest(taskId, true);
+ mScreenPinningRequest.showPrompt(taskId, true);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt
index ff380db..3cb5e1f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt
@@ -39,7 +39,6 @@
override fun getKeyguardMessageArea(): AuthKeyguardMessageArea? = null
override fun isLaunchingActivityOverLockscreen() = false
override fun onKeyguardViewManagerStatesUpdated() {}
- override fun onInputFocusTransfer(start: Boolean, cancel: Boolean, velocity: Float) {}
override fun getCommandQueuePanelsEnabled() = false
override fun showWirelessChargingAnimation(batteryLevel: Int) {}
override fun checkBarModes() {}
@@ -68,10 +67,8 @@
cancelAction: Runnable?,
) {}
override fun getNavigationBarView(): NavigationBarView? = null
- override fun isOverviewEnabled() = false
override fun setBouncerShowing(bouncerShowing: Boolean) {}
override fun isScreenFullyOff() = false
- override fun showScreenPinningRequest(taskId: Int, allowCancel: Boolean) {}
override fun getEmergencyActionIntent(): Intent? = null
override fun isCameraAllowedByAdmin() = false
override fun isGoingToSleep() = false
@@ -84,12 +81,9 @@
override fun awakenDreams() {}
override fun isBouncerShowing() = false
override fun isBouncerShowingScrimmed() = false
- override fun isBouncerShowingOverDream() = false
override fun updateNotificationPanelTouchState() {}
override fun getRotation() = 0
override fun setBarStateForTest(state: Int) {}
- override fun showTransientUnchecked() {}
- override fun clearTransient() {}
override fun acquireGestureWakeLock(time: Long) {}
override fun setAppearance(appearance: Int) = false
override fun getBarMode() = 0
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 490c469..6b260ff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -87,7 +87,6 @@
import android.view.Display;
import android.view.IRemoteAnimationRunner;
import android.view.IWindowManager;
-import android.view.MotionEvent;
import android.view.ThreadedRenderer;
import android.view.View;
import android.view.WindowInsets;
@@ -168,7 +167,6 @@
import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.qs.QSFragment;
import com.android.systemui.qs.QSPanelController;
-import com.android.systemui.recents.ScreenPinningRequest;
import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor;
import com.android.systemui.scrim.ScrimView;
import com.android.systemui.settings.UserTracker;
@@ -208,6 +206,7 @@
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.core.StatusBarInitializer;
+import com.android.systemui.statusbar.data.repository.StatusBarModeRepository;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider;
@@ -239,6 +238,7 @@
import com.android.systemui.util.WallpaperController;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.concurrency.MessageRouter;
+import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.volume.VolumeComponent;
import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.startingsurface.SplashscreenContentDrawer;
@@ -388,7 +388,6 @@
*/
protected int mState; // TODO: remove this. Just use StatusBarStateController
protected boolean mBouncerShowing;
- private boolean mBouncerShowingOverDream;
private final PhoneStatusBarPolicy mIconPolicy;
@@ -412,6 +411,7 @@
private final NotificationShadeWindowController mNotificationShadeWindowController;
private final StatusBarInitializer mStatusBarInitializer;
private final StatusBarWindowController mStatusBarWindowController;
+ private final StatusBarModeRepository mStatusBarModeRepository;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@VisibleForTesting
DozeServiceHost mDozeServiceHost;
@@ -498,8 +498,6 @@
/** @see android.view.WindowInsetsController#setSystemBarsAppearance(int, int) */
private @Appearance int mAppearance;
- private boolean mTransientShown;
-
private final DisplayMetrics mDisplayMetrics;
// XXX: gesture research
@@ -507,8 +505,6 @@
? new GestureRecorder("/sdcard/statusbar_gestures.dat")
: null;
- private final ScreenPinningRequest mScreenPinningRequest;
-
private final MetricsLogger mMetricsLogger;
// ensure quick settings is disabled until the current user makes it through the setup wizard
@@ -562,11 +558,10 @@
private final ScrimController mScrimController;
protected DozeScrimController mDozeScrimController;
private final BackActionInteractor mBackActionInteractor;
+ private final JavaAdapter mJavaAdapter;
private final Executor mUiBgExecutor;
protected boolean mDozing;
- private boolean mIsFullscreen;
-
boolean mCloseQsBeforeScreenOff;
private final NotificationMediaManager mMediaManager;
@@ -637,6 +632,7 @@
StatusBarInitializer statusBarInitializer,
StatusBarWindowController statusBarWindowController,
StatusBarWindowStateController statusBarWindowStateController,
+ StatusBarModeRepository statusBarModeRepository,
KeyguardUpdateMonitor keyguardUpdateMonitor,
StatusBarSignalPolicy statusBarSignalPolicy,
PulseExpansionHandler pulseExpansionHandler,
@@ -655,6 +651,7 @@
DisplayMetrics displayMetrics,
MetricsLogger metricsLogger,
ShadeLogger shadeLogger,
+ JavaAdapter javaAdapter,
@UiBackground Executor uiBgExecutor,
ShadeSurface shadeSurface,
NotificationMediaManager notificationMediaManager,
@@ -692,7 +689,6 @@
DozeServiceHost dozeServiceHost,
BackActionInteractor backActionInteractor,
PowerManager powerManager,
- ScreenPinningRequest screenPinningRequest,
DozeScrimController dozeScrimController,
VolumeComponent volumeComponent,
CommandQueue commandQueue,
@@ -745,6 +741,7 @@
mAutoHideController = autoHideController;
mStatusBarInitializer = statusBarInitializer;
mStatusBarWindowController = statusBarWindowController;
+ mStatusBarModeRepository = statusBarModeRepository;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mPulseExpansionHandler = pulseExpansionHandler;
mWakeUpCoordinator = notificationWakeUpCoordinator;
@@ -764,6 +761,7 @@
mDisplayMetrics = displayMetrics;
mMetricsLogger = metricsLogger;
mShadeLogger = shadeLogger;
+ mJavaAdapter = javaAdapter;
mUiBgExecutor = uiBgExecutor;
mShadeSurface = shadeSurface;
mMediaManager = notificationMediaManager;
@@ -799,7 +797,6 @@
mDozeParameters = dozeParameters;
mScrimController = scrimController;
mLockscreenWallpaperLazy = lockscreenWallpaperLazy;
- mScreenPinningRequest = screenPinningRequest;
mDozeScrimController = dozeScrimController;
mBiometricUnlockControllerLazy = biometricUnlockControllerLazy;
mAuthRippleController = authRippleController;
@@ -940,7 +937,7 @@
setUpPresenter();
if ((result.mTransientBarTypes & WindowInsets.Type.statusBars()) != 0) {
- showTransientUnchecked();
+ mStatusBarModeRepository.showTransient();
}
mCommandQueueCallbacks.onSystemBarAttributesChanged(mDisplayId, result.mAppearance,
result.mAppearanceRegions, result.mNavbarColorManagedByIme, result.mBehavior,
@@ -1190,6 +1187,11 @@
mWallpaperController.setRootView(getNotificationShadeWindowView());
mDemoModeController.addCallback(mDemoModeCallback);
+ mJavaAdapter.alwaysCollectFlow(
+ mStatusBarModeRepository.isTransientShown(), this::onTransientShownChanged);
+ mJavaAdapter.alwaysCollectFlow(
+ mStatusBarModeRepository.isInFullscreenMode(),
+ this::onStatusBarFullscreenChanged);
mCommandQueueCallbacks = mCommandQueueCallbacksLazy.get();
mCommandQueue.addCallback(mCommandQueueCallbacks);
@@ -1255,7 +1257,7 @@
@Override
public void hide() {
- clearTransient();
+ mStatusBarModeRepository.clearTransient();
}
});
@@ -1543,7 +1545,9 @@
return (v, event) -> {
mAutoHideController.checkUserAutoHide(event);
mRemoteInputManager.checkRemoteInputOutside(event);
- mShadeController.onStatusBarTouch(event);
+ if (!mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ mShadeController.onStatusBarTouch(event);
+ }
return getNotificationShadeWindowView().onTouchEvent(event);
};
}
@@ -1687,27 +1691,6 @@
mHeadsUpManager.releaseAllImmediately();
}
- /**
- * Called when another window is about to transfer it's input focus.
- */
- @Override
- public void onInputFocusTransfer(boolean start, boolean cancel, float velocity) {
- if (!mCommandQueue.panelsEnabled()) {
- return;
- }
-
- if (start) {
- mShadeSurface.startWaitingForExpandGesture();
- } else {
- mShadeSurface.stopWaitingForExpandGesture(cancel, velocity);
- }
- }
-
- @Override
- public void onStatusBarTrackpadEvent(MotionEvent event) {
- mShadeSurface.handleExternalTouch(event);
- }
-
private void onExpandedInvisible() {
setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);
if (!mNotificationActivityStarterLazy.get().isCollapsingToShowActivityOverLockscreen()) {
@@ -1722,25 +1705,19 @@
return mCommandQueue.panelsEnabled();
}
- @Override
- public void showTransientUnchecked() {
- if (!mTransientShown) {
- mTransientShown = true;
+ private void onTransientShownChanged(boolean transientShown) {
+ if (transientShown) {
mNoAnimationOnNextBarModeChange = true;
- maybeUpdateBarMode();
}
+ maybeUpdateBarMode();
}
- @Override
- public void clearTransient() {
- if (mTransientShown) {
- mTransientShown = false;
- maybeUpdateBarMode();
- }
+ private void onStatusBarFullscreenChanged(boolean isWindowShown) {
+ maybeUpdateBarMode();
}
private void maybeUpdateBarMode() {
- final int barMode = barMode(mTransientShown, mAppearance);
+ final int barMode = barMode(isTransientShown(), mAppearance);
if (updateBarMode(barMode)) {
mLightBarController.onStatusBarModeChanged(barMode);
updateBubblesVisibility();
@@ -1758,8 +1735,10 @@
}
private @TransitionMode int barMode(boolean isTransient, int appearance) {
+ boolean isFullscreen = mStatusBarModeRepository.isInFullscreenMode().getValue();
final int lightsOutOpaque = APPEARANCE_LOW_PROFILE_BARS | APPEARANCE_OPAQUE_STATUS_BARS;
- if (mOngoingCallController.hasOngoingCall() && mIsFullscreen) {
+ if (mOngoingCallController.hasOngoingCall() && isFullscreen) {
+ // Force show the status bar if there's an ongoing call.
return MODE_SEMI_TRANSPARENT;
} else if (isTransient) {
return MODE_SEMI_TRANSPARENT;
@@ -2180,10 +2159,16 @@
// * When phone is unlocked: we still don't want to execute hiding of the keyguard
// as the animation could prepare 'fake AOD' interface (without actually
// transitioning to keyguard state) and this might reset the view states
+ // Log for b/290627350
+ Log.d(TAG, "!shouldBeKeyguard mStatusBarStateController.isKeyguardRequested() "
+ + mStatusBarStateController.isKeyguardRequested() + " keyguardForDozing "
+ + keyguardForDozing + " wakeAndUnlocking " + wakeAndUnlocking
+ + " isWakingAndOccluded " + isWakingAndOccluded);
if (!mScreenOffAnimationController.isKeyguardHideDelayed()
// If we're animating occluded, there's an activity launching over the keyguard
// UI. Wait to hide it until after the animation concludes.
&& !mKeyguardViewMediator.isOccludeAnimationPlaying()) {
+ Log.d(TAG, "hideKeyguardImpl " + forceStateChange);
return hideKeyguardImpl(forceStateChange);
}
}
@@ -2460,10 +2445,7 @@
*/
@Override
public boolean shouldKeyguardHideImmediately() {
- final boolean isScrimmedBouncer =
- mScrimController.getState() == ScrimState.BOUNCER_SCRIMMED;
- final boolean isBouncerOverDream = isBouncerShowingOverDream();
- return (isScrimmedBouncer || isBouncerOverDream);
+ return mScrimController.getState() == ScrimState.BOUNCER_SCRIMMED;
}
private void showBouncerOrLockScreenIfKeyguard() {
@@ -2541,11 +2523,6 @@
return mNavigationBarController.getNavigationBarView(mDisplayId);
}
- @Override
- public boolean isOverviewEnabled() {
- return mNavigationBarController.isOverviewEnabled(mDisplayId);
- }
-
/**
* Propagation of the bouncer state, indicating that it's fully visible.
*/
@@ -2815,11 +2792,6 @@
return mScreenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_OFF;
}
- @Override
- public void showScreenPinningRequest(int taskId, boolean allowCancel) {
- mScreenPinningRequest.showPrompt(taskId, allowCancel);
- }
-
@Nullable
@Override
public Intent getEmergencyActionIntent() {
@@ -3160,15 +3132,10 @@
return isBouncerShowing() && mStatusBarKeyguardViewManager.primaryBouncerNeedsScrimming();
}
- @Override
- public boolean isBouncerShowingOverDream() {
- return mBouncerShowingOverDream;
- }
-
// End Extra BaseStatusBarMethods.
boolean isTransientShown() {
- return mTransientShown;
+ return mStatusBarModeRepository.isTransientShown().getValue();
}
private void updateLightRevealScrimVisibility() {
@@ -3251,8 +3218,6 @@
if (DEBUG) {
Log.v(TAG, "configuration changed: " + mContext.getResources().getConfiguration());
}
-
- mScreenPinningRequest.onConfigurationChanged();
}
@Override
@@ -3364,12 +3329,6 @@
updateReportRejectedTouchVisibility();
Trace.endSection();
}
-
- @Override
- public void onFullscreenStateChanged(boolean isFullscreen) {
- mIsFullscreen = isFullscreen;
- maybeUpdateBarMode();
- }
};
private final BatteryController.BatteryStateChangeCallback mBatteryStateChangeCallback =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.kt
index 47ab316..5de0d15 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.kt
@@ -62,6 +62,7 @@
private var keyguardIndicationArea: View? = null
private var binding: KeyguardBottomAreaViewBinder.Binding? = null
private var lockIconViewController: LockIconViewController? = null
+ private var isLockscreenLandscapeEnabled: Boolean = false
/** Initializes the view. */
@Deprecated("Deprecated as part of b/278057014")
@@ -115,15 +116,26 @@
}
}
+ fun setIsLockscreenLandscapeEnabled(isLockscreenLandscapeEnabled: Boolean) {
+ this.isLockscreenLandscapeEnabled = isLockscreenLandscapeEnabled
+ }
+
override fun onFinishInflate() {
super.onFinishInflate()
ambientIndicationArea = findViewById(R.id.ambient_indication_container)
+ keyguardIndicationArea = findViewById(R.id.keyguard_indication_area)
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
binding?.onConfigurationChanged()
+ if (isLockscreenLandscapeEnabled) {
+ updateIndicationAreaBottomMargin()
+ }
+ }
+
+ private fun updateIndicationAreaBottomMargin() {
keyguardIndicationArea?.let {
val params = it.layoutParams as FrameLayout.LayoutParams
params.bottomMargin =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaViewController.kt
index 3942dae..0bad47e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaViewController.kt
@@ -16,11 +16,20 @@
package com.android.systemui.statusbar.phone
+import com.android.systemui.flags.FeatureFlagsClassic
+import com.android.systemui.flags.Flags
import com.android.systemui.util.ViewController
import javax.inject.Inject
-class KeyguardBottomAreaViewController @Inject constructor(view: KeyguardBottomAreaView) :
+class KeyguardBottomAreaViewController
+ @Inject constructor(view: KeyguardBottomAreaView, featureFlags: FeatureFlagsClassic) :
ViewController<KeyguardBottomAreaView> (view) {
+
+ init {
+ view.setIsLockscreenLandscapeEnabled(
+ featureFlags.isEnabled(Flags.LOCKSCREEN_ENABLE_LANDSCAPE))
+ }
+
override fun onViewAttached() {
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
index be336e5..16413d2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
@@ -45,6 +45,8 @@
import com.android.systemui.R;
import com.android.systemui.battery.BatteryMeterViewController;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.log.core.LogLevel;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shade.ShadeViewStateProvider;
@@ -69,6 +71,8 @@
import com.android.systemui.util.ViewController;
import com.android.systemui.util.settings.SecureSettings;
+import kotlin.Unit;
+
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@@ -76,8 +80,6 @@
import javax.inject.Inject;
-import kotlin.Unit;
-
/** View Controller for {@link com.android.systemui.statusbar.phone.KeyguardStatusBarView}. */
public class KeyguardStatusBarViewController extends ViewController<KeyguardStatusBarView> {
private static final String TAG = "KeyguardStatusBarViewController";
@@ -111,6 +113,7 @@
private final BiometricUnlockController mBiometricUnlockController;
private final SysuiStatusBarStateController mStatusBarStateController;
private final StatusBarContentInsetsProvider mInsetsProvider;
+ private final FeatureFlags mFeatureFlags;
private final UserManager mUserManager;
private final StatusBarUserChipViewModel mStatusBarUserChipViewModel;
private final SecureSettings mSecureSettings;
@@ -283,6 +286,7 @@
BiometricUnlockController biometricUnlockController,
SysuiStatusBarStateController statusBarStateController,
StatusBarContentInsetsProvider statusBarContentInsetsProvider,
+ FeatureFlags featureFlags,
UserManager userManager,
StatusBarUserChipViewModel userChipViewModel,
SecureSettings secureSettings,
@@ -308,6 +312,7 @@
mBiometricUnlockController = biometricUnlockController;
mStatusBarStateController = statusBarStateController;
mInsetsProvider = statusBarContentInsetsProvider;
+ mFeatureFlags = featureFlags;
mUserManager = userManager;
mStatusBarUserChipViewModel = userChipViewModel;
mSecureSettings = secureSettings;
@@ -367,7 +372,19 @@
mView.findViewById(R.id.statusIcons), StatusBarLocation.KEYGUARD);
mTintedIconManager.setBlockList(getBlockedIcons());
mStatusBarIconController.addIconGroup(mTintedIconManager);
+ } else {
+ // In the old implementation, the keyguard status bar view is never detached and
+ // re-attached, so only calling #addIconGroup when the IconManager is first created was
+ // safe and correct.
+ // In the new scene framework implementation, the keyguard status bar view *is* detached
+ // whenever the shade is opened on top of lockscreen, and then re-attached when the
+ // shade is closed. So, we need to re-add the IconManager each time we're re-attached to
+ // get icon updates.
+ if (mFeatureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_BAR_VIEW)) {
+ mStatusBarIconController.addIconGroup(mTintedIconManager);
+ }
}
+
mSystemIconsContainer = mView.findViewById(R.id.system_icons);
StatusOverlayHoverListener hoverListener = mStatusOverlayHoverListenerFactory
.createDarkAwareListener(mSystemIconsContainer, mView.darkChangeFlow());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index e337215..8d86d72 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -22,7 +22,6 @@
import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
-import static com.android.systemui.util.kotlin.JavaAdapterKt.combineFlows;
import android.content.Context;
import android.content.res.ColorStateList;
@@ -472,17 +471,11 @@
}
if (mFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
+ // Show the keyguard views whenever we've told WM that the lockscreen is visible.
mShadeViewController.postToView(() ->
collectFlow(
getViewRootImpl().getView(),
- combineFlows(
- mKeyguardTransitionInteractor.getFinishedKeyguardState(),
- mWmLockscreenVisibilityInteractor.get()
- .getUsingKeyguardGoingAwayAnimation(),
- (finishedState, animating) ->
- KeyguardInteractor.Companion.isKeyguardVisibleInState(
- finishedState)
- || animating),
+ mWmLockscreenVisibilityInteractor.get().getLockscreenVisibility(),
this::consumeShowStatusBarKeyguardView));
}
}
@@ -635,8 +628,12 @@
protected void showBouncerOrKeyguard(boolean hideBouncerWhenShowing) {
if (needsFullscreenBouncer() && !mDozing) {
// The keyguard might be showing (already). So we need to hide it.
- mCentralSurfaces.hideKeyguard();
- mPrimaryBouncerInteractor.show(true);
+ if (!primaryBouncerIsShowing()) {
+ mCentralSurfaces.hideKeyguard();
+ mPrimaryBouncerInteractor.show(true);
+ } else {
+ Log.e(TAG, "Attempted to show the sim bouncer when it is already showing.");
+ }
} else {
mCentralSurfaces.showKeyguard();
if (hideBouncerWhenShowing) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemBarAttributesListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemBarAttributesListener.kt
index c0269b8..829577b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemBarAttributesListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemBarAttributesListener.kt
@@ -26,7 +26,6 @@
import com.android.systemui.Dumpable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dump.DumpManager
-import com.android.systemui.statusbar.SysuiStatusBarStateController
import java.io.PrintWriter
import javax.inject.Inject
@@ -43,7 +42,6 @@
internal constructor(
private val centralSurfaces: CentralSurfaces,
private val letterboxAppearanceCalculator: LetterboxAppearanceCalculator,
- private val statusBarStateController: SysuiStatusBarStateController,
private val lightBarController: LightBarController,
dumpManager: DumpManager,
) : Dumpable, StatusBarBoundsProvider.BoundsChangeListener {
@@ -101,8 +99,6 @@
appearanceRegions, barModeChanged, centralSurfaces.barMode, navbarColorManagedByIme)
centralSurfaces.updateBubblesVisibility()
- statusBarStateController.setSystemBarAttributes(
- appearance, behavior, requestedVisibleTypes, packageName)
}
private fun modifyAppearanceIfNeeded(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
index b3af91d..230bb58 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
@@ -27,14 +27,16 @@
import android.view.View
import androidx.annotation.VisibleForTesting
import com.android.internal.jank.InteractionJankMonitor
+import com.android.systemui.CoreStartable
import com.android.systemui.Dumpable
import com.android.systemui.R
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.statusbar.data.repository.StatusBarModeRepository
import com.android.systemui.statusbar.gesture.SwipeStatusBarAwayGestureHandler
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
@@ -42,8 +44,9 @@
import com.android.systemui.statusbar.policy.CallbackController
import com.android.systemui.statusbar.window.StatusBarWindowController
import com.android.systemui.util.time.SystemClock
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
import java.io.PrintWriter
-import java.util.Optional
import java.util.concurrent.Executor
import javax.inject.Inject
@@ -52,19 +55,19 @@
*/
@SysUISingleton
class OngoingCallController @Inject constructor(
+ @Application private val scope: CoroutineScope,
private val context: Context,
private val notifCollection: CommonNotifCollection,
- private val ongoingCallFlags: OngoingCallFlags,
private val systemClock: SystemClock,
private val activityStarter: ActivityStarter,
@Main private val mainExecutor: Executor,
private val iActivityManager: IActivityManager,
private val logger: OngoingCallLogger,
private val dumpManager: DumpManager,
- private val statusBarWindowController: Optional<StatusBarWindowController>,
- private val swipeStatusBarAwayGestureHandler: Optional<SwipeStatusBarAwayGestureHandler>,
- private val statusBarStateController: StatusBarStateController
-) : CallbackController<OngoingCallListener>, Dumpable {
+ private val statusBarWindowController: StatusBarWindowController,
+ private val swipeStatusBarAwayGestureHandler: SwipeStatusBarAwayGestureHandler,
+ private val statusBarModeRepository: StatusBarModeRepository,
+) : CallbackController<OngoingCallListener>, Dumpable, CoreStartable {
private var isFullscreen: Boolean = false
/** Non-null if there's an active call notification. */
private var callNotificationInfo: CallNotificationInfo? = null
@@ -120,11 +123,15 @@
}
}
- fun init() {
+ override fun start() {
dumpManager.registerDumpable(this)
- if (ongoingCallFlags.isStatusBarChipEnabled()) {
- notifCollection.addCollectionListener(notifListener)
- statusBarStateController.addCallback(statusBarStateListener)
+ notifCollection.addCollectionListener(notifListener)
+ scope.launch {
+ statusBarModeRepository.isInFullscreenMode.collect {
+ isFullscreen = it
+ updateChipClickListener()
+ updateGestureListening()
+ }
}
}
@@ -138,7 +145,7 @@
this.chipView = chipView
val backgroundView: OngoingCallBackgroundContainer? =
chipView.findViewById(R.id.ongoing_call_chip_background)
- backgroundView?.maxHeightFetcher = { statusBarWindowController.get().statusBarHeight }
+ backgroundView?.maxHeightFetcher = { statusBarWindowController.statusBarHeight }
if (hasOngoingCall()) {
updateChip()
}
@@ -197,9 +204,7 @@
uidObserver.registerWithUid(currentCallNotificationInfo.uid)
if (!currentCallNotificationInfo.statusBarSwipedAway) {
- statusBarWindowController.ifPresent {
- it.setOngoingProcessRequiresStatusBarVisible(true)
- }
+ statusBarWindowController.setOngoingProcessRequiresStatusBarVisible(true)
}
updateGestureListening()
mListeners.forEach { l -> l.onOngoingCallStateChanged(animate = true) }
@@ -217,23 +222,19 @@
private fun updateChipClickListener() {
if (callNotificationInfo == null) { return }
- if (isFullscreen && !ongoingCallFlags.isInImmersiveChipTapEnabled()) {
- chipView?.setOnClickListener(null)
- } else {
- val currentChipView = chipView
- val backgroundView =
- currentChipView?.findViewById<View>(R.id.ongoing_call_chip_background)
- val intent = callNotificationInfo?.intent
- if (currentChipView != null && backgroundView != null && intent != null) {
- currentChipView.setOnClickListener {
- logger.logChipClicked()
- activityStarter.postStartActivityDismissingKeyguard(
- intent,
- ActivityLaunchAnimator.Controller.fromView(
- backgroundView,
- InteractionJankMonitor.CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP)
- )
- }
+ val currentChipView = chipView
+ val backgroundView =
+ currentChipView?.findViewById<View>(R.id.ongoing_call_chip_background)
+ val intent = callNotificationInfo?.intent
+ if (currentChipView != null && backgroundView != null && intent != null) {
+ currentChipView.setOnClickListener {
+ logger.logChipClicked()
+ activityStarter.postStartActivityDismissingKeyguard(
+ intent,
+ ActivityLaunchAnimator.Controller.fromView(
+ backgroundView,
+ InteractionJankMonitor.CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP)
+ )
}
}
}
@@ -247,10 +248,10 @@
if (callNotificationInfo == null ||
callNotificationInfo?.statusBarSwipedAway == true ||
!isFullscreen) {
- swipeStatusBarAwayGestureHandler.ifPresent { it.removeOnGestureDetectedCallback(TAG) }
+ swipeStatusBarAwayGestureHandler.removeOnGestureDetectedCallback(TAG)
} else {
- swipeStatusBarAwayGestureHandler.ifPresent {
- it.addOnGestureDetectedCallback(TAG) { _ -> onSwipeAwayGestureDetected() }
+ swipeStatusBarAwayGestureHandler.addOnGestureDetectedCallback(TAG) { _ ->
+ onSwipeAwayGestureDetected()
}
}
}
@@ -258,8 +259,8 @@
private fun removeChip() {
callNotificationInfo = null
tearDownChipView()
- statusBarWindowController.ifPresent { it.setOngoingProcessRequiresStatusBarVisible(false) }
- swipeStatusBarAwayGestureHandler.ifPresent { it.removeOnGestureDetectedCallback(TAG) }
+ statusBarWindowController.setOngoingProcessRequiresStatusBarVisible(false)
+ swipeStatusBarAwayGestureHandler.removeOnGestureDetectedCallback(TAG)
mListeners.forEach { l -> l.onOngoingCallStateChanged(animate = true) }
uidObserver.unregister()
}
@@ -283,20 +284,8 @@
private fun onSwipeAwayGestureDetected() {
if (DEBUG) { Log.d(TAG, "Swipe away gesture detected") }
callNotificationInfo = callNotificationInfo?.copy(statusBarSwipedAway = true)
- statusBarWindowController.ifPresent {
- it.setOngoingProcessRequiresStatusBarVisible(false)
- }
- swipeStatusBarAwayGestureHandler.ifPresent {
- it.removeOnGestureDetectedCallback(TAG)
- }
- }
-
- private val statusBarStateListener = object : StatusBarStateController.StateListener {
- override fun onFullscreenStateChanged(isFullscreen: Boolean) {
- this@OngoingCallController.isFullscreen = isFullscreen
- updateChipClickListener()
- updateGestureListening()
- }
+ statusBarWindowController.setOngoingProcessRequiresStatusBarVisible(false)
+ swipeStatusBarAwayGestureHandler.removeOnGestureDetectedCallback(TAG)
}
private data class CallNotificationInfo(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallFlags.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallFlags.kt
deleted file mode 100644
index fcfcb8f..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallFlags.kt
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.phone.ongoingcall
-
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
-import javax.inject.Inject
-
-@SysUISingleton
-class OngoingCallFlags @Inject constructor(private val featureFlags: FeatureFlags) {
-
- fun isStatusBarChipEnabled(): Boolean =
- featureFlags.isEnabled(Flags.ONGOING_CALL_STATUS_BAR_CHIP)
-
- fun isInImmersiveEnabled(): Boolean = isStatusBarChipEnabled()
- && featureFlags.isEnabled(Flags.ONGOING_CALL_IN_IMMERSIVE)
-
- fun isInImmersiveChipTapEnabled(): Boolean = isInImmersiveEnabled()
- && featureFlags.isEnabled(Flags.ONGOING_CALL_IN_IMMERSIVE_CHIP_TAP)
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
index dabdcc5..39cdfa3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
@@ -169,6 +169,7 @@
@Override
public void addCallback(@NonNull Callback callback) {
synchronized (mCallbacksLock) {
+ Log.d(TAG, "Added callback " + callback.getClass());
mCallbacks.add(callback);
}
}
@@ -176,6 +177,7 @@
@Override
public void removeCallback(@NonNull Callback callback) {
synchronized (mCallbacksLock) {
+ Log.d(TAG, "Removed callback " + callback.getClass());
mCallbacks.remove(callback);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java
index 81d04d4..6fd0a4d 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java
@@ -16,6 +16,8 @@
package com.android.systemui.wallet.ui;
+import static com.android.systemui.wallet.util.WalletCardUtilsKt.getPaymentCards;
+
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
@@ -47,10 +49,10 @@
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
/** Controller for the wallet card carousel screen. */
public class WalletScreenController implements
@@ -126,22 +128,11 @@
return;
}
Log.i(TAG, "Successfully retrieved wallet cards.");
- List<WalletCard> walletCards = response.getWalletCards();
+ List<WalletCard> walletCards = getPaymentCards(response.getWalletCards());
- boolean allUnknown = true;
- for (WalletCard card : walletCards) {
- if (card.getCardType() != WalletCard.CARD_TYPE_UNKNOWN) {
- allUnknown = false;
- break;
- }
- }
-
- List<WalletCardViewInfo> paymentCardData = new ArrayList<>();
- for (WalletCard card : walletCards) {
- if (allUnknown || card.getCardType() == WalletCard.CARD_TYPE_PAYMENT) {
- paymentCardData.add(new QAWalletCardViewInfo(mContext, card));
- }
- }
+ List<WalletCardViewInfo> paymentCardData = walletCards.stream().map(
+ card -> new QAWalletCardViewInfo(mContext, card)
+ ).collect(Collectors.toList());
// Get on main thread for UI updates.
mHandler.post(() -> {
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/util/WalletCardUtils.kt b/packages/SystemUI/src/com/android/systemui/wallet/util/WalletCardUtils.kt
new file mode 100644
index 0000000..077b80b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/wallet/util/WalletCardUtils.kt
@@ -0,0 +1,34 @@
+/*
+ * 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.systemui.wallet.util
+
+import android.service.quickaccesswallet.WalletCard
+
+/**
+ * Filters wallet cards to only those of [WalletCard.CARD_TYPE_PAYMENT], or returns all cards if
+ * they are all of [WalletCard.CARD_TYPE_UNKNOWN] (maintaining pre-U behavior). Used by the wallet
+ * card carousel, quick settings tile, and lock screen.
+ */
+fun getPaymentCards(walletCards: List<WalletCard>): List<WalletCard> {
+ val atLeastOneKnownCardType = walletCards.any { it.cardType != WalletCard.CARD_TYPE_UNKNOWN }
+
+ return if (atLeastOneKnownCardType) {
+ walletCards.filter { it.cardType == WalletCard.CARD_TYPE_PAYMENT }
+ } else {
+ walletCards
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index 6fafcd5..897c4da 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -61,6 +61,7 @@
import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
import com.android.wm.shell.onehanded.OneHandedUiEventLogger;
import com.android.wm.shell.pip.Pip;
+import com.android.wm.shell.recents.RecentTasks;
import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.sysui.ShellInterface;
@@ -109,6 +110,7 @@
private final Optional<SplitScreen> mSplitScreenOptional;
private final Optional<OneHanded> mOneHandedOptional;
private final Optional<DesktopMode> mDesktopModeOptional;
+ private final Optional<RecentTasks> mRecentTasksOptional;
private final CommandQueue mCommandQueue;
private final ConfigurationController mConfigurationController;
@@ -172,6 +174,7 @@
Optional<SplitScreen> splitScreenOptional,
Optional<OneHanded> oneHandedOptional,
Optional<DesktopMode> desktopMode,
+ Optional<RecentTasks> recentTasks,
CommandQueue commandQueue,
ConfigurationController configurationController,
KeyguardStateController keyguardStateController,
@@ -195,6 +198,7 @@
mSplitScreenOptional = splitScreenOptional;
mOneHandedOptional = oneHandedOptional;
mDesktopModeOptional = desktopMode;
+ mRecentTasksOptional = recentTasks;
mWakefulnessLifecycle = wakefulnessLifecycle;
mUserTracker = userTracker;
mDisplayTracker = displayTracker;
@@ -220,6 +224,7 @@
mSplitScreenOptional.ifPresent(this::initSplitScreen);
mOneHandedOptional.ifPresent(this::initOneHanded);
mDesktopModeOptional.ifPresent(this::initDesktopMode);
+ mRecentTasksOptional.ifPresent(this::initRecentTasks);
mNoteTaskInitializer.initialize();
}
@@ -351,6 +356,12 @@
}, mSysUiMainExecutor);
}
+ @VisibleForTesting
+ void initRecentTasks(RecentTasks recentTasks) {
+ recentTasks.addAnimationStateListener(mSysUiMainExecutor,
+ mCommandQueue::onRecentsAnimationStateChanged);
+ }
+
@Override
public boolean isDumpCritical() {
// Dump can't be critical because the shell has to dump on the main thread for
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
index 93048a5..0ef9f45 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
@@ -94,9 +94,10 @@
.thenReturn(mKeyguardMessageAreaController)
fakeFeatureFlags = FakeFeatureFlags()
fakeFeatureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, false)
+ fakeFeatureFlags.set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false)
mKeyguardPatternView =
View.inflate(mContext, R.layout.keyguard_pattern_view, null) as KeyguardPatternView
-
+ mKeyguardPatternView.setIsLockScreenLandscapeEnabled(false)
mKeyguardPatternViewController =
KeyguardPatternViewController(
mKeyguardPatternView,
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
index 33d4097..a9f044c 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
@@ -123,7 +123,6 @@
private fun constructPinViewController(
mKeyguardPinView: KeyguardPINView
): KeyguardPinViewController {
- mKeyguardPinView.setIsLockScreenLandscapeEnabled(false)
return KeyguardPinViewController(
mKeyguardPinView,
keyguardUpdateMonitor,
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
index decc457..929604b 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
@@ -810,7 +810,8 @@
ObservableTransitionState.Transition(
SceneKey.Lockscreen,
SceneKey.Bouncer,
- flowOf(.5f)
+ flowOf(.5f),
+ false,
)
runCurrent()
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer, null), "reason")
@@ -822,7 +823,12 @@
// keyguard.
sceneInteractor.changeScene(SceneModel(SceneKey.Gone, null), "reason")
sceneTransitionStateFlow.value =
- ObservableTransitionState.Transition(SceneKey.Bouncer, SceneKey.Gone, flowOf(.5f))
+ ObservableTransitionState.Transition(
+ SceneKey.Bouncer,
+ SceneKey.Gone,
+ flowOf(.5f),
+ false
+ )
runCurrent()
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone, null), "reason")
sceneTransitionStateFlow.value = ObservableTransitionState.Idle(SceneKey.Gone)
@@ -834,7 +840,12 @@
clearInvocations(viewMediatorCallback)
sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer, null), "reason")
sceneTransitionStateFlow.value =
- ObservableTransitionState.Transition(SceneKey.Gone, SceneKey.Bouncer, flowOf(.5f))
+ ObservableTransitionState.Transition(
+ SceneKey.Gone,
+ SceneKey.Bouncer,
+ flowOf(.5f),
+ false
+ )
runCurrent()
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer, null), "reason")
sceneTransitionStateFlow.value = ObservableTransitionState.Idle(SceneKey.Bouncer)
@@ -847,7 +858,12 @@
underTest.onViewDetached()
sceneInteractor.changeScene(SceneModel(SceneKey.Gone, null), "reason")
sceneTransitionStateFlow.value =
- ObservableTransitionState.Transition(SceneKey.Bouncer, SceneKey.Gone, flowOf(.5f))
+ ObservableTransitionState.Transition(
+ SceneKey.Bouncer,
+ SceneKey.Gone,
+ flowOf(.5f),
+ false
+ )
runCurrent()
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone, null), "reason")
sceneTransitionStateFlow.value = ObservableTransitionState.Idle(SceneKey.Gone)
@@ -860,7 +876,8 @@
ObservableTransitionState.Transition(
SceneKey.Gone,
SceneKey.Lockscreen,
- flowOf(.5f)
+ flowOf(.5f),
+ false,
)
runCurrent()
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Lockscreen, null), "reason")
@@ -876,7 +893,8 @@
ObservableTransitionState.Transition(
SceneKey.Lockscreen,
SceneKey.Gone,
- flowOf(.5f)
+ flowOf(.5f),
+ false,
)
runCurrent()
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone, null), "reason")
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 562d3a5..1d7c328 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -29,6 +29,7 @@
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_OPTICAL;
import static android.telephony.SubscriptionManager.DATA_ROAMING_DISABLE;
import static android.telephony.SubscriptionManager.NAME_SOURCE_CARRIER_ID;
+
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
@@ -38,11 +39,17 @@
import static com.android.keyguard.KeyguardUpdateMonitor.DEFAULT_CANCEL_SIGNAL_TIMEOUT;
import static com.android.keyguard.KeyguardUpdateMonitor.HAL_POWER_PRESS_TIMEOUT;
import static com.android.keyguard.KeyguardUpdateMonitor.getCurrentUser;
+import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP;
+import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
+import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_WAKING;
import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_CLOSED;
import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_OPENED;
import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_UNKNOWN;
+
import static com.google.common.truth.Truth.assertThat;
+
import static junit.framework.Assert.assertEquals;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -139,6 +146,7 @@
import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.settings.FakeDisplayTracker;
@@ -172,7 +180,6 @@
import java.util.List;
import java.util.Optional;
import java.util.Random;
-import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
@@ -276,6 +283,8 @@
private TaskStackChangeListeners mTaskStackChangeListeners;
@Mock
private IActivityTaskManager mActivityTaskManager;
+ @Mock
+ private WakefulnessLifecycle mWakefulness;
private List<FaceSensorPropertiesInternal> mFaceSensorProperties;
private List<FingerprintSensorPropertiesInternal> mFingerprintSensorProperties;
@@ -1363,7 +1372,7 @@
assertThat(mKeyguardUpdateMonitor.mUserFingerprintAuthenticated.size()).isEqualTo(1);
assertThat(mKeyguardUpdateMonitor.mUserFaceAuthenticated.size()).isEqualTo(1);
- mKeyguardUpdateMonitor.handleUserSwitching(10 /* user */, new CountDownLatch(0));
+ mKeyguardUpdateMonitor.handleUserSwitching(10 /* user */, () -> {});
assertThat(mKeyguardUpdateMonitor.mUserFingerprintAuthenticated.size()).isEqualTo(0);
assertThat(mKeyguardUpdateMonitor.mUserFaceAuthenticated.size()).isEqualTo(0);
}
@@ -2999,8 +3008,57 @@
// THEN face listening is stopped.
verify(faceCancel).cancel();
verify(callback).onBiometricRunningStateChanged(
- eq(false), eq(BiometricSourceType.FACE)); // beverlyt
+ eq(false), eq(BiometricSourceType.FACE));
+ }
+ @Test
+ public void onDisplayOff_whileAsleep_doesNotStopFaceAuth() throws RemoteException {
+ enableStopFaceAuthOnDisplayOff();
+ when(mWakefulness.getWakefulness()).thenReturn(WAKEFULNESS_ASLEEP);
+
+ // GIVEN device is listening for face
+ mKeyguardUpdateMonitor.setKeyguardShowing(true, false);
+ mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
+ mTestableLooper.processAllMessages();
+ verifyFaceAuthenticateCall();
+
+ final CancellationSignal faceCancel = spy(mKeyguardUpdateMonitor.mFaceCancelSignal);
+ mKeyguardUpdateMonitor.mFaceCancelSignal = faceCancel;
+ KeyguardUpdateMonitorCallback callback = mock(KeyguardUpdateMonitorCallback.class);
+ mKeyguardUpdateMonitor.registerCallback(callback);
+
+ // WHEN the default display state changes to OFF
+ triggerDefaultDisplayStateChangeToOff();
+
+ // THEN face listening is NOT stopped.
+ verify(faceCancel, never()).cancel();
+ verify(callback, never()).onBiometricRunningStateChanged(
+ eq(false), eq(BiometricSourceType.FACE));
+ }
+
+ @Test
+ public void onDisplayOff_whileWaking_doesNotStopFaceAuth() throws RemoteException {
+ enableStopFaceAuthOnDisplayOff();
+ when(mWakefulness.getWakefulness()).thenReturn(WAKEFULNESS_WAKING);
+
+ // GIVEN device is listening for face
+ mKeyguardUpdateMonitor.setKeyguardShowing(true, false);
+ mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
+ mTestableLooper.processAllMessages();
+ verifyFaceAuthenticateCall();
+
+ final CancellationSignal faceCancel = spy(mKeyguardUpdateMonitor.mFaceCancelSignal);
+ mKeyguardUpdateMonitor.mFaceCancelSignal = faceCancel;
+ KeyguardUpdateMonitorCallback callback = mock(KeyguardUpdateMonitorCallback.class);
+ mKeyguardUpdateMonitor.registerCallback(callback);
+
+ // WHEN the default display state changes to OFF
+ triggerDefaultDisplayStateChangeToOff();
+
+ // THEN face listening is NOT stopped.
+ verify(faceCancel, never()).cancel();
+ verify(callback, never()).onBiometricRunningStateChanged(
+ eq(false), eq(BiometricSourceType.FACE));
}
private void triggerDefaultDisplayStateChangeToOn() {
@@ -3307,6 +3365,7 @@
clearInvocations(mStatusBarStateController);
mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(mContext);
setupBiometrics(mKeyguardUpdateMonitor);
+ when(mWakefulness.getWakefulness()).thenReturn(WAKEFULNESS_AWAKE);
assertThat(mDisplayTracker.getDisplayCallbacks().size()).isEqualTo(1);
}
@@ -3387,7 +3446,8 @@
mPackageManager, mFaceManager, mFingerprintManager, mBiometricManager,
mFaceWakeUpTriggersConfig, mDevicePostureController,
Optional.of(mInteractiveToAuthProvider),
- mTaskStackChangeListeners, mActivityTaskManager, mDisplayTracker);
+ mTaskStackChangeListeners, mActivityTaskManager, mDisplayTracker,
+ mWakefulness);
setStrongAuthTracker(KeyguardUpdateMonitorTest.this.mStrongAuthTracker);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
index f9830b1..0b0410a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
@@ -59,6 +59,7 @@
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
+import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
@@ -108,6 +109,7 @@
private AppOpsControllerImpl mController;
private TestableLooper mTestableLooper;
+ private final FakeExecutor mBgExecutor = new FakeExecutor(new FakeSystemClock());
private String mExemptedRolePkgName;
@@ -139,6 +141,7 @@
mController = new AppOpsControllerImpl(
mContext,
mTestableLooper.getLooper(),
+ mBgExecutor,
mDumpManager,
mAudioManager,
mSensorPrivacyController,
@@ -150,6 +153,8 @@
@Test
public void testOnlyListenForFewOps() {
mController.setListening(true);
+ mBgExecutor.runAllReady();
+
verify(mAppOpsManager, times(1)).startWatchingActive(AppOpsControllerImpl.OPS, mController);
verify(mDispatcher, times(1)).registerReceiverWithHandler(eq(mController), any(), any());
verify(mSensorPrivacyController, times(1)).addCallback(mController);
@@ -158,6 +163,7 @@
@Test
public void testStopListening() {
mController.setListening(false);
+ mBgExecutor.runAllReady();
verify(mAppOpsManager, times(1)).stopWatchingActive(mController);
verify(mDispatcher, times(1)).unregisterReceiver(mController);
verify(mSensorPrivacyController, times(1)).removeCallback(mController);
@@ -169,6 +175,7 @@
.thenReturn(List.of());
mController.setListening(true);
+ mBgExecutor.runAllReady();
assertThat(mController.getActiveAppOps()).isEmpty();
}
@@ -186,6 +193,7 @@
// WHEN we start listening
mController.setListening(true);
+ mBgExecutor.runAllReady();
// THEN the active list has the op
List<AppOpItem> list = mController.getActiveAppOps();
@@ -218,6 +226,7 @@
// WHEN we start listening
mController.setListening(true);
+ mBgExecutor.runAllReady();
// THEN the active list has the ops
List<AppOpItem> list = mController.getActiveAppOps();
@@ -265,6 +274,7 @@
// WHEN we start listening
mController.setListening(true);
+ mBgExecutor.runAllReady();
// THEN the active list has the ops
List<AppOpItem> list = mController.getActiveAppOps();
@@ -304,6 +314,7 @@
// WHEN we start listening
mController.setListening(true);
+ mBgExecutor.runAllReady();
// THEN the active list has the ops
List<AppOpItem> list = mController.getActiveAppOps();
@@ -341,6 +352,7 @@
mController.addCallback(
new int[]{AppOpsManager.OP_RECORD_AUDIO, AppOpsManager.OP_FINE_LOCATION},
mCallback);
+ mBgExecutor.runAllReady();
mTestableLooper.processAllMessages();
// THEN the callback is notified of the current active ops it cares about
@@ -366,11 +378,14 @@
mController.addCallback(
new int[]{AppOpsManager.OP_RECORD_AUDIO, AppOpsManager.OP_FINE_LOCATION},
mCallback);
+ mBgExecutor.runAllReady();
+
mController.onOpActiveChanged(
AppOpsManager.OPSTR_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED);
mTestableLooper.processAllMessages();
+
verify(mCallback).onActiveStateChanged(AppOpsManager.OP_RECORD_AUDIO,
TEST_UID, TEST_PACKAGE_NAME, true);
}
@@ -383,6 +398,7 @@
public void addCallback_partialIncludedCode() {
mController.addCallback(new int[]{AppOpsManager.OP_RECEIVE_SANDBOX_TRIGGER_AUDIO,
AppOpsManager.OP_FINE_LOCATION}, mCallback);
+ mBgExecutor.runAllReady();
mController.onOpActiveChanged(
AppOpsManager.OPSTR_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
mController.onOpActiveChanged(
@@ -400,9 +416,12 @@
@Test
public void addCallback_notIncludedCode() {
mController.addCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback);
+ mBgExecutor.runAllReady();
+
mController.onOpActiveChanged(
AppOpsManager.OPSTR_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
mTestableLooper.processAllMessages();
+
verify(mCallback, never()).onActiveStateChanged(
anyInt(), anyInt(), anyString(), anyBoolean());
}
@@ -410,7 +429,10 @@
@Test
public void removeCallback_sameCode() {
mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback);
+ mBgExecutor.runAllReady();
+
mController.removeCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback);
+ mBgExecutor.runAllReady();
mController.onOpActiveChanged(
AppOpsManager.OPSTR_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
mTestableLooper.processAllMessages();
@@ -421,7 +443,10 @@
@Test
public void addCallback_notSameCode() {
mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback);
+ mBgExecutor.runAllReady();
+
mController.removeCallback(new int[]{AppOpsManager.OP_CAMERA}, mCallback);
+ mBgExecutor.runAllReady();
mController.onOpActiveChanged(
AppOpsManager.OPSTR_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
mTestableLooper.processAllMessages();
@@ -484,6 +509,8 @@
assumeFalse(mExemptedRolePkgName == null || mExemptedRolePkgName.equals(""));
mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback);
+ mBgExecutor.runAllReady();
+
mController.onOpActiveChanged(AppOpsManager.OPSTR_RECORD_AUDIO,
TEST_UID_NON_USER_SENSITIVE, mExemptedRolePkgName, true);
@@ -506,6 +533,7 @@
mController.setBGHandler(mMockHandler);
mController.setListening(true);
+ mBgExecutor.runAllReady();
mController.onOpActiveChanged(AppOpsManager.OPSTR_FINE_LOCATION, TEST_UID,
TEST_PACKAGE_NAME, true);
mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
@@ -513,6 +541,7 @@
assertFalse(mController.getActiveAppOps().isEmpty());
mController.setListening(false);
+ mBgExecutor.runAllReady();
verify(mMockHandler).removeCallbacksAndMessages(null);
assertTrue(mController.getActiveAppOps().isEmpty());
@@ -583,6 +612,7 @@
TestHandler testHandler = new TestHandler(mTestableLooper.getLooper());
mController.addCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback);
+ mBgExecutor.runAllReady();
mController.setBGHandler(testHandler);
mController.onOpActiveChanged(
@@ -616,6 +646,7 @@
@Test
public void testNotedNotRemovedAfterActive() {
mController.addCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback);
+ mBgExecutor.runAllReady();
mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED);
@@ -645,6 +676,7 @@
@Test
public void testNotedAndActiveOnlyOneCall() {
mController.addCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback);
+ mBgExecutor.runAllReady();
mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED);
@@ -660,6 +692,7 @@
@Test
public void testActiveAndNotedOnlyOneCall() {
mController.addCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback);
+ mBgExecutor.runAllReady();
mController.onOpActiveChanged(
AppOpsManager.OPSTR_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true);
@@ -675,6 +708,7 @@
@Test
public void testPausedRecordingIsRetrievedOnCreation() {
mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback);
+ mBgExecutor.runAllReady();
mTestableLooper.processAllMessages();
mController.onOpActiveChanged(
@@ -688,6 +722,7 @@
@Test
public void testPausedRecordingFilteredOut() {
mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback);
+ mBgExecutor.runAllReady();
mTestableLooper.processAllMessages();
mController.onOpActiveChanged(
@@ -700,6 +735,7 @@
@Test
public void testPausedPhoneCallMicrophoneFilteredOut() {
mController.addCallback(new int[]{AppOpsManager.OP_PHONE_CALL_MICROPHONE}, mCallback);
+ mBgExecutor.runAllReady();
mTestableLooper.processAllMessages();
mController.onOpActiveChanged(
@@ -715,6 +751,7 @@
AppOpsManager.OP_RECORD_AUDIO,
AppOpsManager.OP_CAMERA
}, mCallback);
+ mBgExecutor.runAllReady();
mTestableLooper.processAllMessages();
mController.onOpActiveChanged(
@@ -751,6 +788,7 @@
// Add callbacks for the micOp and nonMicOp, called for the micOp active state change,
// verify the micOp is the only active op returned.
mController.addCallback(new int[]{micOp, nonMicOp}, mCallback);
+ mBgExecutor.runAllReady();
mTestableLooper.processAllMessages();
mController.onOpActiveChanged(
AppOpsManager.opToPublicName(micOp), TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
@@ -779,6 +817,7 @@
// Add callbacks for the micOp and nonMicOp, called for the micOp active state change,
// verify the micOp is the only active op returned.
mController.addCallback(new int[]{micOp, nonMicOp}, mCallback);
+ mBgExecutor.runAllReady();
mTestableLooper.processAllMessages();
mController.onOpActiveChanged(
AppOpsManager.opToPublicName(micOp), TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
@@ -807,6 +846,7 @@
// Add callbacks for the micOp and nonMicOp, called for the micOp active state change,
// verify the micOp is the only active op returned.
mController.addCallback(new int[]{micOp, nonMicOp}, mCallback);
+ mBgExecutor.runAllReady();
mTestableLooper.processAllMessages();
mController.onOpActiveChanged(
AppOpsManager.opToPublicName(micOp), TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
@@ -835,6 +875,7 @@
// Add callbacks for the micOp and nonMicOp, called for the micOp active state change,
// verify the micOp is the only active op returned.
mController.addCallback(new int[]{micOp, nonMicOp}, mCallback);
+ mBgExecutor.runAllReady();
mTestableLooper.processAllMessages();
mController.onOpActiveChanged(
AppOpsManager.opToPublicName(micOp), TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
@@ -859,6 +900,7 @@
public void testCameraFilteredWhenCameraDisabled() {
mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO, AppOpsManager.OP_CAMERA},
mCallback);
+ mBgExecutor.runAllReady();
mTestableLooper.processAllMessages();
mController.onOpActiveChanged(
AppOpsManager.OPSTR_CAMERA, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
@@ -892,6 +934,7 @@
mController.addCallback(
new int[]{AppOpsManager.OP_RECORD_AUDIO, AppOpsManager.OP_PHONE_CALL_CAMERA},
mCallback);
+ mBgExecutor.runAllReady();
mTestableLooper.processAllMessages();
mController.onOpActiveChanged(
AppOpsManager.OPSTR_PHONE_CALL_CAMERA, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
@@ -922,6 +965,7 @@
private void verifyUnPausedSentActive(int micOpCode) {
mController.addCallback(new int[]{micOpCode}, mCallback);
+ mBgExecutor.runAllReady();
mTestableLooper.processAllMessages();
mController.onOpActiveChanged(AppOpsManager.opToPublicName(micOpCode), TEST_UID,
TEST_PACKAGE_NAME, true);
@@ -936,6 +980,7 @@
private void verifyAudioPausedSentInactive(int micOpCode) {
mController.addCallback(new int[]{micOpCode}, mCallback);
+ mBgExecutor.runAllReady();
mTestableLooper.processAllMessages();
mController.onOpActiveChanged(AppOpsManager.opToPublicName(micOpCode), TEST_UID_OTHER,
TEST_PACKAGE_NAME, true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
index d28f530..ddf788e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
@@ -21,6 +21,7 @@
import androidx.test.filters.SmallTest
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.data.repository.FakeCommunalRepository
import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository
import com.android.systemui.communal.shared.CommunalAppWidgetInfo
import com.android.systemui.coroutines.collectLastValue
@@ -44,6 +45,7 @@
private lateinit var testScope: TestScope
+ private lateinit var communalRepository: FakeCommunalRepository
private lateinit var widgetRepository: FakeCommunalWidgetRepository
private lateinit var interactor: CommunalInteractor
@@ -52,12 +54,13 @@
MockitoAnnotations.initMocks(this)
testScope = TestScope()
+ communalRepository = FakeCommunalRepository()
widgetRepository = FakeCommunalWidgetRepository()
- interactor = CommunalInteractor(widgetRepository)
+ interactor = CommunalInteractor(communalRepository, widgetRepository)
}
@Test
- fun testAppWidgetInfoFlow() =
+ fun appWidgetInfoFlow() =
testScope.runTest {
val lastAppWidgetInfo = collectLastValue(interactor.appWidgetInfo)
runCurrent()
@@ -67,4 +70,22 @@
runCurrent()
assertThat(lastAppWidgetInfo()).isEqualTo(stopwatchAppWidgetInfo)
}
+
+ @Test
+ fun communalEnabled() =
+ testScope.runTest {
+ communalRepository.setIsCommunalEnabled(true)
+
+ val interactor = CommunalInteractor(communalRepository, widgetRepository)
+ assertThat(interactor.isCommunalEnabled).isTrue()
+ }
+
+ @Test
+ fun communalDisabled() =
+ testScope.runTest {
+ communalRepository.setIsCommunalEnabled(false)
+
+ val interactor = CommunalInteractor(communalRepository, widgetRepository)
+ assertThat(interactor.isCommunalEnabled).isFalse()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
index 3a0883b..511562f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
@@ -21,6 +21,7 @@
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.Display
+import android.view.Display.TYPE_EXTERNAL
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.FlowValue
@@ -62,6 +63,7 @@
@Before
fun setup() {
setDisplays(emptyList())
+ setAllDisplaysIncludingDisabled()
displayRepository =
DisplayRepositoryImpl(
displayManager,
@@ -70,6 +72,7 @@
UnconfinedTestDispatcher()
)
verify(displayManager, never()).registerDisplayListener(any(), any())
+ verify(displayManager, never()).getDisplays(any())
}
@Test
@@ -351,6 +354,22 @@
}
@Test
+ fun initialState_onePendingDisplayOnBoot_notNull() =
+ testScope.runTest {
+ // 1 is not enabled, but just connected. It should be seen as pending
+ setAllDisplaysIncludingDisabled(0, 1)
+ setDisplays(0) // 0 is enabled.
+ verify(displayManager, never()).getDisplays(any())
+
+ val pendingDisplay by collectLastValue(displayRepository.pendingDisplay)
+
+ verify(displayManager).getDisplays(any())
+
+ assertThat(pendingDisplay).isNotNull()
+ assertThat(pendingDisplay!!.id).isEqualTo(1)
+ }
+
+ @Test
fun onPendingDisplay_internalDisplay_ignored() =
testScope.runTest {
val pendingDisplay by lastPendingDisplay()
@@ -365,7 +384,7 @@
testScope.runTest {
val pendingDisplay by lastPendingDisplay()
- sendOnDisplayConnected(1, Display.TYPE_EXTERNAL)
+ sendOnDisplayConnected(1, TYPE_EXTERNAL)
sendOnDisplayConnected(2, Display.TYPE_INTERNAL)
assertThat(pendingDisplay!!.id).isEqualTo(1)
@@ -416,7 +435,7 @@
whenever(displayManager.getDisplay(eq(id))).thenReturn(null)
}
- private fun sendOnDisplayConnected(id: Int, displayType: Int = Display.TYPE_EXTERNAL) {
+ private fun sendOnDisplayConnected(id: Int, displayType: Int = TYPE_EXTERNAL) {
val mockDisplay = display(id = id, type = displayType)
whenever(displayManager.getDisplay(eq(id))).thenReturn(mockDisplay)
connectedDisplayListener.value.onDisplayConnected(id)
@@ -424,15 +443,25 @@
private fun setDisplays(displays: List<Display>) {
whenever(displayManager.displays).thenReturn(displays.toTypedArray())
+ displays.forEach { display ->
+ whenever(displayManager.getDisplay(eq(display.displayId))).thenReturn(display)
+ }
+ }
+
+ private fun setAllDisplaysIncludingDisabled(vararg ids: Int) {
+ val displays = ids.map { display(type = TYPE_EXTERNAL, id = it) }.toTypedArray()
+ whenever(
+ displayManager.getDisplays(
+ eq(DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED)
+ )
+ )
+ .thenReturn(displays)
+ displays.forEach { display ->
+ whenever(displayManager.getDisplay(eq(display.displayId))).thenReturn(display)
+ }
}
private fun setDisplays(vararg ids: Int) {
- setDisplays(ids.map { display(it) })
- }
-
- private fun display(id: Int): Display {
- return mock<Display>().also { mockDisplay ->
- whenever(mockDisplay.displayId).thenReturn(id)
- }
+ setDisplays(ids.map { display(type = TYPE_EXTERNAL, id = it) })
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
index f62137c..f3c9432 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
@@ -172,7 +172,6 @@
val featureFlags =
FakeFeatureFlags().apply {
set(Flags.LOCKSCREEN_CUSTOM_CLOCKS, true)
- set(Flags.REVAMPED_WALLPAPER_UI, true)
set(Flags.WALLPAPER_FULLSCREEN_PREVIEW, true)
set(Flags.FACE_AUTH_REFACTOR, true)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index f78d051..a45b0bd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -186,8 +186,6 @@
private @Mock AuthController mAuthController;
private @Mock ShadeExpansionStateManager mShadeExpansionStateManager;
private @Mock ShadeWindowLogger mShadeWindowLogger;
- private @Captor ArgumentCaptor<KeyguardUpdateMonitorCallback>
- mKeyguardUpdateMonitorCallbackCaptor;
private @Captor ArgumentCaptor<KeyguardStateController.Callback>
mKeyguardStateControllerCallback;
private DeviceConfigProxy mDeviceConfig = new DeviceConfigProxyFake();
@@ -294,25 +292,6 @@
}
@Test
- @TestableLooper.RunWithLooper(setAsMainLooper = true)
- public void onLockdown_showKeyguard_evenIfKeyguardIsNotEnabledExternally() {
- // GIVEN keyguard is not enabled and isn't showing
- mViewMediator.onSystemReady();
- mViewMediator.setKeyguardEnabled(false);
- TestableLooper.get(this).processAllMessages();
- captureKeyguardUpdateMonitorCallback();
- assertFalse(mViewMediator.isShowingAndNotOccluded());
-
- // WHEN lockdown occurs
- when(mLockPatternUtils.isUserInLockdown(anyInt())).thenReturn(true);
- mKeyguardUpdateMonitorCallbackCaptor.getValue().onStrongAuthStateChanged(0);
-
- // THEN keyguard is shown
- TestableLooper.get(this).processAllMessages();
- assertTrue(mViewMediator.isShowingAndNotOccluded());
- }
-
- @Test
public void testOnGoingToSleep_UpdatesKeyguardGoingAway() {
mViewMediator.onStartedGoingToSleep(OFF_BECAUSE_OF_USER);
verify(mUpdateMonitor).dispatchKeyguardGoingAway(false);
@@ -1120,10 +1099,6 @@
mViewMediator.registerCentralSurfaces(mCentralSurfaces, null, null, null, null, null);
}
- private void captureKeyguardUpdateMonitorCallback() {
- verify(mUpdateMonitor).registerCallback(mKeyguardUpdateMonitorCallbackCaptor.capture());
- }
-
private void captureKeyguardStateControllerCallback() {
verify(mKeyguardStateController).addCallback(mKeyguardStateControllerCallback.capture());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
index d36e778..b9c0b7f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
@@ -20,6 +20,7 @@
import android.graphics.drawable.Drawable
import android.service.quickaccesswallet.GetWalletCardsResponse
import android.service.quickaccesswallet.QuickAccessWalletClient
+import android.service.quickaccesswallet.WalletCard
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.R
@@ -38,6 +39,7 @@
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runBlockingTest
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -92,6 +94,40 @@
}
@Test
+ fun affordance_keyguardShowing_hasNonPaymentCard_modelIsNone() =
+ runTest(UnconfinedTestDispatcher()) {
+ setUpState(cardType = WalletCard.CARD_TYPE_NON_PAYMENT)
+ var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null
+
+ val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this)
+
+ assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
+ job.cancel()
+ }
+
+ @Test
+ fun affordance_keyguardShowing_hasPaymentCard_visibleModel() =
+ runTest(UnconfinedTestDispatcher()) {
+ setUpState(cardType = WalletCard.CARD_TYPE_PAYMENT)
+ var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null
+
+ val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this)
+
+ val visibleModel = latest as KeyguardQuickAffordanceConfig.LockScreenState.Visible
+ assertThat(visibleModel.icon)
+ .isEqualTo(
+ Icon.Loaded(
+ drawable = ICON,
+ contentDescription =
+ ContentDescription.Resource(
+ res = R.string.accessibility_wallet_button,
+ ),
+ )
+ )
+ job.cancel()
+ }
+
+ @Test
fun affordance_walletFeatureNotEnabled_modelIsNone() = runBlockingTest {
setUpState(isWalletFeatureAvailable = false)
var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null
@@ -187,6 +223,7 @@
isWalletServiceAvailable: Boolean = true,
isWalletQuerySuccessful: Boolean = true,
hasSelectedCard: Boolean = true,
+ cardType: Int = WalletCard.CARD_TYPE_UNKNOWN
) {
val walletClient: QuickAccessWalletClient = mock()
whenever(walletClient.tileIcon).thenReturn(ICON)
@@ -202,7 +239,19 @@
if (isWalletQuerySuccessful) {
onWalletCardsRetrieved(
if (hasSelectedCard) {
- GetWalletCardsResponse(listOf(mock()), 0)
+ GetWalletCardsResponse(
+ listOf(
+ WalletCard.Builder(
+ /*cardId= */ CARD_ID,
+ /*cardType= */ cardType,
+ /*cardImage= */ mock(),
+ /*contentDescription= */ CARD_DESCRIPTION,
+ /*pendingIntent= */ mock()
+ )
+ .build()
+ ),
+ 0
+ )
} else {
GetWalletCardsResponse(emptyList(), 0)
}
@@ -216,5 +265,7 @@
companion object {
private val ICON: Drawable = mock()
+ private const val CARD_ID: String = "Id"
+ private const val CARD_DESCRIPTION: String = "Description"
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
index a76c885..6b194f2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
@@ -36,6 +36,7 @@
import com.android.internal.logging.UiEventLogger
import com.android.keyguard.FaceAuthUiEvent
import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN
+import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_NOTIFICATION_PANEL_CLICKED
import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.R
@@ -285,7 +286,7 @@
allPreconditionsToRunFaceAuthAreTrue()
FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER.extraInfo = 10
- underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+ underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
faceAuthenticateIsCalled()
uiEventIsLogged(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
@@ -318,12 +319,12 @@
initCollectors()
allPreconditionsToRunFaceAuthAreTrue()
- underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+ underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
faceAuthenticateIsCalled()
clearInvocations(faceManager)
clearInvocations(uiEventLogger)
- underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+ underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
verifyNoMoreInteractions(faceManager)
verifyNoMoreInteractions(uiEventLogger)
}
@@ -335,7 +336,7 @@
verify(faceManager).addLockoutResetCallback(faceLockoutResetCallback.capture())
allPreconditionsToRunFaceAuthAreTrue()
- underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+ underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
faceAuthenticateIsCalled()
authenticationCallback.value.onAuthenticationError(
@@ -389,7 +390,7 @@
initCollectors()
allPreconditionsToRunFaceAuthAreTrue()
- underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+ underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
faceAuthenticateIsCalled()
var wasAuthCancelled = false
@@ -443,7 +444,7 @@
initCollectors()
allPreconditionsToRunFaceAuthAreTrue()
- underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+ underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
faceAuthenticateIsCalled()
// Enter cancelling state
@@ -451,7 +452,7 @@
clearInvocations(faceManager)
// Auth is while cancelling.
- underTest.authenticate(FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN)
+ underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN)
// Auth is not started
verifyNoMoreInteractions(faceManager)
@@ -474,14 +475,14 @@
initCollectors()
allPreconditionsToRunFaceAuthAreTrue()
- underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+ underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
faceAuthenticateIsCalled()
clearInvocations(faceManager)
underTest.cancel()
advanceTimeBy(DeviceEntryFaceAuthRepositoryImpl.DEFAULT_CANCEL_SIGNAL_TIMEOUT + 1)
- underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+ underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
faceAuthenticateIsCalled()
}
@@ -492,7 +493,7 @@
allPreconditionsToRunFaceAuthAreTrue()
val emittedValues by collectValues(underTest.authenticationStatus)
- underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+ underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
underTest.cancel()
advanceTimeBy(100)
underTest.cancel()
@@ -519,7 +520,7 @@
initCollectors()
allPreconditionsToRunFaceAuthAreTrue()
- underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+ underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
faceAuthenticateIsCalled()
authenticationCallback.value.onAuthenticationHelp(9, "help msg")
@@ -562,8 +563,26 @@
}
@Test
- fun authenticateDoesNotRunIfFaceAuthIsCurrentlyPaused() =
- testScope.runTest { testGatingCheckForFaceAuth { underTest.pauseFaceAuth() } }
+ fun authenticateDoesNotRunIfUserSwitchingIsCurrentlyInProgress() =
+ testScope.runTest {
+ testGatingCheckForFaceAuth {
+ fakeUserRepository.setSelectedUserInfo(
+ primaryUser,
+ SelectionStatus.SELECTION_IN_PROGRESS
+ )
+ }
+ }
+
+ @Test
+ fun detectDoesNotRunIfUserSwitchingIsCurrentlyInProgress() =
+ testScope.runTest {
+ testGatingCheckForDetect {
+ fakeUserRepository.setSelectedUserInfo(
+ userInfo = primaryUser,
+ selectionStatus = SelectionStatus.SELECTION_IN_PROGRESS
+ )
+ }
+ }
@Test
fun authenticateDoesNotRunIfKeyguardIsNotShowing() =
@@ -582,12 +601,6 @@
testScope.runTest { testGatingCheckForFaceAuth { underTest.setLockedOut(true) } }
@Test
- fun authenticateDoesNotRunWhenUserIsCurrentlyTrusted() =
- testScope.runTest {
- testGatingCheckForFaceAuth { trustRepository.setCurrentUserTrusted(true) }
- }
-
- @Test
fun authenticateDoesNotRunWhenKeyguardIsGoingAway() =
testScope.runTest {
testGatingCheckForFaceAuth { keyguardRepository.setKeyguardGoingAway(true) }
@@ -608,14 +621,6 @@
}
@Test
- fun authenticateDoesNotRunWhenFaceAuthIsNotCurrentlyAllowedToRun() =
- testScope.runTest {
- testGatingCheckForFaceAuth {
- biometricSettingsRepository.setIsFaceAuthCurrentlyAllowed(false)
- }
- }
-
- @Test
fun authenticateDoesNotRunWhenSecureCameraIsActive() =
testScope.runTest {
testGatingCheckForFaceAuth {
@@ -672,7 +677,7 @@
// Flip one precondition to false.
biometricSettingsRepository.setIsFaceAuthCurrentlyAllowed(false)
assertThat(canFaceAuthRun()).isFalse()
- underTest.authenticate(
+ underTest.requestAuthenticate(
FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER,
fallbackToDetection = true
)
@@ -693,7 +698,7 @@
trustRepository.setCurrentUserTrusted(true)
assertThat(canFaceAuthRun()).isFalse()
- underTest.authenticate(
+ underTest.requestAuthenticate(
FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER,
fallbackToDetection = true
)
@@ -884,10 +889,6 @@
}
@Test
- fun detectDoesNotRunWhenUserSwitchingInProgress() =
- testScope.runTest { testGatingCheckForDetect { underTest.pauseFaceAuth() } }
-
- @Test
fun detectDoesNotRunWhenKeyguardGoingAway() =
testScope.runTest {
testGatingCheckForDetect { keyguardRepository.setKeyguardGoingAway(true) }
@@ -1075,6 +1076,28 @@
faceAuthenticateIsCalled()
}
+ @Test
+ fun queuedAuthOnlyRequestShouldNotBeProcessedIfOnlyDetectionCanBeRun() =
+ testScope.runTest {
+ initCollectors()
+ allPreconditionsToRunFaceAuthAreTrue()
+
+ // This will prevent auth from running but not detection
+ biometricSettingsRepository.setIsFaceAuthCurrentlyAllowed(false)
+
+ runCurrent()
+ assertThat(canFaceAuthRun()).isFalse()
+
+ underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_NOTIFICATION_PANEL_CLICKED, false)
+ runCurrent()
+
+ faceDetectIsNotCalled()
+ faceAuthenticateIsNotCalled()
+
+ biometricSettingsRepository.setIsFaceAuthCurrentlyAllowed(true)
+ faceAuthenticateIsCalled()
+ }
+
private suspend fun TestScope.testGatingCheckForFaceAuth(
gatingCheckModifier: suspend () -> Unit
) {
@@ -1087,10 +1110,18 @@
// gating check doesn't allow face auth to run.
assertThat(underTest.canRunFaceAuth.value).isFalse()
+ // request face auth just before gating conditions become true, this ensures any race
+ // conditions won't prevent face auth from running
+ underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER, false)
+ faceAuthenticateIsNotCalled()
+
// flip the gating check back on.
allPreconditionsToRunFaceAuthAreTrue()
+ assertThat(underTest.canRunFaceAuth.value).isTrue()
- triggerFaceAuth(false)
+ faceAuthenticateIsCalled()
+ assertThat(authRunning()).isTrue()
+ cancellationSignal.value.setOnCancelListener { wasAuthCancelled = true }
// Flip gating check off
gatingCheckModifier()
@@ -1101,13 +1132,17 @@
clearInvocations(faceManager)
// Try auth again
- underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+ underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+
+ runCurrent()
// Auth can't run again
faceAuthenticateIsNotCalled()
}
- private suspend fun TestScope.testGatingCheckForDetect(gatingCheckModifier: () -> Unit) {
+ private suspend fun TestScope.testGatingCheckForDetect(
+ gatingCheckModifier: suspend () -> Unit
+ ) {
initCollectors()
allPreconditionsToRunFaceAuthAreTrue()
@@ -1118,7 +1153,11 @@
assertThat(canFaceAuthRun()).isFalse()
// Trigger authenticate with detection fallback
- underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER, fallbackToDetection = true)
+ underTest.requestAuthenticate(
+ FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER,
+ fallbackToDetection = true
+ )
+ runCurrent()
faceAuthenticateIsNotCalled()
faceDetectIsCalled()
@@ -1133,15 +1172,21 @@
clearInvocations(faceManager)
// Try to run detect again
- underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER, fallbackToDetection = true)
+ underTest.requestAuthenticate(
+ FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER,
+ fallbackToDetection = true
+ )
// Detect won't run because preconditions are not true anymore.
faceDetectIsNotCalled()
}
- private suspend fun triggerFaceAuth(fallbackToDetect: Boolean) {
+ private fun TestScope.triggerFaceAuth(fallbackToDetect: Boolean) {
assertThat(canFaceAuthRun()).isTrue()
- underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER, fallbackToDetect)
+ underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER, fallbackToDetect)
+
+ runCurrent()
+
faceAuthenticateIsCalled()
assertThat(authRunning()).isTrue()
cancellationSignal.value.setOnCancelListener { wasAuthCancelled = true }
@@ -1150,7 +1195,6 @@
private suspend fun TestScope.allPreconditionsToRunFaceAuthAreTrue() {
verify(faceManager, atLeastOnce())
.addLockoutResetCallback(faceLockoutResetCallback.capture())
- underTest.resumeFaceAuth()
trustRepository.setCurrentUserTrusted(false)
keyguardRepository.setKeyguardGoingAway(false)
keyguardRepository.setWakefulnessModel(
@@ -1164,7 +1208,7 @@
biometricSettingsRepository.setIsFaceAuthSupportedInCurrentPosture(true)
biometricSettingsRepository.setIsFaceAuthCurrentlyAllowed(true)
biometricSettingsRepository.setIsUserInLockdown(false)
- fakeUserRepository.setSelectedUserInfo(primaryUser)
+ fakeUserRepository.setSelectedUserInfo(primaryUser, SelectionStatus.SELECTION_COMPLETE)
faceLockoutResetCallback.value.onLockoutReset(0)
bouncerRepository.setAlternateVisible(true)
keyguardRepository.setKeyguardShowing(true)
@@ -1187,7 +1231,9 @@
private fun successResult() = FaceManager.AuthenticationResult(null, null, primaryUserId, false)
- private fun faceDetectIsCalled() {
+ private fun TestScope.faceDetectIsCalled() {
+ runCurrent()
+
verify(faceManager)
.detectFace(
cancellationSignal.capture(),
@@ -1196,7 +1242,9 @@
)
}
- private fun faceAuthenticateIsCalled() {
+ private fun TestScope.faceAuthenticateIsCalled() {
+ runCurrent()
+
verify(faceManager)
.authenticate(
isNull(),
@@ -1207,7 +1255,9 @@
)
}
- private fun faceAuthenticateIsNotCalled() {
+ private fun TestScope.faceAuthenticateIsNotCalled() {
+ runCurrent()
+
verify(faceManager, never())
.authenticate(
isNull(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
index da70a9f..2ed9de2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
@@ -224,23 +224,7 @@
}
@Test
- fun faceAuthIsPausedWhenUserSwitchingIsInProgress() =
- testScope.runTest {
- underTest.start()
-
- fakeUserRepository.setSelectedUserInfo(primaryUser, SelectionStatus.SELECTION_COMPLETE)
- runCurrent()
- fakeUserRepository.setSelectedUserInfo(
- secondaryUser,
- SelectionStatus.SELECTION_IN_PROGRESS
- )
- runCurrent()
-
- assertThat(faceAuthRepository.isFaceAuthPaused()).isTrue()
- }
-
- @Test
- fun faceAuthIsUnpausedWhenUserSwitchingIsInComplete() =
+ fun faceAuthLockedOutStateIsUpdatedAfterUserSwitch() =
testScope.runTest {
underTest.start()
@@ -251,7 +235,6 @@
SelectionStatus.SELECTION_IN_PROGRESS
)
runCurrent()
- assertThat(faceAuthRepository.isFaceAuthPaused()).isTrue()
bouncerRepository.setPrimaryShow(true)
// New user is not locked out.
@@ -262,7 +245,6 @@
)
runCurrent()
- assertThat(faceAuthRepository.isFaceAuthPaused()).isFalse()
assertThat(faceAuthRepository.isLockedOut.value).isFalse()
runCurrent()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
index 14cdf2f..90e217f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
@@ -217,6 +217,7 @@
fromScene = SceneKey.Gone,
toScene = SceneKey.Lockscreen,
progress = flowOf(0f),
+ isUserInputDriven = false,
)
runCurrent()
assertThat(isAnimate).isFalse()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
index 0050d64..0bbeeff 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
@@ -302,7 +302,6 @@
featureFlags =
FakeFeatureFlags().apply {
set(Flags.LOCK_SCREEN_LONG_PRESS_ENABLED, isLongPressFeatureEnabled)
- set(Flags.REVAMPED_WALLPAPER_UI, isRevampedWppFeatureEnabled)
set(Flags.LOCK_SCREEN_LONG_PRESS_DIRECT_TO_WPP, isOpenWppDirectlyEnabled)
},
broadcastDispatcher = fakeBroadcastDispatcher,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
index 681fce8..7f4755d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
@@ -23,8 +23,6 @@
import androidx.constraintlayout.widget.ConstraintSet
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
import com.android.systemui.keyguard.shared.model.KeyguardSection
import com.android.systemui.keyguard.ui.view.KeyguardRootView
@@ -35,6 +33,7 @@
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultNotificationStackScrollLayoutSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultSettingsPopupMenuSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultShortcutsSection
+import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusBarSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusViewSection
import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeGuidelines
import com.android.systemui.util.mockito.whenever
@@ -60,12 +59,11 @@
private lateinit var defaultAmbientIndicationAreaSection: DefaultAmbientIndicationAreaSection
@Mock private lateinit var defaultSettingsPopupMenuSection: DefaultSettingsPopupMenuSection
@Mock private lateinit var defaultStatusViewSection: DefaultStatusViewSection
+ @Mock private lateinit var defaultStatusBarViewSection: DefaultStatusBarSection
@Mock private lateinit var defaultNSSLSection: DefaultNotificationStackScrollLayoutSection
@Mock private lateinit var splitShadeGuidelines: SplitShadeGuidelines
@Mock private lateinit var aodNotificationIconsSection: AodNotificationIconsSection
- private val featureFlags = FakeFeatureFlags()
-
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
@@ -78,26 +76,17 @@
defaultAmbientIndicationAreaSection,
defaultSettingsPopupMenuSection,
defaultStatusViewSection,
+ defaultStatusBarViewSection,
defaultNSSLSection,
splitShadeGuidelines,
aodNotificationIconsSection,
- featureFlags,
)
- featureFlags.set(Flags.LAZY_INFLATE_KEYGUARD, false)
}
@Test
fun replaceViews() {
val constraintLayout = ConstraintLayout(context, null)
underTest.replaceViews(null, constraintLayout)
- underTest.sections.forEach { verify(it, never()).addViews(constraintLayout) }
- }
-
- @Test
- fun replaceViews_lazyInflateFlagOn() {
- featureFlags.set(Flags.LAZY_INFLATE_KEYGUARD, true)
- val constraintLayout = ConstraintLayout(context, null)
- underTest.replaceViews(null, constraintLayout)
underTest.sections.forEach { verify(it).addViews(constraintLayout) }
}
@@ -107,7 +96,6 @@
val someSection = mock(KeyguardSection::class.java)
whenever(prevBlueprint.sections)
.thenReturn(underTest.sections.minus(defaultLockIconSection).plus(someSection))
- featureFlags.set(Flags.LAZY_INFLATE_KEYGUARD, true)
val constraintLayout = ConstraintLayout(context, null)
underTest.replaceViews(prevBlueprint, constraintLayout)
underTest.sections.minus(defaultLockIconSection).forEach {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
index b30dc9c..1681cfd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
@@ -42,14 +42,7 @@
repository = utils.authenticationRepository(),
)
- private val underTest =
- LockscreenSceneViewModel(
- authenticationInteractor = authenticationInteractor,
- longPress =
- KeyguardLongPressViewModel(
- interactor = mock(),
- ),
- )
+ private val underTest = createLockscreenSceneViewModel()
@Test
fun upTransitionSceneKey_canSwipeToUnlock_gone() =
@@ -73,4 +66,34 @@
assertThat(upTransitionSceneKey).isEqualTo(SceneKey.Bouncer)
}
+
+ @Test
+ fun leftTransitionSceneKey_communalIsEnabled_communal() =
+ testScope.runTest {
+ utils.communalRepository.setIsCommunalEnabled(true)
+ val underTest = createLockscreenSceneViewModel()
+
+ assertThat(underTest.leftDestinationSceneKey).isEqualTo(SceneKey.Communal)
+ }
+
+ @Test
+ fun leftTransitionSceneKey_communalIsDisabled_null() =
+ testScope.runTest {
+ utils.communalRepository.setIsCommunalEnabled(false)
+ val underTest = createLockscreenSceneViewModel()
+
+ assertThat(underTest.leftDestinationSceneKey).isNull()
+ }
+
+ private fun createLockscreenSceneViewModel(): LockscreenSceneViewModel {
+ return LockscreenSceneViewModel(
+ applicationScope = testScope.backgroundScope,
+ authenticationInteractor = authenticationInteractor,
+ communalInteractor = utils.communalInteractor(),
+ longPress =
+ KeyguardLongPressViewModel(
+ interactor = mock(),
+ ),
+ )
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java
similarity index 97%
rename from packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java
index 89405c1..c835146 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java
@@ -74,11 +74,11 @@
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
@SmallTest
-public class NavigationBarControllerTest extends SysuiTestCase {
+public class NavigationBarControllerImplTest extends SysuiTestCase {
private static final int SECONDARY_DISPLAY = 1;
- private NavigationBarController mNavigationBarController;
+ private NavigationBarControllerImpl mNavigationBarController;
private NavigationBar mDefaultNavBar;
private NavigationBar mSecondaryNavBar;
private StaticMockitoSession mMockitoSession;
@@ -95,7 +95,7 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
mNavigationBarController = spy(
- new NavigationBarController(mContext,
+ new NavigationBarControllerImpl(mContext,
mock(OverviewProxyService.class),
mock(NavigationModeController.class),
mock(SysUiState.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarInflaterViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarInflaterViewTest.java
index 7369c82..52d02b6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarInflaterViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarInflaterViewTest.java
@@ -33,8 +33,6 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.navigationbar.buttons.ButtonDispatcher;
-import com.android.systemui.navigationbar.NavigationBarInflaterView;
-import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.recents.OverviewProxyService;
import org.junit.After;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
index 1536c17..b50032f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
@@ -73,6 +73,7 @@
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runCurrent
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -705,12 +706,12 @@
fun updateNoteTaskAsUser_sameUser_shouldUpdateShortcuts() {
val user = UserHandle.CURRENT
val controller = spy(createNoteTaskController())
- doNothing().whenever(controller).updateNoteTaskAsUserInternal(any())
+ doNothing().whenever(controller).launchUpdateNoteTaskAsUser(any())
whenever(controller.getCurrentRunningUser()).thenReturn(user)
controller.updateNoteTaskAsUser(user)
- verify(controller).updateNoteTaskAsUserInternal(user)
+ verify(controller).launchUpdateNoteTaskAsUser(user)
verify(context, never()).startServiceAsUser(any(), any())
}
@@ -718,12 +719,12 @@
fun updateNoteTaskAsUser_differentUser_shouldUpdateShortcutsInUserProcess() {
val user = UserHandle.CURRENT
val controller = spy(createNoteTaskController(isEnabled = true))
- doNothing().whenever(controller).updateNoteTaskAsUserInternal(any())
+ doNothing().whenever(controller).launchUpdateNoteTaskAsUser(any())
whenever(controller.getCurrentRunningUser()).thenReturn(UserHandle.SYSTEM)
controller.updateNoteTaskAsUser(user)
- verify(controller, never()).updateNoteTaskAsUserInternal(any())
+ verify(controller, never()).launchUpdateNoteTaskAsUser(any())
val intent = withArgCaptor { verify(context).startServiceAsUser(capture(), eq(user)) }
assertThat(intent).hasComponentClass(NoteTaskControllerUpdateService::class.java)
}
@@ -733,7 +734,8 @@
@Test
fun updateNoteTaskAsUserInternal_withNotesRole_withShortcuts_shouldUpdateShortcuts() {
createNoteTaskController(isEnabled = true)
- .updateNoteTaskAsUserInternal(userTracker.userHandle)
+ .launchUpdateNoteTaskAsUser(userTracker.userHandle)
+ testScope.runCurrent()
val actualComponent = argumentCaptor<ComponentName>()
verify(context.packageManager)
@@ -768,7 +770,8 @@
.thenReturn(emptyList())
createNoteTaskController(isEnabled = true)
- .updateNoteTaskAsUserInternal(userTracker.userHandle)
+ .launchUpdateNoteTaskAsUser(userTracker.userHandle)
+ testScope.runCurrent()
val argument = argumentCaptor<ComponentName>()
verify(context.packageManager)
@@ -787,7 +790,8 @@
@Test
fun updateNoteTaskAsUserInternal_flagDisabled_shouldDisableShortcuts() {
createNoteTaskController(isEnabled = false)
- .updateNoteTaskAsUserInternal(userTracker.userHandle)
+ .launchUpdateNoteTaskAsUser(userTracker.userHandle)
+ testScope.runCurrent()
val argument = argumentCaptor<ComponentName>()
verify(context.packageManager)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
index 95bb3e0..7833007 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
@@ -22,6 +22,8 @@
import android.testing.AndroidTestingRunner
import android.view.KeyEvent
import android.view.KeyEvent.ACTION_DOWN
+import android.view.KeyEvent.ACTION_UP
+import android.view.KeyEvent.KEYCODE_N
import android.view.KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardUpdateMonitor
@@ -30,7 +32,6 @@
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
@@ -43,7 +44,6 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.Mockito.never
import org.mockito.Mockito.times
@@ -66,6 +66,7 @@
private val executor = FakeExecutor(FakeSystemClock())
private val userTracker = FakeUserTracker()
+ private val handlerCallbacks = mutableListOf<Runnable>()
@Before
fun setUp() {
@@ -74,19 +75,27 @@
}
private fun createUnderTest(
- isEnabled: Boolean,
- bubbles: Bubbles?,
+ isEnabled: Boolean,
+ bubbles: Bubbles?,
): NoteTaskInitializer =
- NoteTaskInitializer(
- controller = controller,
- commandQueue = commandQueue,
- optionalBubbles = Optional.ofNullable(bubbles),
- isEnabled = isEnabled,
- roleManager = roleManager,
- userTracker = userTracker,
- keyguardUpdateMonitor = keyguardMonitor,
- backgroundExecutor = executor,
- )
+ NoteTaskInitializer(
+ controller = controller,
+ commandQueue = commandQueue,
+ optionalBubbles = Optional.ofNullable(bubbles),
+ isEnabled = isEnabled,
+ roleManager = roleManager,
+ userTracker = userTracker,
+ keyguardUpdateMonitor = keyguardMonitor,
+ backgroundExecutor = executor,
+ )
+
+ private fun createKeyEvent(
+ action: Int,
+ code: Int,
+ downTime: Long = 0L,
+ eventTime: Long = 0L,
+ metaState: Int = 0
+ ): KeyEvent = KeyEvent(downTime, eventTime, action, code, 0 /*repeat*/, metaState)
@Test
fun initialize_withUserUnlocked() {
@@ -120,12 +129,12 @@
underTest.initialize()
verifyZeroInteractions(
- commandQueue,
- bubbles,
- controller,
- roleManager,
- userManager,
- keyguardMonitor,
+ commandQueue,
+ bubbles,
+ controller,
+ roleManager,
+ userManager,
+ keyguardMonitor,
)
}
@@ -136,18 +145,23 @@
underTest.initialize()
verifyZeroInteractions(
- commandQueue,
- bubbles,
- controller,
- roleManager,
- userManager,
- keyguardMonitor,
+ commandQueue,
+ bubbles,
+ controller,
+ roleManager,
+ userManager,
+ keyguardMonitor,
)
}
@Test
fun initialize_handleSystemKey() {
- val expectedKeyEvent = KeyEvent(ACTION_DOWN, KEYCODE_STYLUS_BUTTON_TAIL)
+ val expectedKeyEvent =
+ createKeyEvent(
+ ACTION_DOWN,
+ KEYCODE_N,
+ metaState = KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON
+ )
val underTest = createUnderTest(isEnabled = true, bubbles = bubbles)
underTest.initialize()
val callback = withArgCaptor { verify(commandQueue).addCallback(capture()) }
@@ -176,7 +190,7 @@
underTest.initialize()
val callback = withArgCaptor {
verify(roleManager)
- .addOnRoleHoldersChangedListenerAsUser(any(), capture(), eq(UserHandle.ALL))
+ .addOnRoleHoldersChangedListenerAsUser(any(), capture(), eq(UserHandle.ALL))
}
callback.onRoleHoldersChanged(ROLE_NOTES, userTracker.userHandle)
@@ -203,4 +217,60 @@
verify(controller, times(2)).updateNoteTaskForCurrentUserAndManagedProfiles()
}
+
+ @Test
+ fun tailButtonGestureDetection_singlePress_shouldShowNoteTaskOnUp() {
+ val underTest = createUnderTest(isEnabled = true, bubbles = bubbles)
+ underTest.initialize()
+ val callback = withArgCaptor { verify(commandQueue).addCallback(capture()) }
+
+ callback.handleSystemKey(
+ createKeyEvent(ACTION_DOWN, KEYCODE_STYLUS_BUTTON_TAIL, downTime = 0, eventTime = 0)
+ )
+ verify(controller, never()).showNoteTask(any())
+
+ callback.handleSystemKey(
+ createKeyEvent(ACTION_UP, KEYCODE_STYLUS_BUTTON_TAIL, downTime = 0, eventTime = 50)
+ )
+
+ verify(controller).showNoteTask(any())
+ }
+
+ @Test
+ fun tailButtonGestureDetection_doublePress_shouldNotShowNoteTaskTwice() {
+ val underTest = createUnderTest(isEnabled = true, bubbles = bubbles)
+ underTest.initialize()
+ val callback = withArgCaptor { verify(commandQueue).addCallback(capture()) }
+
+ callback.handleSystemKey(
+ createKeyEvent(ACTION_DOWN, KEYCODE_STYLUS_BUTTON_TAIL, downTime = 0, eventTime = 0)
+ )
+ callback.handleSystemKey(
+ createKeyEvent(ACTION_UP, KEYCODE_STYLUS_BUTTON_TAIL, downTime = 0, eventTime = 50)
+ )
+ callback.handleSystemKey(
+ createKeyEvent(ACTION_DOWN, KEYCODE_STYLUS_BUTTON_TAIL, downTime = 99, eventTime = 99)
+ )
+ callback.handleSystemKey(
+ createKeyEvent(ACTION_UP, KEYCODE_STYLUS_BUTTON_TAIL, downTime = 99, eventTime = 150)
+ )
+
+ verify(controller, times(1)).showNoteTask(any())
+ }
+
+ @Test
+ fun tailButtonGestureDetection_longPress_shouldNotShowNoteTask() {
+ val underTest = createUnderTest(isEnabled = true, bubbles = bubbles)
+ underTest.initialize()
+ val callback = withArgCaptor { verify(commandQueue).addCallback(capture()) }
+
+ callback.handleSystemKey(
+ createKeyEvent(ACTION_DOWN, KEYCODE_STYLUS_BUTTON_TAIL, downTime = 0, eventTime = 0)
+ )
+ callback.handleSystemKey(
+ createKeyEvent(ACTION_UP, KEYCODE_STYLUS_BUTTON_TAIL, downTime = 0, eventTime = 1000)
+ )
+
+ verify(controller, never()).showNoteTask(any())
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
index b00ae39..dd55bad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
@@ -395,7 +395,7 @@
PendingIntent.getActivity(mContext, 0, mWalletIntent, PendingIntent.FLAG_IMMUTABLE);
WalletCard walletCard =
new WalletCard.Builder(
- CARD_ID, mCardImage, CARD_DESCRIPTION, pendingIntent).build();
+ CARD_ID, mCardImage, CARD_DESCRIPTION, pendingIntent).build();
GetWalletCardsResponse response =
new GetWalletCardsResponse(Collections.singletonList(walletCard), 0);
@@ -410,6 +410,22 @@
}
@Test
+ public void testQueryCards_cardDataPayment_updateSideViewDrawable() {
+ when(mKeyguardStateController.isUnlocked()).thenReturn(true);
+ setUpWalletCardWithType(/* hasCard =*/ true, WalletCard.CARD_TYPE_PAYMENT);
+
+ assertNotNull(mTile.getState().sideViewCustomDrawable);
+ }
+
+ @Test
+ public void testQueryCards_cardDataNonPayment_updateSideViewDrawable() {
+ when(mKeyguardStateController.isUnlocked()).thenReturn(true);
+ setUpWalletCardWithType(/* hasCard =*/ true, WalletCard.CARD_TYPE_NON_PAYMENT);
+
+ assertNull(mTile.getState().sideViewCustomDrawable);
+ }
+
+ @Test
public void testQueryCards_noCards_notUpdateSideViewDrawable() {
setUpWalletCard(/* hasCard= */ false);
@@ -438,6 +454,29 @@
verifyZeroInteractions(mQuickAccessWalletClient);
}
+ private WalletCard createWalletCardWithType(Context context, int cardType) {
+ PendingIntent pendingIntent =
+ PendingIntent.getActivity(context, 0, mWalletIntent, PendingIntent.FLAG_IMMUTABLE);
+ return new WalletCard.Builder(CARD_ID, cardType, CARD_IMAGE, CARD_DESCRIPTION,
+ pendingIntent).build();
+ }
+
+ private void setUpWalletCardWithType(boolean hasCard, int cardType) {
+ GetWalletCardsResponse response =
+ new GetWalletCardsResponse(
+ hasCard
+ ? Collections.singletonList(
+ createWalletCardWithType(mContext, cardType))
+ : Collections.EMPTY_LIST, 0);
+
+ mTile.handleSetListening(true);
+
+ verify(mController).queryWalletCards(mCallbackCaptor.capture());
+
+ mCallbackCaptor.getValue().onWalletCardsRetrieved(response);
+ mTestableLooper.processAllMessages();
+ }
+
private void setUpWalletCard(boolean hasCard) {
GetWalletCardsResponse response =
new GetWalletCardsResponse(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/QSTileIntentUserActionHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/QSTileIntentUserActionHandlerTest.kt
new file mode 100644
index 0000000..47b4244
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/QSTileIntentUserActionHandlerTest.kt
@@ -0,0 +1,41 @@
+package com.android.systemui.qs.tiles.base
+
+import android.content.Intent
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserActionHandler
+import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.any
+import org.mockito.Mockito.eq
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class QSTileIntentUserActionHandlerTest : SysuiTestCase() {
+
+ @Mock private lateinit var activityStarted: ActivityStarter
+
+ lateinit var underTest: QSTileIntentUserActionHandler
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ underTest = QSTileIntentUserActionHandler(activityStarted)
+ }
+
+ @Test
+ fun testPassesIntentToStarter() {
+ val intent = Intent("test.ACTION")
+
+ underTest.handle(QSTileUserAction.Click(context, null), intent)
+
+ verify(activityStarted).postStartActivityDismissingKeyguard(eq(intent), eq(0), any())
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt
new file mode 100644
index 0000000..643866e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt
@@ -0,0 +1,90 @@
+package com.android.systemui.qs.tiles.viewmodel
+
+import android.graphics.drawable.Icon
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.MediumTest
+import com.android.systemui.RoboPilotTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.tiles.base.interactor.FakeQSTileDataInteractor
+import com.android.systemui.qs.tiles.base.interactor.FakeQSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataRequest
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.base.interactor.StateUpdateTrigger
+import com.android.systemui.qs.tiles.base.viewmodel.BaseQSTileViewModel
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+// TODO(b/299909368): Add more tests
+@MediumTest
+@RoboPilotTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class QSTileViewModelInterfaceComplianceTest : SysuiTestCase() {
+
+ private val fakeQSTileDataInteractor = FakeQSTileDataInteractor<Any>()
+ private val fakeQSTileUserActionInteractor = FakeQSTileUserActionInteractor<Any>()
+
+ private val testCoroutineDispatcher = StandardTestDispatcher()
+ private val testScope = TestScope(testCoroutineDispatcher)
+
+ private lateinit var underTest: QSTileViewModel
+
+ @Before
+ fun setup() {
+ underTest = createViewModel(testScope)
+ }
+
+ @Test
+ fun testDoesntListenStateUntilCreated() =
+ testScope.runTest {
+ assertThat(fakeQSTileDataInteractor.dataRequests).isEmpty()
+
+ underTest.onLifecycle(QSTileLifecycle.ALIVE)
+ underTest.onUserIdChanged(1)
+
+ assertThat(fakeQSTileDataInteractor.dataRequests).isEmpty()
+
+ underTest.state.launchIn(backgroundScope)
+ runCurrent()
+
+ assertThat(fakeQSTileDataInteractor.dataRequests).isNotEmpty()
+ assertThat(fakeQSTileDataInteractor.dataRequests.first())
+ .isEqualTo(QSTileDataRequest(1, StateUpdateTrigger.InitialRequest))
+ }
+
+ private fun createViewModel(
+ scope: TestScope,
+ config: QSTileConfig = TEST_QS_TILE_CONFIG,
+ ): QSTileViewModel =
+ object :
+ BaseQSTileViewModel<Any>(
+ config,
+ fakeQSTileUserActionInteractor,
+ fakeQSTileDataInteractor,
+ object : QSTileDataToStateMapper<Any> {
+ override fun map(config: QSTileConfig, data: Any): QSTileState {
+ return QSTileState(config.tileIcon, config.tileLabel)
+ }
+ },
+ testCoroutineDispatcher,
+ tileScope = scope.backgroundScope,
+ ) {}
+
+ private companion object {
+ val TEST_QS_TILE_CONFIG =
+ QSTileConfig(
+ TileSpec.create("default"),
+ Icon.createWithContentUri(""),
+ "",
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
index e353a53..6d358db 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
@@ -48,7 +48,6 @@
import com.android.systemui.shared.system.QuickStepContract.WAKEFULNESS_WAKING
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.NotificationShadeWindowController
-import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.unfold.progress.UnfoldTransitionProgressForwarder
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
@@ -94,8 +93,8 @@
@Mock private lateinit var commandQueue: CommandQueue
@Mock private lateinit var shellInterface: ShellInterface
@Mock private lateinit var navBarController: NavigationBarController
- @Mock private lateinit var centralSurfaces: CentralSurfaces
@Mock private lateinit var shadeViewController: ShadeViewController
+ @Mock private lateinit var screenPinningRequest: ScreenPinningRequest
@Mock private lateinit var navModeController: NavigationModeController
@Mock private lateinit var statusBarWinController: NotificationShadeWindowController
@Mock private lateinit var userTracker: UserTracker
@@ -134,8 +133,8 @@
commandQueue,
shellInterface,
{ navBarController },
- { Optional.of(centralSurfaces) },
{ shadeViewController },
+ screenPinningRequest,
navModeController,
statusBarWinController,
sysUiState,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index 141fcbb..9c8d14d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -99,6 +99,8 @@
sceneInteractor = sceneInteractor,
)
+ private val communalInteractor = utils.communalInteractor()
+
private val transitionState =
MutableStateFlow<ObservableTransitionState>(
ObservableTransitionState.Idle(sceneContainerConfig.initialSceneKey)
@@ -123,7 +125,9 @@
private val lockscreenSceneViewModel =
LockscreenSceneViewModel(
+ applicationScope = testScope.backgroundScope,
authenticationInteractor = authenticationInteractor,
+ communalInteractor = communalInteractor,
longPress =
KeyguardLongPressViewModel(
interactor = mock(),
@@ -432,6 +436,7 @@
fromScene = getCurrentSceneInUi(),
toScene = to.key,
progress = progressFlow,
+ isUserInputDriven = false,
)
runCurrent()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
index 181f8a7..ff28d2d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
@@ -119,6 +119,7 @@
fromScene = SceneKey.Lockscreen,
toScene = SceneKey.Shade,
progress = progress,
+ isUserInputDriven = false,
)
assertThat(reflectedTransitionState).isEqualTo(transitionState.value)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
index ed716a9..afc0e69 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
@@ -86,6 +86,7 @@
fromScene = SceneKey.Lockscreen,
toScene = SceneKey.Shade,
progress = progress,
+ isUserInputDriven = false,
)
assertThat(reflectedTransitionState).isEqualTo(transitionState.value)
@@ -123,6 +124,7 @@
fromScene = underTest.desiredScene.value.key,
toScene = SceneKey.Shade,
progress = progress,
+ isUserInputDriven = false,
)
assertThat(transitionTo).isEqualTo(SceneKey.Shade)
@@ -158,7 +160,8 @@
ObservableTransitionState.Transition(
fromScene = SceneKey.Gone,
toScene = SceneKey.Lockscreen,
- progress = flowOf(0.5f)
+ progress = flowOf(0.5f),
+ isUserInputDriven = false,
)
)
val transitioning by
@@ -176,7 +179,8 @@
ObservableTransitionState.Transition(
fromScene = SceneKey.Shade,
toScene = SceneKey.QuickSettings,
- progress = flowOf(0.5f)
+ progress = flowOf(0.5f),
+ isUserInputDriven = false,
)
)
underTest.setTransitionState(transitionState)
@@ -192,7 +196,8 @@
ObservableTransitionState.Transition(
fromScene = SceneKey.Shade,
toScene = SceneKey.Lockscreen,
- progress = flowOf(0.5f)
+ progress = flowOf(0.5f),
+ isUserInputDriven = false,
)
)
val transitioning by
@@ -219,7 +224,8 @@
ObservableTransitionState.Transition(
fromScene = SceneKey.Shade,
toScene = SceneKey.Lockscreen,
- progress = flowOf(0.5f)
+ progress = flowOf(0.5f),
+ isUserInputDriven = false,
)
assertThat(transitioning).isTrue()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
index 145629a..16fdf8e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
@@ -109,6 +109,7 @@
fromScene = SceneKey.Gone,
toScene = SceneKey.Shade,
progress = flowOf(0.5f),
+ isUserInputDriven = false,
)
assertThat(isVisible).isTrue()
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Shade), "reason")
@@ -121,6 +122,7 @@
fromScene = SceneKey.Shade,
toScene = SceneKey.Gone,
progress = flowOf(0.5f),
+ isUserInputDriven = false,
)
assertThat(isVisible).isTrue()
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone), "reason")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt
index 17ee3a1..f6195aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt
@@ -78,8 +78,8 @@
@JvmStatic
fun testCases() = buildList {
repeat(4) { combination ->
- val isComposeAvailable = combination and 0b100 != 0
- val areAllFlagsSet = combination and 0b001 != 0
+ val isComposeAvailable = combination and 0b10 != 0
+ val areAllFlagsSet = combination and 0b01 != 0
val expectedEnabled = isComposeAvailable && areAllFlagsSet
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordDialogTest.kt
deleted file mode 100644
index 03d9444..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordDialogTest.kt
+++ /dev/null
@@ -1,111 +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.
- */
-
-/*
- * 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.screenrecord
-
-import android.testing.AndroidTestingRunner
-import android.testing.TestableLooper
-import android.view.View
-import androidx.test.filters.SmallTest
-import com.android.systemui.R
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.animation.DialogLaunchAnimator
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
-import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.settings.UserContextProvider
-import com.google.common.truth.Truth.assertThat
-import org.junit.After
-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` as whenever
-
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
-class ScreenRecordDialogTest : SysuiTestCase() {
-
- @Mock
- private lateinit var starter: ActivityStarter
- @Mock
- private lateinit var controller: RecordingController
- @Mock
- private lateinit var userContextProvider: UserContextProvider
- @Mock
- private lateinit var flags: FeatureFlags
- @Mock
- private lateinit var dialogLaunchAnimator: DialogLaunchAnimator
- @Mock
- private lateinit var onStartRecordingClicked: Runnable
-
- private lateinit var dialog: ScreenRecordDialog
-
- @Before
- fun setUp() {
- MockitoAnnotations.initMocks(this)
-
- dialog = ScreenRecordDialog(
- context, controller, starter, userContextProvider, flags, dialogLaunchAnimator,
- onStartRecordingClicked
- )
- }
-
- @After
- fun teardown() {
- if (::dialog.isInitialized) {
- dialog.dismiss()
- }
- }
-
- @Test
- fun testShowDialog_partialScreenSharingDisabled_appButtonIsNotVisible() {
- whenever(flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)).thenReturn(false)
-
- dialog.show()
-
- val visibility = dialog.requireViewById<View>(R.id.button_app).visibility
- assertThat(visibility).isEqualTo(View.GONE)
- }
-
- @Test
- fun testShowDialog_partialScreenSharingEnabled_appButtonIsVisible() {
- whenever(flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)).thenReturn(true)
-
- dialog.show()
-
- val visibility = dialog.requireViewById<View>(R.id.button_app).visibility
- assertThat(visibility).isEqualTo(View.VISIBLE)
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt
index ad6909d..a6c25be 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt
@@ -24,7 +24,6 @@
import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
-import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.plugins.ActivityStarter
@@ -36,8 +35,8 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
-import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.`when` as whenever
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -48,7 +47,6 @@
@Mock private lateinit var controller: RecordingController
@Mock private lateinit var userContextProvider: UserContextProvider
@Mock private lateinit var flags: FeatureFlags
- @Mock private lateinit var dialogLaunchAnimator: DialogLaunchAnimator
@Mock private lateinit var onStartRecordingClicked: Runnable
private lateinit var dialog: ScreenRecordPermissionDialog
@@ -63,7 +61,6 @@
UserHandle.of(0),
controller,
starter,
- dialogLaunchAnimator,
userContextProvider,
onStartRecordingClicked
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt
index cfdf66e..a105c15 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt
@@ -180,7 +180,7 @@
}
@Test
- fun executeScreenshots_doesNotReportFinishedIfOneFinishesOtherFails() =
+ fun executeScreenshots_oneFinishesOtherFails_reportFailsOnlyAtTheEnd() =
testScope.runTest {
setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
val onSaved = { _: Uri -> }
@@ -207,7 +207,7 @@
}
@Test
- fun executeScreenshots_doesNotReportFinishedAfterOneFails() =
+ fun executeScreenshots_allDisplaysFail_reportsFail() =
testScope.runTest {
setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
val onSaved = { _: Uri -> }
@@ -224,11 +224,12 @@
capturer0.value.reportError()
verify(callback, never()).onFinish()
- verify(callback).reportError()
+ verify(callback, never()).reportError()
- capturer1.value.onFinish()
+ capturer1.value.reportError()
verify(callback, never()).onFinish()
+ verify(callback).reportError()
screenshotExecutor.onDestroy()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt
index beb981d..1bb2ff8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt
@@ -11,10 +11,13 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.capture
import com.google.common.truth.Truth.assertThat
import java.util.concurrent.Executor
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -60,6 +63,9 @@
@Mock private lateinit var callback: UserTracker.Callback
@Captor private lateinit var captor: ArgumentCaptor<List<UserInfo>>
+ private val fakeFeatures = FakeFeatureFlagsClassic()
+ private val testDispatcher = StandardTestDispatcher()
+
private lateinit var tracker: UserTrackerImpl
@Before
@@ -68,12 +74,21 @@
`when`(context.user).thenReturn(UserHandle.SYSTEM)
`when`(context.createContextAsUser(ArgumentMatchers.any(), anyInt())).thenReturn(context)
-
- tracker = UserTrackerImpl(context, userManager, iActivityManager, dumpManager, handler)
}
@Test
- fun callsCallbackAndUpdatesProfilesWhenAnIntentReceived() {
+ fun callsCallbackAndUpdatesProfilesWhenAnIntentReceived() = runTest {
+ tracker =
+ UserTrackerImpl(
+ context,
+ { fakeFeatures },
+ userManager,
+ iActivityManager,
+ dumpManager,
+ this,
+ testDispatcher,
+ handler
+ )
tracker.initialize(0)
tracker.addCallback(callback, executor)
val profileID = tracker.userId + 10
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
index aa98f08..52b25f8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
@@ -26,16 +26,25 @@
import android.os.IRemoteCallback
import android.os.UserHandle
import android.os.UserManager
-import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.FakeFeatureFlagsClassic
+import com.android.systemui.flags.Flags
+import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
-import java.util.concurrent.Executor
+import com.google.common.truth.TruthJUnit.assume
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.anyInt
@@ -43,28 +52,52 @@
import org.mockito.ArgumentMatchers.eq
import org.mockito.ArgumentMatchers.isNull
import org.mockito.Mock
+import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
+import java.util.concurrent.Executor
+
+@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(Parameterized::class)
class UserTrackerImplTest : SysuiTestCase() {
+ companion object {
+
+ @JvmStatic
+ @Parameterized.Parameters
+ fun isBackgroundUserTrackerEnabled(): Iterable<Boolean> = listOf(true, false)
+ }
+
@Mock
private lateinit var context: Context
+
@Mock
private lateinit var userManager: UserManager
+
@Mock
private lateinit var iActivityManager: IActivityManager
+
@Mock
private lateinit var userSwitchingReply: IRemoteCallback
+
@Mock(stubOnly = true)
private lateinit var dumpManager: DumpManager
+
@Mock(stubOnly = true)
private lateinit var handler: Handler
+ @Parameterized.Parameter
+ @JvmField
+ var isBackgroundUserTrackerEnabled: Boolean = false
+
+ private val testScope = TestScope()
+ private val testDispatcher = StandardTestDispatcher(testScope.testScheduler)
private val executor = Executor(Runnable::run)
+ private val featureFlags = FakeFeatureFlagsClassic()
+
private lateinit var tracker: UserTrackerImpl
@Before
@@ -84,54 +117,65 @@
listOf(info)
}
- tracker = UserTrackerImpl(context, userManager, iActivityManager, dumpManager, handler)
+ featureFlags.set(Flags.USER_TRACKER_BACKGROUND_CALLBACKS, isBackgroundUserTrackerEnabled)
+ tracker =
+ UserTrackerImpl(
+ context,
+ { featureFlags },
+ userManager,
+ iActivityManager,
+ dumpManager,
+ testScope.backgroundScope,
+ testDispatcher,
+ handler,
+ )
}
@Test
- fun testNotInitialized() {
+ fun testNotInitialized() = testScope.runTest {
assertThat(tracker.initialized).isFalse()
}
@Test(expected = IllegalStateException::class)
- fun testGetUserIdBeforeInitThrowsException() {
+ fun testGetUserIdBeforeInitThrowsException() = testScope.runTest {
tracker.userId
}
@Test(expected = IllegalStateException::class)
- fun testGetUserHandleBeforeInitThrowsException() {
+ fun testGetUserHandleBeforeInitThrowsException() = testScope.runTest {
tracker.userHandle
}
@Test(expected = IllegalStateException::class)
- fun testGetUserContextBeforeInitThrowsException() {
+ fun testGetUserContextBeforeInitThrowsException() = testScope.runTest {
tracker.userContext
}
@Test(expected = IllegalStateException::class)
- fun testGetUserContentResolverBeforeInitThrowsException() {
+ fun testGetUserContentResolverBeforeInitThrowsException() = testScope.runTest {
tracker.userContentResolver
}
@Test(expected = IllegalStateException::class)
- fun testGetUserProfilesBeforeInitThrowsException() {
+ fun testGetUserProfilesBeforeInitThrowsException() = testScope.runTest {
tracker.userProfiles
}
@Test
- fun testInitialize() {
+ fun testInitialize() = testScope.runTest {
tracker.initialize(0)
assertThat(tracker.initialized).isTrue()
}
@Test
- fun testReceiverRegisteredOnInitialize() {
+ fun testReceiverRegisteredOnInitialize() = testScope.runTest {
tracker.initialize(0)
val captor = ArgumentCaptor.forClass(IntentFilter::class.java)
- verify(context).registerReceiverForAllUsers(
- eq(tracker), capture(captor), isNull(), eq(handler))
+ verify(context)
+ .registerReceiverForAllUsers(eq(tracker), capture(captor), isNull(), eq(handler))
with(captor.value) {
assertThat(countActions()).isEqualTo(6)
assertThat(hasAction(Intent.ACTION_USER_INFO_CHANGED)).isTrue()
@@ -144,7 +188,7 @@
}
@Test
- fun testInitialValuesSet() {
+ fun testInitialValuesSet() = testScope.runTest {
val testID = 4
tracker.initialize(testID)
@@ -161,7 +205,7 @@
}
@Test
- fun testUserSwitch() {
+ fun testUserSwitch() = testScope.runTest {
tracker.initialize(0)
val newID = 5
@@ -169,6 +213,7 @@
verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
captor.value.onBeforeUserSwitching(newID)
captor.value.onUserSwitching(newID, userSwitchingReply)
+ runCurrent()
verify(userSwitchingReply).sendResult(any())
verify(userManager).getProfiles(newID)
@@ -184,7 +229,7 @@
}
@Test
- fun testManagedProfileAvailable() {
+ fun testManagedProfileAvailable() = testScope.runTest {
tracker.initialize(0)
val profileID = tracker.userId + 10
@@ -209,7 +254,8 @@
assertThat(tracker.userProfiles.map { it.id }).containsExactly(tracker.userId, profileID)
}
- fun testManagedProfileUnavailable() {
+ @Test
+ fun testManagedProfileUnavailable() = testScope.runTest {
tracker.initialize(0)
val profileID = tracker.userId + 10
@@ -234,7 +280,8 @@
assertThat(tracker.userProfiles.map { it.id }).containsExactly(tracker.userId, profileID)
}
- fun testManagedProfileStartedAndRemoved() {
+ @Test
+ fun testManagedProfileStartedAndRemoved() = testScope.runTest {
tracker.initialize(0)
val profileID = tracker.userId + 10
@@ -271,7 +318,7 @@
}
@Test
- fun testCallbackNotCalledOnAdd() {
+ fun testCallbackNotCalledOnAdd() = testScope.runTest {
tracker.initialize(0)
val callback = TestCallback()
@@ -282,7 +329,7 @@
}
@Test
- fun testCallbackCalledOnUserChanging() {
+ fun testCallbackCalledOnUserChanging() = testScope.runTest {
tracker.initialize(0)
val callback = TestCallback()
tracker.addCallback(callback, executor)
@@ -293,15 +340,49 @@
verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
captor.value.onBeforeUserSwitching(newID)
captor.value.onUserSwitching(newID, userSwitchingReply)
- verify(userSwitchingReply).sendResult(any())
+ runCurrent()
+ verify(userSwitchingReply).sendResult(any())
assertThat(callback.calledOnUserChanging).isEqualTo(1)
assertThat(callback.lastUser).isEqualTo(newID)
assertThat(callback.lastUserContext?.userId).isEqualTo(newID)
}
@Test
- fun testCallbackCalledOnUserChanged() {
+ fun testAsyncCallbackWaitsUserToChange() = testScope.runTest {
+ // Skip this test for CountDownLatch variation. The problem is that there would be a
+ // deadlock if the callbacks processing runs on the same thread as the callback (which
+ // is blocked by the latch). Before the change it works because the callbacks are
+ // processed on a binder thread which is always distinct.
+ // This is the issue that this feature addresses.
+ assume().that(isBackgroundUserTrackerEnabled).isTrue()
+
+ tracker.initialize(0)
+ val callback = TestCallback()
+ val callbackExecutor = FakeExecutor(FakeSystemClock())
+ tracker.addCallback(callback, callbackExecutor)
+
+ val newID = 5
+
+ val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
+ verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
+ captor.value.onBeforeUserSwitching(newID)
+ captor.value.onUserSwitching(newID, userSwitchingReply)
+
+ assertThat(callback.calledOnUserChanging).isEqualTo(0)
+ verify(userSwitchingReply, never()).sendResult(any())
+
+ FakeExecutor.exhaustExecutors(callbackExecutor)
+ runCurrent()
+ FakeExecutor.exhaustExecutors(callbackExecutor)
+ runCurrent()
+
+ assertThat(callback.calledOnUserChanging).isEqualTo(1)
+ verify(userSwitchingReply).sendResult(any())
+ }
+
+ @Test
+ fun testCallbackCalledOnUserChanged() = testScope.runTest {
tracker.initialize(0)
val callback = TestCallback()
tracker.addCallback(callback, executor)
@@ -312,6 +393,7 @@
verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
captor.value.onBeforeUserSwitching(newID)
captor.value.onUserSwitchComplete(newID)
+ runCurrent()
assertThat(callback.calledOnUserChanged).isEqualTo(1)
assertThat(callback.lastUser).isEqualTo(newID)
@@ -321,7 +403,7 @@
}
@Test
- fun testCallbackCalledOnUserInfoChanged() {
+ fun testCallbackCalledOnUserInfoChanged() = testScope.runTest {
tracker.initialize(0)
val callback = TestCallback()
tracker.addCallback(callback, executor)
@@ -331,18 +413,18 @@
val id = invocation.getArgument<Int>(0)
val info = UserInfo(id, "", UserInfo.FLAG_FULL)
val infoProfile = UserInfo(
- id + 10,
- "",
- "",
- UserInfo.FLAG_MANAGED_PROFILE,
- UserManager.USER_TYPE_PROFILE_MANAGED
+ id + 10,
+ "",
+ "",
+ UserInfo.FLAG_MANAGED_PROFILE,
+ UserManager.USER_TYPE_PROFILE_MANAGED
)
infoProfile.profileGroupId = id
listOf(info, infoProfile)
}
val intent = Intent(Intent.ACTION_USER_INFO_CHANGED)
- .putExtra(Intent.EXTRA_USER, UserHandle.of(profileID))
+ .putExtra(Intent.EXTRA_USER, UserHandle.of(profileID))
tracker.onReceive(context, intent)
@@ -352,7 +434,7 @@
}
@Test
- fun testCallbackRemoved() {
+ fun testCallbackRemoved() = testScope.runTest {
tracker.initialize(0)
val newID = 5
val profileID = newID + 10
@@ -364,6 +446,7 @@
val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
captor.value.onUserSwitching(newID, userSwitchingReply)
+ runCurrent()
verify(userSwitchingReply).sendResult(any())
captor.value.onUserSwitchComplete(newID)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
index 638c266..911cab4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -16,7 +16,6 @@
package com.android.systemui.shade;
-import static com.android.keyguard.FaceAuthApiRequestReason.NOTIFICATION_PANEL_CLICKED;
import static com.android.keyguard.KeyguardClockSwitch.LARGE;
import static com.android.keyguard.KeyguardClockSwitch.SMALL;
import static com.android.systemui.shade.ShadeExpansionStateManagerKt.STATE_CLOSED;
@@ -59,6 +58,7 @@
import com.android.keyguard.FaceAuthApiRequestReason;
import com.android.systemui.DejankUtils;
import com.android.systemui.R;
+import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.shared.model.WakeSleepReason;
import com.android.systemui.keyguard.shared.model.WakefulnessModel;
import com.android.systemui.keyguard.shared.model.WakefulnessState;
@@ -197,6 +197,7 @@
@Test
public void getVerticalSpaceForLockscreenShelf_useLockIconBottomPadding_returnsShelfHeight() {
+ enableSplitShade(/* enabled= */ false);
setBottomPadding(/* stackScrollLayoutBottom= */ 100,
/* lockIconPadding= */ 20,
/* indicationPadding= */ 0,
@@ -209,6 +210,7 @@
@Test
public void getVerticalSpaceForLockscreenShelf_useIndicationBottomPadding_returnsZero() {
+ enableSplitShade(/* enabled= */ false);
setBottomPadding(/* stackScrollLayoutBottom= */ 100,
/* lockIconPadding= */ 0,
/* indicationPadding= */ 30,
@@ -221,6 +223,7 @@
@Test
public void getVerticalSpaceForLockscreenShelf_useAmbientBottomPadding_returnsZero() {
+ enableSplitShade(/* enabled= */ false);
setBottomPadding(/* stackScrollLayoutBottom= */ 100,
/* lockIconPadding= */ 0,
/* indicationPadding= */ 0,
@@ -233,6 +236,7 @@
@Test
public void getVerticalSpaceForLockscreenShelf_useLockIconPadding_returnsLessThanShelfHeight() {
+ enableSplitShade(/* enabled= */ false);
setBottomPadding(/* stackScrollLayoutBottom= */ 100,
/* lockIconPadding= */ 10,
/* indicationPadding= */ 8,
@@ -244,6 +248,19 @@
}
@Test
+ public void getVerticalSpaceForLockscreenShelf_splitShade() {
+ enableSplitShade(/* enabled= */ true);
+ setBottomPadding(/* stackScrollLayoutBottom= */ 100,
+ /* lockIconPadding= */ 10,
+ /* indicationPadding= */ 8,
+ /* ambientPadding= */ 0);
+
+ when(mNotificationShelfController.getIntrinsicHeight()).thenReturn(5);
+ assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
+ .isEqualTo(0);
+ }
+
+ @Test
public void testSetPanelScrimMinFractionWhenHeadsUpIsDragged() {
mNotificationPanelViewController.setHeadsUpDraggingStartingHeight(
mNotificationPanelViewController.getMaxPanelHeight() / 2);
@@ -817,6 +834,42 @@
}
@Test
+ public void switchesToBigClockInSplitShadeOn_landFlagOn_ForceSmallClock() {
+ when(mScreenOffAnimationController.shouldAnimateClockChange()).thenReturn(false);
+ mStatusBarStateController.setState(KEYGUARD);
+ enableSplitShade(/* enabled= */ false);
+ mNotificationPanelViewController.setDozing(false, false);
+ when(mFeatureFlags.isEnabled(Flags.LOCKSCREEN_ENABLE_LANDSCAPE)).thenReturn(true);
+ when(mResources.getBoolean(R.bool.force_small_clock_on_lockscreen)).thenReturn(true);
+ when(mMediaDataManager.hasActiveMedia()).thenReturn(false);
+ when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
+ clearInvocations(mKeyguardStatusViewController);
+
+ enableSplitShade(/* enabled= */ true);
+ mNotificationPanelViewController.updateResources();
+
+ verify(mKeyguardStatusViewController).displayClock(SMALL, /* animate */ false);
+ }
+
+ @Test
+ public void switchesToBigClockInSplitShadeOn_landFlagOff_DontForceSmallClock() {
+ when(mScreenOffAnimationController.shouldAnimateClockChange()).thenReturn(false);
+ mStatusBarStateController.setState(KEYGUARD);
+ enableSplitShade(/* enabled= */ false);
+ mNotificationPanelViewController.setDozing(false, false);
+ when(mFeatureFlags.isEnabled(Flags.LOCKSCREEN_ENABLE_LANDSCAPE)).thenReturn(false);
+ when(mResources.getBoolean(R.bool.force_small_clock_on_lockscreen)).thenReturn(true);
+ when(mMediaDataManager.hasActiveMedia()).thenReturn(false);
+ when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
+ clearInvocations(mKeyguardStatusViewController);
+
+ enableSplitShade(/* enabled= */ true);
+ mNotificationPanelViewController.updateResources();
+
+ verify(mKeyguardStatusViewController).displayClock(LARGE, /* animate */ false);
+ }
+
+ @Test
public void testDisplaysSmallClockOnLockscreenInSplitShadeWhenMediaIsPlaying() {
mStatusBarStateController.setState(KEYGUARD);
enableSplitShade(/* enabled= */ true);
@@ -908,8 +961,10 @@
@Test
public void testQsExpansionChangedToDefaultWhenRotatingFromOrToSplitShade() {
+ when(mCommandQueue.panelsEnabled()).thenReturn(true);
+
// to make sure shade is in expanded state
- mNotificationPanelViewController.startWaitingForExpandGesture();
+ mNotificationPanelViewController.startInputFocusTransfer();
// switch to split shade from portrait (default state)
enableSplitShade(/* enabled= */ true);
@@ -1047,7 +1102,18 @@
mEmptySpaceClickListenerCaptor.getValue().onEmptySpaceClicked(0, 0);
verify(mUpdateMonitor, never()).requestFaceAuth(anyString());
+ }
+ @Test
+ public void nsslFlagEnabled_allowOnlyExternalTouches() {
+ when(mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)).thenReturn(true);
+
+ // This sets the dozing state that is read when onMiddleClicked is eventually invoked.
+ mTouchHandler.onTouch(mock(View.class), mDownMotionEvent);
+ verify(mQsController, never()).disallowTouches();
+
+ mNotificationPanelViewController.handleExternalInterceptTouch(mDownMotionEvent);
+ verify(mQsController).disallowTouches();
}
@Test
@@ -1171,18 +1237,50 @@
}
@Test
- public void shadeExpanded_whenWaitingForExpandGesture() {
- mNotificationPanelViewController.startWaitingForExpandGesture();
+ public void shadeExpanded_whenUnlockedOffscreenAnimationRunning() {
+ when(mUnlockedScreenOffAnimationController.isAnimationPlaying()).thenReturn(true);
assertThat(mNotificationPanelViewController.isExpanded()).isTrue();
}
@Test
- public void shadeExpanded_whenUnlockedOffscreenAnimationRunning() {
- when(mUnlockedScreenOffAnimationController.isAnimationPlaying()).thenReturn(true);
+ public void shadeExpanded_whenInputFocusTransferStarted() {
+ when(mCommandQueue.panelsEnabled()).thenReturn(true);
+
+ mNotificationPanelViewController.startInputFocusTransfer();
+
assertThat(mNotificationPanelViewController.isExpanded()).isTrue();
}
@Test
+ public void shadeNotExpanded_whenInputFocusTransferStartedButPanelsDisabled() {
+ when(mCommandQueue.panelsEnabled()).thenReturn(false);
+
+ mNotificationPanelViewController.startInputFocusTransfer();
+
+ assertThat(mNotificationPanelViewController.isExpanded()).isFalse();
+ }
+
+ @Test
+ public void cancelInputFocusTransfer_shadeCollapsed() {
+ when(mCommandQueue.panelsEnabled()).thenReturn(true);
+ mNotificationPanelViewController.startInputFocusTransfer();
+
+ mNotificationPanelViewController.cancelInputFocusTransfer();
+
+ assertThat(mNotificationPanelViewController.isExpanded()).isFalse();
+ }
+
+ @Test
+ public void finishInputFocusTransfer_shadeFlingingOpen() {
+ when(mCommandQueue.panelsEnabled()).thenReturn(true);
+ mNotificationPanelViewController.startInputFocusTransfer();
+
+ mNotificationPanelViewController.finishInputFocusTransfer(/* velocity= */ 0f);
+
+ assertThat(mNotificationPanelViewController.isFlinging()).isTrue();
+ }
+
+ @Test
public void getFalsingThreshold_deviceNotInteractive_isQsThreshold() {
mFakeKeyguardRepository.setWakefulnessModel(
new WakefulnessModel(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index 39fe498..3da5f6a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -48,6 +48,7 @@
import com.android.systemui.log.BouncerLogger
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
+import com.android.systemui.statusbar.DragDownHelper
import com.android.systemui.statusbar.LockscreenShadeTransitionController
import com.android.systemui.statusbar.NotificationInsetsController
import com.android.systemui.statusbar.NotificationShadeDepthController
@@ -124,6 +125,7 @@
private lateinit var unfoldTransitionProgressProvider:
Optional<UnfoldTransitionProgressProvider>
@Mock lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
+ @Mock lateinit var dragDownHelper: DragDownHelper
@Mock
lateinit var primaryBouncerToGoneTransitionViewModel: PrimaryBouncerToGoneTransitionViewModel
@Mock lateinit var keyEventInteractor: KeyEventInteractor
@@ -137,6 +139,8 @@
private lateinit var testScope: TestScope
+ private lateinit var featureFlags: FakeFeatureFlags
+
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
@@ -150,12 +154,13 @@
whenever(keyguardTransitionInteractor.lockscreenToDreamingTransition)
.thenReturn(emptyFlow<TransitionStep>())
- val featureFlags = FakeFeatureFlags()
+ featureFlags = FakeFeatureFlags()
featureFlags.set(Flags.TRACKPAD_GESTURE_COMMON, true)
featureFlags.set(Flags.TRACKPAD_GESTURE_FEATURES, false)
featureFlags.set(Flags.SPLIT_SHADE_SUBPIXEL_OPTIMIZATION, true)
featureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true)
featureFlags.set(Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false)
+ featureFlags.set(Flags.MIGRATE_NSSL, false)
testScope = TestScope()
fakeClock = FakeSystemClock()
@@ -206,6 +211,7 @@
keyEventInteractor,
)
underTest.setupExpandedStatusBar()
+ underTest.setDragDownHelper(dragDownHelper)
interactionEventHandlerCaptor = ArgumentCaptor.forClass(InteractionEventHandler::class.java)
verify(view).setInteractionEventHandler(interactionEventHandlerCaptor.capture())
@@ -347,9 +353,8 @@
testScope.runTest {
// GIVEN touch dispatcher in a state that returns true
underTest.setStatusBarViewController(phoneStatusBarViewController)
- whenever(keyguardUnlockAnimationController.isPlayingCannedUnlockAnimation()).thenReturn(
- true
- )
+ whenever(keyguardUnlockAnimationController.isPlayingCannedUnlockAnimation())
+ .thenReturn(true)
assertThat(interactionEventHandler.handleDispatchTouchEvent(DOWN_EVENT)).isTrue()
// WHEN launch animation is running for 2 seconds
@@ -381,6 +386,32 @@
}
@Test
+ fun shouldInterceptTouchEvent_notificationPanelViewControllerShouldIntercept() {
+ // GIVEN not dozing
+ whenever(sysuiStatusBarStateController.isDozing()).thenReturn(false)
+ // AND alternate bouncer doesn't want the touch
+ whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(DOWN_EVENT))
+ .thenReturn(false)
+ // AND the lock icon doesn't want the touch
+ whenever(lockIconViewController.onInterceptTouchEvent(DOWN_EVENT)).thenReturn(false)
+ // AND the notification panel can accept touches
+ whenever(notificationPanelViewController.isFullyExpanded()).thenReturn(true)
+ whenever(dragDownHelper.isDragDownEnabled).thenReturn(true)
+ whenever(centralSurfaces.isBouncerShowing()).thenReturn(false)
+
+ // AND the drag down helper doesn't want the touch (to pull the shade down)
+ whenever(dragDownHelper.onInterceptTouchEvent(DOWN_EVENT)).thenReturn(false)
+
+ featureFlags.set(Flags.MIGRATE_NSSL, true)
+
+ // WHEN asked if should intercept touch
+ interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)
+
+ // Verify that NPVC gets a chance to use the touch
+ verify(notificationPanelViewController).handleExternalInterceptTouch(DOWN_EVENT)
+ }
+
+ @Test
fun testGetKeyguardMessageArea() =
testScope.runTest {
underTest.keyguardMessageArea
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
index 3031658..04c4b45 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
@@ -164,6 +164,7 @@
featureFlags.set(Flags.SPLIT_SHADE_SUBPIXEL_OPTIMIZATION, true)
featureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true)
featureFlags.set(Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false)
+ featureFlags.set(Flags.MIGRATE_NSSL, false)
testScope = TestScope()
controller =
NotificationShadeWindowViewController(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
index 36cf1d9..e9e6d31 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
@@ -101,7 +101,11 @@
MockitoAnnotations.initMocks(this)
fakeSystemClock = FakeSystemClock()
delayableExecutor = FakeExecutor(fakeSystemClock)
- featureFlags = FakeFeatureFlags().apply { set(Flags.MIGRATE_NSSL, false) }
+ featureFlags =
+ FakeFeatureFlags().apply {
+ set(Flags.MIGRATE_NSSL, false)
+ set(Flags.QS_CONTAINER_GRAPH_OPTIMIZER, false)
+ }
mContext.ensureTestableResources()
whenever(view.context).thenReturn(mContext)
whenever(view.resources).thenReturn(mContext.resources)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt
index 090bee2..6801f2f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt
@@ -100,7 +100,11 @@
MockitoAnnotations.initMocks(this)
fakeSystemClock = FakeSystemClock()
delayableExecutor = FakeExecutor(fakeSystemClock)
- featureFlags = FakeFeatureFlags().apply { set(Flags.MIGRATE_NSSL, true) }
+ featureFlags =
+ FakeFeatureFlags().apply {
+ set(Flags.MIGRATE_NSSL, true)
+ set(Flags.QS_CONTAINER_GRAPH_OPTIMIZER, true)
+ }
mContext.ensureTestableResources()
whenever(view.context).thenReturn(mContext)
whenever(view.resources).thenReturn(mContext.resources)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
index 9275ccb..342b1c5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
@@ -402,6 +402,7 @@
assertThat(actual).isEqualTo(0.8f)
}
+ @Test
fun shadeExpansionWhenInSplitShadeAndQsExpanded() =
testScope.runTest {
val actual by collectLastValue(underTest.shadeExpansion)
@@ -410,27 +411,31 @@
keyguardRepository.setStatusBarState(StatusBarState.SHADE)
overrideResource(R.bool.config_use_split_notification_shade, true)
configurationRepository.onAnyConfigurationChange()
- runCurrent()
shadeRepository.setQsExpansion(.5f)
shadeRepository.setLegacyShadeExpansion(.7f)
+ runCurrent()
// THEN legacy shade expansion is passed through
assertThat(actual).isEqualTo(.7f)
}
+ @Test
fun shadeExpansionWhenNotInSplitShadeAndQsExpanded() =
testScope.runTest {
val actual by collectLastValue(underTest.shadeExpansion)
// WHEN split shade is not enabled and QS is expanded
keyguardRepository.setStatusBarState(StatusBarState.SHADE)
+ overrideResource(R.bool.config_use_split_notification_shade, false)
shadeRepository.setQsExpansion(.5f)
shadeRepository.setLegacyShadeExpansion(1f)
+ runCurrent()
// THEN shade expansion is zero
assertThat(actual).isEqualTo(0f)
}
+ @Test
fun shadeExpansionWhenNotInSplitShadeAndQsCollapsed() =
testScope.runTest {
val actual by collectLastValue(underTest.shadeExpansion)
@@ -471,7 +476,7 @@
@Test
fun expanding_shadeDraggedDown_expandingTrue() =
testScope.runTest() {
- val actual by collectLastValue(underTest.anyExpanding)
+ val actual by collectLastValue(underTest.isAnyExpanding)
// GIVEN shade and QS collapsed
shadeRepository.setLegacyShadeExpansion(0f)
@@ -489,7 +494,7 @@
@Test
fun expanding_qsDraggedDown_expandingTrue() =
testScope.runTest() {
- val actual by collectLastValue(underTest.anyExpanding)
+ val actual by collectLastValue(underTest.isAnyExpanding)
// GIVEN shade and QS collapsed
shadeRepository.setLegacyShadeExpansion(0f)
@@ -507,7 +512,7 @@
@Test
fun expanding_shadeDraggedUpAndDown() =
testScope.runTest() {
- val actual by collectLastValue(underTest.anyExpanding)
+ val actual by collectLastValue(underTest.isAnyExpanding)
// WHEN shade starts collapsed then partially expanded
shadeRepository.setLegacyShadeExpansion(0f)
@@ -532,7 +537,7 @@
// THEN anyExpanding is still true
assertThat(actual).isTrue()
- // WHEN shade fully shadeExpanded
+ // WHEN shade fully expanded
shadeRepository.setLegacyShadeExpansion(1f)
runCurrent()
@@ -550,7 +555,7 @@
@Test
fun expanding_shadeDraggedDownThenUp_expandingFalse() =
testScope.runTest() {
- val actual by collectLastValue(underTest.anyExpanding)
+ val actual by collectLastValue(underTest.isAnyExpanding)
// GIVEN shade starts collapsed
shadeRepository.setLegacyShadeExpansion(0f)
@@ -618,6 +623,7 @@
fromScene = SceneKey.Lockscreen,
toScene = key,
progress = progress,
+ isUserInputDriven = false,
)
)
sceneInteractor.setTransitionState(transitionState)
@@ -654,6 +660,7 @@
fromScene = key,
toScene = SceneKey.Lockscreen,
progress = progress,
+ isUserInputDriven = false,
)
)
sceneInteractor.setTransitionState(transitionState)
@@ -689,6 +696,7 @@
fromScene = SceneKey.Lockscreen,
toScene = SceneKey.Shade,
progress = progress,
+ isUserInputDriven = false,
)
)
sceneInteractor.setTransitionState(transitionState)
@@ -708,4 +716,415 @@
// THEN expansion is still 0
assertThat(expansionAmount).isEqualTo(0f)
}
+
+ @Test
+ fun userInteractingWithShade_shadeDraggedUpAndDown() =
+ testScope.runTest() {
+ val actual by collectLastValue(underTest.isUserInteractingWithShade)
+ // GIVEN shade collapsed and not tracking input
+ shadeRepository.setLegacyShadeExpansion(0f)
+ shadeRepository.setLegacyShadeTracking(false)
+ runCurrent()
+
+ // THEN user is not interacting
+ assertThat(actual).isFalse()
+
+ // WHEN shade tracking starts
+ shadeRepository.setLegacyShadeTracking(true)
+ runCurrent()
+
+ // THEN user is interacting
+ assertThat(actual).isTrue()
+
+ // WHEN shade dragged down halfway
+ shadeRepository.setLegacyShadeExpansion(.5f)
+ runCurrent()
+
+ // THEN user is interacting
+ assertThat(actual).isTrue()
+
+ // WHEN shade fully expanded but tracking is not stopped
+ shadeRepository.setLegacyShadeExpansion(1f)
+ runCurrent()
+
+ // THEN user is interacting
+ assertThat(actual).isTrue()
+
+ // WHEN shade fully collapsed but tracking is not stopped
+ shadeRepository.setLegacyShadeExpansion(0f)
+ runCurrent()
+
+ // THEN user is interacting
+ assertThat(actual).isTrue()
+
+ // WHEN shade dragged halfway and tracking is stopped
+ shadeRepository.setLegacyShadeExpansion(.6f)
+ shadeRepository.setLegacyShadeTracking(false)
+ runCurrent()
+
+ // THEN user is interacting
+ assertThat(actual).isTrue()
+
+ // WHEN shade completes expansion stopped
+ shadeRepository.setLegacyShadeExpansion(1f)
+ runCurrent()
+
+ // THEN user is not interacting
+ assertThat(actual).isFalse()
+ }
+
+ @Test
+ fun userInteractingWithShade_shadeExpanded() =
+ testScope.runTest() {
+ val actual by collectLastValue(underTest.isUserInteractingWithShade)
+ // GIVEN shade collapsed and not tracking input
+ shadeRepository.setLegacyShadeExpansion(0f)
+ shadeRepository.setLegacyShadeTracking(false)
+ runCurrent()
+
+ // THEN user is not interacting
+ assertThat(actual).isFalse()
+
+ // WHEN shade tracking starts
+ shadeRepository.setLegacyShadeTracking(true)
+ runCurrent()
+
+ // THEN user is interacting
+ assertThat(actual).isTrue()
+
+ // WHEN shade dragged down halfway
+ shadeRepository.setLegacyShadeExpansion(.5f)
+ runCurrent()
+
+ // THEN user is interacting
+ assertThat(actual).isTrue()
+
+ // WHEN shade fully expanded and tracking is stopped
+ shadeRepository.setLegacyShadeExpansion(1f)
+ shadeRepository.setLegacyShadeTracking(false)
+ runCurrent()
+
+ // THEN user is not interacting
+ assertThat(actual).isFalse()
+ }
+
+ @Test
+ fun userInteractingWithShade_shadePartiallyExpanded() =
+ testScope.runTest() {
+ val actual by collectLastValue(underTest.isUserInteractingWithShade)
+ // GIVEN shade collapsed and not tracking input
+ shadeRepository.setLegacyShadeExpansion(0f)
+ shadeRepository.setLegacyShadeTracking(false)
+ runCurrent()
+
+ // THEN user is not interacting
+ assertThat(actual).isFalse()
+
+ // WHEN shade tracking starts
+ shadeRepository.setLegacyShadeTracking(true)
+ runCurrent()
+
+ // THEN user is interacting
+ assertThat(actual).isTrue()
+
+ // WHEN shade partially expanded
+ shadeRepository.setLegacyShadeExpansion(.4f)
+ runCurrent()
+
+ // THEN user is interacting
+ assertThat(actual).isTrue()
+
+ // WHEN tracking is stopped
+ shadeRepository.setLegacyShadeTracking(false)
+ runCurrent()
+
+ // THEN user is interacting
+ assertThat(actual).isTrue()
+
+ // WHEN shade goes back to collapsed
+ shadeRepository.setLegacyShadeExpansion(0f)
+ runCurrent()
+
+ // THEN user is not interacting
+ assertThat(actual).isFalse()
+ }
+
+ @Test
+ fun userInteractingWithShade_shadeCollapsed() =
+ testScope.runTest() {
+ val actual by collectLastValue(underTest.isUserInteractingWithShade)
+ // GIVEN shade expanded and not tracking input
+ shadeRepository.setLegacyShadeExpansion(1f)
+ shadeRepository.setLegacyShadeTracking(false)
+ runCurrent()
+
+ // THEN user is not interacting
+ assertThat(actual).isFalse()
+
+ // WHEN shade tracking starts
+ shadeRepository.setLegacyShadeTracking(true)
+ runCurrent()
+
+ // THEN user is interacting
+ assertThat(actual).isTrue()
+
+ // WHEN shade dragged up halfway
+ shadeRepository.setLegacyShadeExpansion(.5f)
+ runCurrent()
+
+ // THEN user is interacting
+ assertThat(actual).isTrue()
+
+ // WHEN shade fully collapsed and tracking is stopped
+ shadeRepository.setLegacyShadeExpansion(0f)
+ shadeRepository.setLegacyShadeTracking(false)
+ runCurrent()
+
+ // THEN user is not interacting
+ assertThat(actual).isFalse()
+ }
+
+ @Test
+ fun userInteractingWithQs_qsDraggedUpAndDown() =
+ testScope.runTest() {
+ val actual by collectLastValue(underTest.isUserInteractingWithQs)
+ // GIVEN qs collapsed and not tracking input
+ shadeRepository.setQsExpansion(0f)
+ shadeRepository.setLegacyQsTracking(false)
+ runCurrent()
+
+ // THEN user is not interacting
+ assertThat(actual).isFalse()
+
+ // WHEN qs tracking starts
+ shadeRepository.setLegacyQsTracking(true)
+ runCurrent()
+
+ // THEN user is interacting
+ assertThat(actual).isTrue()
+
+ // WHEN qs dragged down halfway
+ shadeRepository.setQsExpansion(.5f)
+ runCurrent()
+
+ // THEN user is interacting
+ assertThat(actual).isTrue()
+
+ // WHEN qs fully expanded but tracking is not stopped
+ shadeRepository.setQsExpansion(1f)
+ runCurrent()
+
+ // THEN user is interacting
+ assertThat(actual).isTrue()
+
+ // WHEN qs fully collapsed but tracking is not stopped
+ shadeRepository.setQsExpansion(0f)
+ runCurrent()
+
+ // THEN user is interacting
+ assertThat(actual).isTrue()
+
+ // WHEN qs dragged halfway and tracking is stopped
+ shadeRepository.setQsExpansion(.6f)
+ shadeRepository.setLegacyQsTracking(false)
+ runCurrent()
+
+ // THEN user is interacting
+ assertThat(actual).isTrue()
+
+ // WHEN qs completes expansion stopped
+ shadeRepository.setQsExpansion(1f)
+ runCurrent()
+
+ // THEN user is not interacting
+ assertThat(actual).isFalse()
+ }
+ @Test
+ fun userInteracting_idle() =
+ testScope.runTest() {
+ // GIVEN an interacting flow based on transitions to and from a scene
+ val key = SceneKey.Shade
+ val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key)
+ val interacting by collectLastValue(interactingFlow)
+
+ // WHEN transition state is idle
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(ObservableTransitionState.Idle(key))
+ sceneInteractor.setTransitionState(transitionState)
+
+ // THEN interacting is false
+ assertThat(interacting).isFalse()
+ }
+
+ @Test
+ fun userInteracting_transitioning_toScene_programmatic() =
+ testScope.runTest() {
+ // GIVEN an interacting flow based on transitions to and from a scene
+ val key = SceneKey.QuickSettings
+ val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key)
+ val interacting by collectLastValue(interactingFlow)
+
+ // WHEN transition state is starting to move to the scene
+ val progress = MutableStateFlow(0f)
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Transition(
+ fromScene = SceneKey.Lockscreen,
+ toScene = key,
+ progress = progress,
+ isUserInputDriven = false,
+ )
+ )
+ sceneInteractor.setTransitionState(transitionState)
+
+ // THEN interacting is false
+ assertThat(interacting).isFalse()
+
+ // WHEN transition state is partially to the scene
+ progress.value = .4f
+
+ // THEN interacting is false
+ assertThat(interacting).isFalse()
+
+ // WHEN transition completes
+ progress.value = 1f
+
+ // THEN interacting is false
+ assertThat(interacting).isFalse()
+ }
+
+ @Test
+ fun userInteracting_transitioning_toScene_userInputDriven() =
+ testScope.runTest() {
+ // GIVEN an interacting flow based on transitions to and from a scene
+ val key = SceneKey.QuickSettings
+ val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key)
+ val interacting by collectLastValue(interactingFlow)
+
+ // WHEN transition state is starting to move to the scene
+ val progress = MutableStateFlow(0f)
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Transition(
+ fromScene = SceneKey.Lockscreen,
+ toScene = key,
+ progress = progress,
+ isUserInputDriven = true,
+ )
+ )
+ sceneInteractor.setTransitionState(transitionState)
+
+ // THEN interacting is true
+ assertThat(interacting).isTrue()
+
+ // WHEN transition state is partially to the scene
+ progress.value = .4f
+
+ // THEN interacting is true
+ assertThat(interacting).isTrue()
+
+ // WHEN transition completes
+ progress.value = 1f
+
+ // THEN interacting is true
+ assertThat(interacting).isTrue()
+ }
+
+ @Test
+ fun userInteracting_transitioning_fromScene_programmatic() =
+ testScope.runTest() {
+ // GIVEN an interacting flow based on transitions to and from a scene
+ val key = SceneKey.QuickSettings
+ val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key)
+ val interacting by collectLastValue(interactingFlow)
+
+ // WHEN transition state is starting to move to the scene
+ val progress = MutableStateFlow(0f)
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Transition(
+ fromScene = key,
+ toScene = SceneKey.Lockscreen,
+ progress = progress,
+ isUserInputDriven = false,
+ )
+ )
+ sceneInteractor.setTransitionState(transitionState)
+
+ // THEN interacting is false
+ assertThat(interacting).isFalse()
+
+ // WHEN transition state is partially to the scene
+ progress.value = .4f
+
+ // THEN interacting is false
+ assertThat(interacting).isFalse()
+
+ // WHEN transition completes
+ progress.value = 1f
+
+ // THEN interacting is false
+ assertThat(interacting).isFalse()
+ }
+
+ @Test
+ fun userInteracting_transitioning_fromScene_userInputDriven() =
+ testScope.runTest() {
+ // GIVEN an interacting flow based on transitions to and from a scene
+ val key = SceneKey.QuickSettings
+ val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key)
+ val interacting by collectLastValue(interactingFlow)
+
+ // WHEN transition state is starting to move to the scene
+ val progress = MutableStateFlow(0f)
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Transition(
+ fromScene = key,
+ toScene = SceneKey.Lockscreen,
+ progress = progress,
+ isUserInputDriven = true,
+ )
+ )
+ sceneInteractor.setTransitionState(transitionState)
+
+ // THEN interacting is true
+ assertThat(interacting).isTrue()
+
+ // WHEN transition state is partially to the scene
+ progress.value = .4f
+
+ // THEN interacting is true
+ assertThat(interacting).isTrue()
+
+ // WHEN transition completes
+ progress.value = 1f
+
+ // THEN interacting is true
+ assertThat(interacting).isTrue()
+ }
+
+ @Test
+ fun userInteracting_transitioning_toAndFromDifferentScenes() =
+ testScope.runTest() {
+ // GIVEN an interacting flow based on transitions to and from a scene
+ val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, SceneKey.Shade)
+ val interacting by collectLastValue(interactingFlow)
+
+ // WHEN transition state is starting to between different scenes
+ val progress = MutableStateFlow(0f)
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Transition(
+ fromScene = SceneKey.Lockscreen,
+ toScene = SceneKey.QuickSettings,
+ progress = progress,
+ isUserInputDriven = true,
+ )
+ )
+ sceneInteractor.setTransitionState(transitionState)
+
+ // THEN interacting is false
+ assertThat(interacting).isFalse()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
index 19d59fd..7463e65 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
@@ -169,6 +169,15 @@
}
@Test
+ fun updateLegacyExpandedOrAwaitingInputTransfer() =
+ testScope.runTest {
+ assertThat(underTest.legacyExpandedOrAwaitingInputTransfer.value).isEqualTo(false)
+
+ underTest.setLegacyExpandedOrAwaitingInputTransfer(true)
+ assertThat(underTest.legacyExpandedOrAwaitingInputTransfer.value).isEqualTo(true)
+ }
+
+ @Test
fun updateUdfpsTransitionToFullShadeProgress() =
testScope.runTest {
assertThat(underTest.udfpsTransitionToFullShadeProgress.value).isEqualTo(0f)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt
index a09e844..0925858 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt
@@ -83,7 +83,8 @@
ObservableTransitionState.Transition(
fromScene = SceneKey.Shade,
toScene = SceneKey.QuickSettings,
- progress = MutableStateFlow(0.5f)
+ progress = MutableStateFlow(0.5f),
+ isUserInputDriven = false,
)
)
)
@@ -100,7 +101,8 @@
ObservableTransitionState.Transition(
fromScene = SceneKey.QuickSettings,
toScene = SceneKey.Shade,
- progress = MutableStateFlow(0.5f)
+ progress = MutableStateFlow(0.5f),
+ isUserInputDriven = false,
)
)
)
@@ -117,7 +119,8 @@
ObservableTransitionState.Transition(
fromScene = SceneKey.Gone,
toScene = SceneKey.Shade,
- progress = MutableStateFlow(0.5f)
+ progress = MutableStateFlow(0.5f),
+ isUserInputDriven = false,
)
)
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java
index 88d853e..8f06e63 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java
@@ -181,29 +181,30 @@
String ACTION = "testAction";
}
- public void assertInstances(Integer allocated, Integer created) {
- // Run the garbage collector to finalize and deallocate outstanding
- // instances. Since the GC doesn't always appear to want to run
- // completely when we ask, we ask it 10 times in a short loop.
- for (int i = 0; i < 10; i++) {
+ private void assertInstances(int allocated, int created) {
+ // If there are more than the expected number of allocated instances, then we run the
+ // garbage collector to finalize and deallocate any outstanding non-referenced instances.
+ // Since the GC doesn't always appear to want to run completely when we ask, we do this up
+ // to 10 times before failing the test.
+ for (int i = 0; mCounter.getAllocatedInstances() > allocated && i < 10; i++) {
System.runFinalization();
System.gc();
}
- mCounter.assertInstances(allocated, created);
+ assertEquals(allocated, mCounter.getAllocatedInstances());
+ assertEquals(created, mCounter.getCreatedInstances());
}
public static class RefCounter {
public final AtomicInteger mAllocatedInstances = new AtomicInteger();
public final AtomicInteger mCreatedInstances = new AtomicInteger();
- public void assertInstances(Integer allocated, Integer created) {
- if (allocated != null) {
- assertEquals(allocated.intValue(), mAllocatedInstances.get());
- }
- if (created != null) {
- assertEquals(created.intValue(), mCreatedInstances.get());
- }
+ public int getAllocatedInstances() {
+ return mAllocatedInstances.get();
+ }
+
+ public int getCreatedInstances() {
+ return mCreatedInstances.get();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt
new file mode 100644
index 0000000..2c8900c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt
@@ -0,0 +1,31 @@
+/*
+ * 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.systemui.statusbar.data.repository
+
+import kotlinx.coroutines.flow.MutableStateFlow
+
+class FakeStatusBarModeRepository : StatusBarModeRepository {
+ override val isTransientShown = MutableStateFlow(false)
+ override val isInFullscreenMode = MutableStateFlow(false)
+
+ override fun showTransient() {
+ isTransientShown.value = true
+ }
+ override fun clearTransient() {
+ isTransientShown.value = false
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt
new file mode 100644
index 0000000..27c1ba3
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt
@@ -0,0 +1,257 @@
+/*
+ * 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.systemui.statusbar.data.repository
+
+import android.view.WindowInsets
+import android.view.WindowInsetsController
+import android.view.WindowInsetsController.APPEARANCE_OPAQUE_STATUS_BARS
+import androidx.test.filters.SmallTest
+import com.android.internal.statusbar.LetterboxDetails
+import com.android.internal.view.AppearanceRegion
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.mockito.Mockito.verify
+
+@SmallTest
+class StatusBarModeRepositoryImplTest : SysuiTestCase() {
+ private val testScope = TestScope()
+ private val commandQueue = mock<CommandQueue>()
+
+ private val underTest =
+ StatusBarModeRepositoryImpl(
+ testScope.backgroundScope,
+ DISPLAY_ID,
+ commandQueue,
+ )
+ .apply { this.start() }
+
+ private val commandQueueCallback: CommandQueue.Callbacks
+ get() {
+ val callbackCaptor = argumentCaptor<CommandQueue.Callbacks>()
+ verify(commandQueue).addCallback(callbackCaptor.capture())
+ return callbackCaptor.value
+ }
+
+ @Test
+ fun isTransientShown_commandQueueShow_wrongDisplayId_notUpdated() {
+ commandQueueCallback.showTransient(
+ DISPLAY_ID + 1,
+ WindowInsets.Type.statusBars(),
+ /* isGestureOnSystemBar= */ false,
+ )
+
+ assertThat(underTest.isTransientShown.value).isFalse()
+ }
+
+ @Test
+ fun isTransientShown_commandQueueShow_notStatusBarType_notUpdated() {
+ commandQueueCallback.showTransient(
+ DISPLAY_ID,
+ WindowInsets.Type.navigationBars(),
+ /* isGestureOnSystemBar= */ false,
+ )
+
+ assertThat(underTest.isTransientShown.value).isFalse()
+ }
+
+ @Test
+ fun isTransientShown_commandQueueShow_true() {
+ commandQueueCallback.showTransient(
+ DISPLAY_ID,
+ WindowInsets.Type.statusBars(),
+ /* isGestureOnSystemBar= */ false,
+ )
+
+ assertThat(underTest.isTransientShown.value).isTrue()
+ }
+
+ @Test
+ fun isTransientShown_commandQueueShow_statusBarAndOtherTypes_true() {
+ commandQueueCallback.showTransient(
+ DISPLAY_ID,
+ WindowInsets.Type.statusBars().or(WindowInsets.Type.navigationBars()),
+ /* isGestureOnSystemBar= */ false,
+ )
+
+ assertThat(underTest.isTransientShown.value).isTrue()
+ }
+
+ @Test
+ fun isTransientShown_commandQueueAbort_wrongDisplayId_notUpdated() {
+ // Start as true
+ commandQueueCallback.showTransient(
+ DISPLAY_ID,
+ WindowInsets.Type.statusBars(),
+ /* isGestureOnSystemBar= */ false,
+ )
+ assertThat(underTest.isTransientShown.value).isTrue()
+
+ // GIVEN the wrong display ID
+ commandQueueCallback.abortTransient(DISPLAY_ID + 1, WindowInsets.Type.statusBars())
+
+ // THEN the old value remains
+ assertThat(underTest.isTransientShown.value).isTrue()
+ }
+
+ @Test
+ fun isTransientShown_commandQueueAbort_notStatusBarType_notUpdated() {
+ // Start as true
+ commandQueueCallback.showTransient(
+ DISPLAY_ID,
+ WindowInsets.Type.statusBars(),
+ /* isGestureOnSystemBar= */ false,
+ )
+ assertThat(underTest.isTransientShown.value).isTrue()
+
+ // GIVEN the wrong type
+ commandQueueCallback.abortTransient(DISPLAY_ID, WindowInsets.Type.navigationBars())
+
+ // THEN the old value remains
+ assertThat(underTest.isTransientShown.value).isTrue()
+ }
+
+ @Test
+ fun isTransientShown_commandQueueAbort_false() {
+ // Start as true
+ commandQueueCallback.showTransient(
+ DISPLAY_ID,
+ WindowInsets.Type.statusBars(),
+ /* isGestureOnSystemBar= */ false,
+ )
+ assertThat(underTest.isTransientShown.value).isTrue()
+
+ commandQueueCallback.abortTransient(DISPLAY_ID, WindowInsets.Type.statusBars())
+
+ assertThat(underTest.isTransientShown.value).isFalse()
+ }
+
+ @Test
+ fun isTransientShown_commandQueueAbort_statusBarAndOtherTypes_false() {
+ // Start as true
+ commandQueueCallback.showTransient(
+ DISPLAY_ID,
+ WindowInsets.Type.statusBars(),
+ /* isGestureOnSystemBar= */ false,
+ )
+ assertThat(underTest.isTransientShown.value).isTrue()
+
+ commandQueueCallback.abortTransient(
+ DISPLAY_ID,
+ WindowInsets.Type.statusBars().or(WindowInsets.Type.captionBar()),
+ )
+
+ assertThat(underTest.isTransientShown.value).isFalse()
+ }
+
+ @Test
+ fun isTransientShown_showTransient_true() {
+ underTest.showTransient()
+
+ assertThat(underTest.isTransientShown.value).isTrue()
+ }
+
+ @Test
+ fun isTransientShown_clearTransient_false() {
+ // Start as true
+ commandQueueCallback.showTransient(
+ DISPLAY_ID,
+ WindowInsets.Type.statusBars(),
+ /* isGestureOnSystemBar= */ false,
+ )
+ assertThat(underTest.isTransientShown.value).isTrue()
+
+ underTest.clearTransient()
+
+ assertThat(underTest.isTransientShown.value).isFalse()
+ }
+
+ @Test
+ fun isInFullscreenMode_visibleTypesHasStatusBar_false() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.isInFullscreenMode)
+
+ onSystemBarAttributesChanged(
+ requestedVisibleTypes = WindowInsets.Type.statusBars(),
+ )
+
+ assertThat(latest).isFalse()
+ }
+
+ @Test
+ fun isInFullscreenMode_visibleTypesDoesNotHaveStatusBar_true() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.isInFullscreenMode)
+
+ onSystemBarAttributesChanged(
+ requestedVisibleTypes = WindowInsets.Type.navigationBars(),
+ )
+
+ assertThat(latest).isTrue()
+ }
+
+ @Test
+ fun isInFullscreenMode_wrongDisplayId_notUpdated() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.isInFullscreenMode)
+
+ onSystemBarAttributesChanged(
+ requestedVisibleTypes = WindowInsets.Type.navigationBars(),
+ )
+ assertThat(latest).isTrue()
+
+ onSystemBarAttributesChanged(
+ displayId = DISPLAY_ID + 1,
+ requestedVisibleTypes = WindowInsets.Type.statusBars(),
+ )
+
+ assertThat(latest).isTrue()
+ }
+
+ private fun onSystemBarAttributesChanged(
+ displayId: Int = DISPLAY_ID,
+ @WindowInsetsController.Appearance appearance: Int = APPEARANCE_OPAQUE_STATUS_BARS,
+ appearanceRegions: Array<AppearanceRegion> = emptyArray(),
+ navbarColorManagedByIme: Boolean = false,
+ @WindowInsetsController.Behavior behavior: Int = WindowInsetsController.BEHAVIOR_DEFAULT,
+ @WindowInsets.Type.InsetsType
+ requestedVisibleTypes: Int = WindowInsets.Type.defaultVisible(),
+ packageName: String = "package name",
+ letterboxDetails: Array<LetterboxDetails> = emptyArray(),
+ ) {
+ commandQueueCallback.onSystemBarAttributesChanged(
+ displayId,
+ appearance,
+ appearanceRegions,
+ navbarColorManagedByIme,
+ behavior,
+ requestedVisibleTypes,
+ packageName,
+ letterboxDetails,
+ )
+ }
+
+ private companion object {
+ const val DISPLAY_ID = 5
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/BigPictureIconManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/BigPictureIconManagerTest.kt
new file mode 100644
index 0000000..c650e01
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/BigPictureIconManagerTest.kt
@@ -0,0 +1,359 @@
+/*
+ * 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.systemui.statusbar.notification.row
+
+import android.graphics.BitmapFactory
+import android.graphics.drawable.BitmapDrawable
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.Icon
+import android.net.Uri
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.internal.widget.NotificationDrawableConsumer
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.graphics.ImageLoader
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.advanceTimeBy
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.reset
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyZeroInteractions
+
+private const val FREE_IMAGE_DELAY_MS = 4000L
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class BigPictureIconManagerTest : SysuiTestCase() {
+
+ private val testDispatcher = StandardTestDispatcher()
+ private val testScope = TestScope(testDispatcher)
+ private val testableResources = context.orCreateTestableResources
+ private val imageLoader: ImageLoader = ImageLoader(context, testDispatcher)
+ private var mockConsumer: NotificationDrawableConsumer = mock()
+ private val drawableCaptor = argumentCaptor<Drawable>()
+
+ private lateinit var iconManager: BigPictureIconManager
+
+ private val expectedDrawable by lazy {
+ context.resources.getDrawable(R.drawable.dessert_zombiegingerbread, context.theme)
+ }
+ private val supportedIcon by lazy {
+ Icon.createWithContentUri(
+ Uri.parse(
+ "android.resource://${context.packageName}/${R.drawable.dessert_zombiegingerbread}"
+ )
+ )
+ }
+ private val unsupportedIcon by lazy {
+ Icon.createWithBitmap(
+ BitmapFactory.decodeResource(context.resources, R.drawable.dessert_zombiegingerbread)
+ )
+ }
+ private val invalidIcon by lazy { Icon.createWithContentUri(Uri.parse("this.is/broken")) }
+
+ @Before
+ fun setUp() {
+ iconManager =
+ BigPictureIconManager(
+ context,
+ imageLoader,
+ scope = testScope,
+ mainDispatcher = testDispatcher,
+ bgDispatcher = testDispatcher
+ )
+ }
+
+ @Test
+ fun onIconUpdated_supportedType_placeholderLoaded() =
+ testScope.runTest {
+ // WHEN update with a supported icon
+ iconManager.updateIcon(mockConsumer, supportedIcon).run()
+
+ // THEN consumer is updated with a placeholder
+ verify(mockConsumer).setImageDrawable(drawableCaptor.capture())
+ assertIsPlaceHolder(drawableCaptor.value)
+ assertSize(drawableCaptor.value)
+ }
+
+ @Test
+ fun onIconUpdated_notSupportedType_fullImageLoaded() =
+ testScope.runTest {
+ // WHEN update with an unsupported icon
+ iconManager.updateIcon(mockConsumer, unsupportedIcon).run()
+
+ // THEN consumer is updated with the full image
+ verify(mockConsumer).setImageDrawable(drawableCaptor.capture())
+ assertIsFullImage(drawableCaptor.value)
+ assertSize(drawableCaptor.value)
+ }
+
+ @Test
+ fun onIconUpdated_invalidIcon_drawableIsNull() =
+ testScope.runTest {
+ // WHEN update with an invalid icon
+ iconManager.updateIcon(mockConsumer, invalidIcon).run()
+
+ // THEN consumer is updated with null
+ verify(mockConsumer).setImageDrawable(null)
+ }
+
+ @Test
+ fun onIconUpdated_consumerAlreadySet_nothingHappens() =
+ testScope.runTest {
+ // GIVEN a consumer is set
+ val otherConsumer: NotificationDrawableConsumer = mock()
+ iconManager.updateIcon(mockConsumer, supportedIcon).run()
+ reset(mockConsumer)
+
+ // WHEN a new consumer is set
+ iconManager.updateIcon(otherConsumer, unsupportedIcon).run()
+
+ // THEN nothing happens
+ verifyZeroInteractions(mockConsumer, otherConsumer)
+ }
+
+ @Test
+ fun onIconUpdated_iconAlreadySet_loadsNewIcon() =
+ testScope.runTest {
+ // GIVEN an icon is set
+ iconManager.updateIcon(mockConsumer, supportedIcon).run()
+ reset(mockConsumer)
+
+ // WHEN a new icon is set
+ iconManager.updateIcon(mockConsumer, unsupportedIcon).run()
+
+ // THEN consumer is updated with the new image
+ verify(mockConsumer).setImageDrawable(drawableCaptor.capture())
+ assertIsFullImage(drawableCaptor.value)
+ assertSize(drawableCaptor.value)
+ }
+
+ @Test
+ fun onIconUpdated_supportedTypeButTooWide_resizedPlaceholderLoaded() =
+ testScope.runTest {
+ // GIVEN the max width is smaller than our image
+ testableResources.addOverride(
+ com.android.internal.R.dimen.notification_big_picture_max_width,
+ 20
+ )
+ iconManager.updateMaxImageSizes()
+
+ // WHEN update with a supported icon
+ iconManager.updateIcon(mockConsumer, supportedIcon).run()
+
+ // THEN consumer is updated with the resized placeholder
+ verify(mockConsumer).setImageDrawable(drawableCaptor.capture())
+ assertIsPlaceHolder(drawableCaptor.value)
+ assertSize(drawableCaptor.value, expectedWidth = 20, expectedHeight = 20)
+ }
+
+ @Test
+ fun onIconUpdated_supportedTypeButTooHigh_resizedPlaceholderLoaded() =
+ testScope.runTest {
+ // GIVEN the max height is smaller than our image
+ testableResources.addOverride(
+ com.android.internal.R.dimen.notification_big_picture_max_height,
+ 20
+ )
+ iconManager.updateMaxImageSizes()
+
+ // WHEN update with a supported icon
+ iconManager.updateIcon(mockConsumer, supportedIcon).run()
+
+ // THEN consumer is updated with the resized placeholder
+ verify(mockConsumer).setImageDrawable(drawableCaptor.capture())
+ assertIsPlaceHolder(drawableCaptor.value)
+ assertSize(drawableCaptor.value, expectedWidth = 20, expectedHeight = 20)
+ }
+
+ @Test
+ fun onViewShown_placeholderShowing_fullImageLoaded() =
+ testScope.runTest {
+ // GIVEN placeholder is showing
+ iconManager.updateIcon(mockConsumer, supportedIcon).run()
+ reset(mockConsumer)
+
+ // WHEN the view is shown
+ iconManager.onViewShown(true)
+ runCurrent()
+
+ // THEN full image is set
+ verify(mockConsumer).setImageDrawable(drawableCaptor.capture())
+ assertIsFullImage(drawableCaptor.value)
+ assertSize(drawableCaptor.value)
+ }
+
+ @Test
+ fun onViewHidden_fullImageShowing_placeHolderSet() =
+ testScope.runTest {
+ // GIVEN full image is showing and the view is shown
+ iconManager.updateIcon(mockConsumer, supportedIcon).run()
+ iconManager.onViewShown(true)
+ runCurrent()
+ reset(mockConsumer)
+
+ // WHEN the view goes off the screen
+ iconManager.onViewShown(false)
+ // AND we wait a bit
+ advanceTimeBy(FREE_IMAGE_DELAY_MS)
+ runCurrent()
+
+ // THEN placeholder is set
+ verify(mockConsumer).setImageDrawable(drawableCaptor.capture())
+ assertIsPlaceHolder(drawableCaptor.value)
+ assertSize(drawableCaptor.value)
+ }
+
+ @Test
+ fun onViewShownToggled_viewShown_nothingHappens() =
+ testScope.runTest {
+ // GIVEN full image is showing and the view is shown
+ iconManager.updateIcon(mockConsumer, supportedIcon).run()
+ iconManager.onViewShown(true)
+ runCurrent()
+ reset(mockConsumer)
+
+ // WHEN the onViewShown is toggled
+ iconManager.onViewShown(false)
+ runCurrent()
+ iconManager.onViewShown(true)
+ // AND we wait a bit
+ advanceTimeBy(FREE_IMAGE_DELAY_MS)
+ runCurrent()
+
+ // THEN nothing happens
+ verifyZeroInteractions(mockConsumer)
+ }
+
+ // nice to have tests
+
+ @Test
+ fun onViewShown_fullImageLoaded_nothingHappens() =
+ testScope.runTest {
+ // GIVEN full image is showing
+ iconManager.updateIcon(mockConsumer, unsupportedIcon).run()
+ reset(mockConsumer)
+
+ // WHEN the view is shown
+ iconManager.onViewShown(true)
+ runCurrent()
+
+ // THEN nothing happens
+ verifyZeroInteractions(mockConsumer)
+ }
+
+ @Test
+ fun onViewHidden_placeholderShowing_nothingHappens() =
+ testScope.runTest {
+ // GIVEN placeholder image is showing
+ iconManager.updateIcon(mockConsumer, unsupportedIcon).run()
+ reset(mockConsumer)
+
+ // WHEN the view is hidden
+ iconManager.onViewShown(false)
+ // AND we wait a bit
+ advanceTimeBy(FREE_IMAGE_DELAY_MS)
+ runCurrent()
+
+ // THEN nothing happens
+ verifyZeroInteractions(mockConsumer)
+ }
+
+ @Test
+ fun onViewShown_alreadyShowing_nothingHappens() =
+ testScope.runTest {
+ // GIVEN full image is showing and the view is shown
+ iconManager.updateIcon(mockConsumer, supportedIcon).run()
+ iconManager.onViewShown(true)
+ runCurrent()
+ reset(mockConsumer)
+
+ // WHEN view shown called again
+ iconManager.onViewShown(true)
+ runCurrent()
+
+ verifyZeroInteractions(mockConsumer)
+ }
+
+ @Test
+ fun onViewHidden_alreadyHidden_nothingHappens() =
+ testScope.runTest {
+ // GIVEN placeholder image is showing and the view is hidden
+ iconManager.updateIcon(mockConsumer, unsupportedIcon).run()
+ iconManager.onViewShown(false)
+ advanceTimeBy(FREE_IMAGE_DELAY_MS)
+ runCurrent()
+ reset(mockConsumer)
+
+ // WHEN the view is hidden again
+ iconManager.onViewShown(false)
+ // AND we wait a bit
+ advanceTimeBy(FREE_IMAGE_DELAY_MS)
+ runCurrent()
+
+ // THEN nothing happens
+ verifyZeroInteractions(mockConsumer)
+ }
+
+ @Test
+ fun cancelJobs_freeImageJobRunning_jobCancelled() =
+ testScope.runTest {
+ // GIVEN full image is showing
+ iconManager.updateIcon(mockConsumer, supportedIcon).run()
+ iconManager.onViewShown(true)
+ runCurrent()
+ reset(mockConsumer)
+ // AND the view has just gone off the screen
+ iconManager.onViewShown(false)
+
+ // WHEN cancelJobs is called
+ iconManager.cancelJobs()
+ // AND we wait a bit
+ advanceTimeBy(FREE_IMAGE_DELAY_MS)
+ runCurrent()
+
+ // THEN no more updates are happening
+ verifyZeroInteractions(mockConsumer)
+ }
+
+ private fun assertIsPlaceHolder(drawable: Drawable) {
+ assertThat(drawable).isInstanceOf(PlaceHolderDrawable::class.java)
+ }
+
+ private fun assertIsFullImage(drawable: Drawable) {
+ assertThat(drawable).isInstanceOf(BitmapDrawable::class.java)
+ }
+
+ private fun assertSize(
+ drawable: Drawable,
+ expectedWidth: Int = expectedDrawable.intrinsicWidth,
+ expectedHeight: Int = expectedDrawable.intrinsicHeight
+ ) {
+ assertThat(drawable.intrinsicWidth).isEqualTo(expectedWidth)
+ assertThat(drawable.intrinsicHeight).isEqualTo(expectedHeight)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index b3f5ea2..5606216 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -180,7 +180,7 @@
allowTestableLooperAsMainThread();
MockitoAnnotations.initMocks(this);
- mFeatureFlags.set(Flags.USE_REPOS_FOR_BOUNCER_SHOWING, false);
+ mFeatureFlags.set(Flags.USE_REPOS_FOR_BOUNCER_SHOWING, true);
when(mNotificationSwipeHelperBuilder.build()).thenReturn(mNotificationSwipeHelper);
when(mKeyguardTransitionRepo.getTransitions()).thenReturn(emptyFlow());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
index 3ad3c15..c71c0c57 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
@@ -50,6 +50,7 @@
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.recents.ScreenPinningRequest;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.CameraLauncher;
import com.android.systemui.shade.QuickSettingsController;
@@ -79,6 +80,7 @@
public class CentralSurfacesCommandQueueCallbacksTest extends SysuiTestCase {
@Mock private CentralSurfaces mCentralSurfaces;
+ @Mock private ScreenPinningRequest mScreenPinningRequest;
@Mock private ShadeController mShadeController;
@Mock private CommandQueue mCommandQueue;
@Mock private QuickSettingsController mQuickSettingsController;
@@ -116,6 +118,7 @@
mQuickSettingsController,
mContext,
mContext.getResources(),
+ mScreenPinningRequest,
mShadeController,
mCommandQueue,
mShadeViewController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index bd3fb9f..26c0fd6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -82,6 +82,7 @@
import com.android.internal.logging.testing.FakeMetricsLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.TestScopeProvider;
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.InitController;
import com.android.systemui.R;
@@ -116,7 +117,6 @@
import com.android.systemui.plugins.PluginManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.power.domain.interactor.PowerInteractor;
-import com.android.systemui.recents.ScreenPinningRequest;
import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.settings.brightness.BrightnessSliderController;
@@ -146,6 +146,7 @@
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.StatusBarStateControllerImpl;
import com.android.systemui.statusbar.core.StatusBarInitializer;
+import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepository;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
@@ -178,6 +179,7 @@
import com.android.systemui.util.WallpaperController;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.concurrency.MessageRouterImpl;
+import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.volume.VolumeComponent;
import com.android.wm.shell.bubbles.Bubbles;
@@ -285,7 +287,6 @@
@Mock private PluginManager mPluginManager;
@Mock private ViewMediatorCallback mViewMediatorCallback;
@Mock private StatusBarTouchableRegionManager mStatusBarTouchableRegionManager;
- @Mock private ScreenPinningRequest mScreenPinningRequest;
@Mock private PluginDependencyProvider mPluginDependencyProvider;
@Mock private ExtensionController mExtensionController;
@Mock private UserInfoControllerImpl mUserInfoControllerImpl;
@@ -454,6 +455,7 @@
emptySet()),
mStatusBarWindowController,
mStatusBarWindowStateController,
+ new FakeStatusBarModeRepository(),
mKeyguardUpdateMonitor,
mStatusBarSignalPolicy,
mPulseExpansionHandler,
@@ -472,6 +474,7 @@
new DisplayMetrics(),
mMetricsLogger,
mShadeLogger,
+ new JavaAdapter(TestScopeProvider.getTestScope()),
mUiBgExecutor,
mNotificationPanelViewController,
mNotificationMediaManager,
@@ -508,7 +511,6 @@
mDozeServiceHost,
mBackActionInteractor,
mPowerManager,
- mScreenPinningRequest,
mDozeScrimController,
mVolumeComponent,
mCommandQueue,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
index d100c687..79f8dbd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
@@ -30,6 +30,8 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -51,6 +53,8 @@
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.battery.BatteryMeterViewController;
+import com.android.systemui.flags.FakeFeatureFlagsClassic;
+import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shade.ShadeViewStateProvider;
import com.android.systemui.statusbar.CommandQueue;
@@ -79,6 +83,7 @@
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class KeyguardStatusBarViewControllerTest extends SysuiTestCase {
+ private final FakeFeatureFlagsClassic mFeatureFlags = new FakeFeatureFlagsClassic();
@Mock
private CarrierTextController mCarrierTextController;
@Mock
@@ -131,6 +136,7 @@
@Before
public void setup() throws Exception {
+ mFeatureFlags.set(Flags.MIGRATE_KEYGUARD_STATUS_BAR_VIEW, false);
mShadeViewStateProvider = new TestShadeViewStateProvider();
MockitoAnnotations.initMocks(this);
@@ -166,6 +172,7 @@
mBiometricUnlockController,
mStatusBarStateController,
mStatusBarContentInsetsProvider,
+ mFeatureFlags,
mUserManager,
mStatusBarUserChipViewModel,
mSecureSettings,
@@ -228,6 +235,30 @@
}
@Test
+ public void onViewReAttached_flagOff_iconManagerNotReRegistered() {
+ mFeatureFlags.set(Flags.MIGRATE_KEYGUARD_STATUS_BAR_VIEW, false);
+ mController.onViewAttached();
+ mController.onViewDetached();
+ reset(mStatusBarIconController);
+
+ mController.onViewAttached();
+
+ verify(mStatusBarIconController, never()).addIconGroup(any());
+ }
+
+ @Test
+ public void onViewReAttached_flagOn_iconManagerReRegistered() {
+ mFeatureFlags.set(Flags.MIGRATE_KEYGUARD_STATUS_BAR_VIEW, true);
+ mController.onViewAttached();
+ mController.onViewDetached();
+ reset(mStatusBarIconController);
+
+ mController.onViewAttached();
+
+ verify(mStatusBarIconController).addIconGroup(any());
+ }
+
+ @Test
public void setBatteryListening_true_callbackAdded() {
mController.setBatteryListening(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index ba4e8d3..bac8579 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -976,4 +976,23 @@
verify(mKeyguardMessageAreaController).setIsVisible(eq(false));
verify(mKeyguardMessageAreaController).setMessage(eq(""));
}
+
+ @Test
+ public void testShowBouncerOrKeyguard_needsFullScreen() {
+ when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(
+ KeyguardSecurityModel.SecurityMode.SimPin);
+ mStatusBarKeyguardViewManager.showBouncerOrKeyguard(false);
+ verify(mCentralSurfaces).hideKeyguard();
+ verify(mPrimaryBouncerInteractor).show(true);
+ }
+
+ @Test
+ public void testShowBouncerOrKeyguard_needsFullScreen_bouncerAlreadyShowing() {
+ when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(
+ KeyguardSecurityModel.SecurityMode.SimPin);
+ when(mPrimaryBouncerInteractor.isFullyShowing()).thenReturn(true);
+ mStatusBarKeyguardViewManager.showBouncerOrKeyguard(false);
+ verify(mCentralSurfaces, never()).hideKeyguard();
+ verify(mPrimaryBouncerInteractor, never()).show(true);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemBarAttributesListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemBarAttributesListenerTest.kt
index 4d79a53..0cd2214 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemBarAttributesListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemBarAttributesListenerTest.kt
@@ -11,11 +11,9 @@
import com.android.internal.view.AppearanceRegion
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
-import com.android.systemui.statusbar.SysuiStatusBarStateController
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.eq
@@ -33,7 +31,6 @@
@Mock private lateinit var dumpManager: DumpManager
@Mock private lateinit var lightBarController: LightBarController
- @Mock private lateinit var statusBarStateController: SysuiStatusBarStateController
@Mock private lateinit var letterboxAppearanceCalculator: LetterboxAppearanceCalculator
@Mock private lateinit var centralSurfaces: CentralSurfaces
@@ -52,7 +49,6 @@
SystemBarAttributesListener(
centralSurfaces,
letterboxAppearanceCalculator,
- statusBarStateController,
lightBarController,
dumpManager)
}
@@ -81,23 +77,6 @@
}
@Test
- fun onSysBarAttrsChanged_forwardsAppearanceToStatusBarStateController() {
- changeSysBarAttrs(TEST_APPEARANCE)
-
- verify(statusBarStateController)
- .setSystemBarAttributes(eq(TEST_APPEARANCE), anyInt(), anyInt(), any())
- }
-
- @Test
- fun onSysBarAttrsChanged_forwardsLetterboxAppearanceToStatusBarStateCtrl() {
- changeSysBarAttrs(TEST_APPEARANCE, TEST_LETTERBOX_DETAILS)
-
- verify(statusBarStateController)
- .setSystemBarAttributes(
- eq(TEST_LETTERBOX_APPEARANCE.appearance), anyInt(), anyInt(), any())
- }
-
- @Test
fun onSysBarAttrsChanged_forwardsAppearanceToLightBarController() {
changeSysBarAttrs(TEST_APPEARANCE, TEST_APPEARANCE_REGIONS)
@@ -119,21 +98,9 @@
}
@Test
- fun onStatusBarBoundsChanged_forwardsLetterboxAppearanceToStatusBarStateController() {
- changeSysBarAttrs(TEST_APPEARANCE, TEST_APPEARANCE_REGIONS, TEST_LETTERBOX_DETAILS)
- reset(centralSurfaces, lightBarController, statusBarStateController)
-
- sysBarAttrsListener.onStatusBarBoundsChanged()
-
- verify(statusBarStateController)
- .setSystemBarAttributes(
- eq(TEST_LETTERBOX_APPEARANCE.appearance), anyInt(), anyInt(), any())
- }
-
- @Test
fun onStatusBarBoundsChanged_forwardsLetterboxAppearanceToLightBarController() {
changeSysBarAttrs(TEST_APPEARANCE, TEST_APPEARANCE_REGIONS, TEST_LETTERBOX_DETAILS)
- reset(centralSurfaces, lightBarController, statusBarStateController)
+ reset(centralSurfaces, lightBarController)
sysBarAttrsListener.onStatusBarBoundsChanged()
@@ -148,7 +115,7 @@
@Test
fun onStatusBarBoundsChanged_forwardsLetterboxAppearanceToCentralSurfaces() {
changeSysBarAttrs(TEST_APPEARANCE, TEST_APPEARANCE_REGIONS, TEST_LETTERBOX_DETAILS)
- reset(centralSurfaces, lightBarController, statusBarStateController)
+ reset(centralSurfaces, lightBarController)
sysBarAttrsListener.onStatusBarBoundsChanged()
@@ -158,11 +125,11 @@
@Test
fun onStatusBarBoundsChanged_previousCallEmptyLetterbox_doesNothing() {
changeSysBarAttrs(TEST_APPEARANCE, TEST_APPEARANCE_REGIONS, arrayOf())
- reset(centralSurfaces, lightBarController, statusBarStateController)
+ reset(centralSurfaces, lightBarController)
sysBarAttrsListener.onStatusBarBoundsChanged()
- verifyZeroInteractions(centralSurfaces, lightBarController, statusBarStateController)
+ verifyZeroInteractions(centralSurfaces, lightBarController)
}
private fun changeSysBarAttrs(@Appearance appearance: Int) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
index 711e4ac..dc5d55e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
@@ -34,17 +34,20 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.gesture.SwipeStatusBarAwayGestureHandler
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
+import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepository
import com.android.systemui.statusbar.window.StatusBarWindowController
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
import org.junit.After
import org.junit.Before
import org.junit.Test
@@ -62,7 +65,6 @@
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
-import java.util.*
private const val CALL_UID = 900
@@ -75,23 +77,24 @@
@SmallTest
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper
+@OptIn(ExperimentalCoroutinesApi::class)
class OngoingCallControllerTest : SysuiTestCase() {
private val clock = FakeSystemClock()
private val mainExecutor = FakeExecutor(clock)
private val uiEventLoggerFake = UiEventLoggerFake()
+ private val testScope = TestScope()
+ private val statusBarModeRepository = FakeStatusBarModeRepository()
private lateinit var controller: OngoingCallController
private lateinit var notifCollectionListener: NotifCollectionListener
- @Mock private lateinit var mockOngoingCallFlags: OngoingCallFlags
@Mock private lateinit var mockSwipeStatusBarAwayGestureHandler:
SwipeStatusBarAwayGestureHandler
@Mock private lateinit var mockOngoingCallListener: OngoingCallListener
@Mock private lateinit var mockActivityStarter: ActivityStarter
@Mock private lateinit var mockIActivityManager: IActivityManager
@Mock private lateinit var mockStatusBarWindowController: StatusBarWindowController
- @Mock private lateinit var mockStatusBarStateController: StatusBarStateController
private lateinit var chipView: View
@@ -103,24 +106,23 @@
}
MockitoAnnotations.initMocks(this)
- `when`(mockOngoingCallFlags.isStatusBarChipEnabled()).thenReturn(true)
val notificationCollection = mock(CommonNotifCollection::class.java)
controller = OngoingCallController(
- context,
- notificationCollection,
- mockOngoingCallFlags,
- clock,
- mockActivityStarter,
- mainExecutor,
- mockIActivityManager,
- OngoingCallLogger(uiEventLoggerFake),
- DumpManager(),
- Optional.of(mockStatusBarWindowController),
- Optional.of(mockSwipeStatusBarAwayGestureHandler),
- mockStatusBarStateController,
- )
- controller.init()
+ testScope.backgroundScope,
+ context,
+ notificationCollection,
+ clock,
+ mockActivityStarter,
+ mainExecutor,
+ mockIActivityManager,
+ OngoingCallLogger(uiEventLoggerFake),
+ DumpManager(),
+ mockStatusBarWindowController,
+ mockSwipeStatusBarAwayGestureHandler,
+ statusBarModeRepository,
+ )
+ controller.start()
controller.addCallback(mockOngoingCallListener)
controller.setChipView(chipView)
@@ -494,44 +496,10 @@
}
@Test
- fun fullscreenIsTrue_thenCallNotificationAdded_chipNotClickable() {
- `when`(mockOngoingCallFlags.isInImmersiveChipTapEnabled()).thenReturn(false)
-
- getStateListener().onFullscreenStateChanged(/* isFullscreen= */ true)
+ fun fullscreenIsTrue_chipStillClickable() {
notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
-
- assertThat(chipView.hasOnClickListeners()).isFalse()
- }
-
- @Test
- fun callNotificationAdded_thenFullscreenIsTrue_chipNotClickable() {
- `when`(mockOngoingCallFlags.isInImmersiveChipTapEnabled()).thenReturn(false)
-
- notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
- getStateListener().onFullscreenStateChanged(/* isFullscreen= */ true)
-
- assertThat(chipView.hasOnClickListeners()).isFalse()
- }
-
- @Test
- fun fullscreenChangesToFalse_chipClickable() {
- `when`(mockOngoingCallFlags.isInImmersiveChipTapEnabled()).thenReturn(false)
-
- notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
- // First, update to true
- getStateListener().onFullscreenStateChanged(/* isFullscreen= */ true)
- // Then, update to false
- getStateListener().onFullscreenStateChanged(/* isFullscreen= */ false)
-
- assertThat(chipView.hasOnClickListeners()).isTrue()
- }
-
- @Test
- fun fullscreenIsTrue_butChipClickInImmersiveEnabled_chipClickable() {
- `when`(mockOngoingCallFlags.isInImmersiveChipTapEnabled()).thenReturn(true)
-
- notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
- getStateListener().onFullscreenStateChanged(/* isFullscreen= */ true)
+ statusBarModeRepository.isInFullscreenMode.value = true
+ testScope.runCurrent()
assertThat(chipView.hasOnClickListeners()).isTrue()
}
@@ -540,7 +508,8 @@
@Test
fun callStartedInImmersiveMode_swipeGestureCallbackAdded() {
- getStateListener().onFullscreenStateChanged(true)
+ statusBarModeRepository.isInFullscreenMode.value = true
+ testScope.runCurrent()
notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
@@ -550,7 +519,8 @@
@Test
fun callStartedNotInImmersiveMode_swipeGestureCallbackNotAdded() {
- getStateListener().onFullscreenStateChanged(false)
+ statusBarModeRepository.isInFullscreenMode.value = false
+ testScope.runCurrent()
notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
@@ -562,7 +532,8 @@
fun transitionToImmersiveMode_swipeGestureCallbackAdded() {
notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
- getStateListener().onFullscreenStateChanged(true)
+ statusBarModeRepository.isInFullscreenMode.value = true
+ testScope.runCurrent()
verify(mockSwipeStatusBarAwayGestureHandler)
.addOnGestureDetectedCallback(anyString(), any())
@@ -570,11 +541,12 @@
@Test
fun transitionOutOfImmersiveMode_swipeGestureCallbackRemoved() {
- getStateListener().onFullscreenStateChanged(true)
+ statusBarModeRepository.isInFullscreenMode.value = true
notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
reset(mockSwipeStatusBarAwayGestureHandler)
- getStateListener().onFullscreenStateChanged(false)
+ statusBarModeRepository.isInFullscreenMode.value = false
+ testScope.runCurrent()
verify(mockSwipeStatusBarAwayGestureHandler)
.removeOnGestureDetectedCallback(anyString())
@@ -582,7 +554,8 @@
@Test
fun callEndedWhileInImmersiveMode_swipeGestureCallbackRemoved() {
- getStateListener().onFullscreenStateChanged(true)
+ statusBarModeRepository.isInFullscreenMode.value = true
+ testScope.runCurrent()
val ongoingCallNotifEntry = createOngoingCallNotifEntry()
notifCollectionListener.onEntryAdded(ongoingCallNotifEntry)
reset(mockSwipeStatusBarAwayGestureHandler)
@@ -623,13 +596,6 @@
}
private fun createNotCallNotifEntry() = NotificationEntryBuilder().build()
-
- private fun getStateListener(): StatusBarStateController.StateListener {
- val statusBarStateListenerCaptor = ArgumentCaptor.forClass(
- StatusBarStateController.StateListener::class.java)
- verify(mockStatusBarStateController).addCallback(statusBarStateListenerCaptor.capture())
- return statusBarStateListenerCaptor.value!!
- }
}
private val person = Person.Builder().setName("name").build()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java
index 7f3d4b7..66c5aaa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java
@@ -132,12 +132,6 @@
}
@Test
- public void testAddNullCallback() {
- mController.addCallback(null);
- mController.fireConfigChanged(null);
- }
-
- @Test
public void testModeChange() {
List<Integer> states = List.of(
Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
index 3152fd1..aa9d62da 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
@@ -41,6 +41,7 @@
import android.app.KeyguardManager;
import android.content.res.Configuration;
import android.media.AudioManager;
+import android.os.Handler;
import android.os.SystemClock;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -146,6 +147,10 @@
mTestableLooper = TestableLooper.get(this);
allowTestableLooperAsMainThread();
+ // Ensure previous tests have not left messages on main looper
+ Handler localHandler = new Handler(mTestableLooper.getLooper());
+ localHandler.removeCallbacksAndMessages(null);
+
when(mPostureController.getDevicePosture())
.thenReturn(DevicePostureController.DEVICE_POSTURE_CLOSED);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java
index 692af6a..c1d11aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java
@@ -459,7 +459,7 @@
WalletCard.CARD_TYPE_UNKNOWN),
createWalletCardWithType(mContext, WalletCard.CARD_TYPE_PAYMENT),
createWalletCardWithType(mContext, WalletCard.CARD_TYPE_NON_PAYMENT)
- );
+ );
GetWalletCardsResponse response = new GetWalletCardsResponse(walletCardList, 0);
mController.onWalletCardsRetrieved(response);
mTestableLooper.processAllMessages();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallet/util/WalletCardUtilsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/wallet/util/WalletCardUtilsTest.kt
new file mode 100644
index 0000000..e46c1f5
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallet/util/WalletCardUtilsTest.kt
@@ -0,0 +1,73 @@
+/*
+ * 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.systemui.wallet.util
+
+import android.service.quickaccesswallet.WalletCard
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/** Test class for WalletCardUtils */
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+@SmallTest
+class WalletCardUtilsTest : SysuiTestCase() {
+
+ private val paymentCard = createWalletCardWithType(WalletCard.CARD_TYPE_PAYMENT)
+ private val nonPaymentCard = createWalletCardWithType(WalletCard.CARD_TYPE_NON_PAYMENT)
+ private val unknownCard = createWalletCardWithType(WalletCard.CARD_TYPE_UNKNOWN)
+
+ @Test
+ fun paymentCards_cardTypesAllUnknown_getsAllCards() {
+ val walletCardList =
+ mutableListOf(
+ createWalletCardWithType(WalletCard.CARD_TYPE_UNKNOWN),
+ createWalletCardWithType(WalletCard.CARD_TYPE_UNKNOWN),
+ createWalletCardWithType(WalletCard.CARD_TYPE_UNKNOWN)
+ )
+
+ assertThat(walletCardList).isEqualTo(getPaymentCards(walletCardList))
+ }
+
+ @Test
+ fun paymentCards_cardTypesDifferent_onlyGetsPayment() {
+ val walletCardList = mutableListOf(paymentCard, nonPaymentCard, unknownCard)
+
+ assertThat(getPaymentCards(walletCardList)).isEqualTo(mutableListOf(paymentCard))
+ }
+
+ private fun createWalletCardWithType(cardType: Int): WalletCard {
+ return WalletCard.Builder(
+ /*cardId= */ CARD_ID,
+ /*cardType= */ cardType,
+ /*cardImage= */ mock(),
+ /*contentDescription= */ CARD_DESCRIPTION,
+ /*pendingIntent= */ mock()
+ )
+ .build()
+ }
+
+ companion object {
+ private const val CARD_ID: String = "ID"
+ private const val CARD_DESCRIPTION: String = "Description"
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
index ef0adbb..d2c8aea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
@@ -41,6 +41,7 @@
import com.android.wm.shell.onehanded.OneHandedEventCallback;
import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
import com.android.wm.shell.pip.Pip;
+import com.android.wm.shell.recents.RecentTasks;
import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.sysui.ShellInterface;
@@ -79,6 +80,7 @@
@Mock ShellExecutor mSysUiMainExecutor;
@Mock NoteTaskInitializer mNoteTaskInitializer;
@Mock DesktopMode mDesktopMode;
+ @Mock RecentTasks mRecentTasks;
@Before
public void setUp() {
@@ -91,6 +93,7 @@
Optional.of(mSplitScreen),
Optional.of(mOneHanded),
Optional.of(mDesktopMode),
+ Optional.of(mRecentTasks),
mCommandQueue,
mConfigurationController,
mKeyguardStateController,
@@ -129,4 +132,10 @@
any(DesktopModeTaskRepository.VisibleTasksListener.class),
any(Executor.class));
}
+
+ @Test
+ public void initRecentTasks_registersListener() {
+ mWMShell.initRecentTasks(mRecentTasks);
+ verify(mRecentTasks).addAnimationStateListener(any(Executor.class), any());
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt
new file mode 100644
index 0000000..e1c6dde
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt
@@ -0,0 +1,10 @@
+package com.android.systemui.communal.data.repository
+
+/** Fake implementation of [CommunalRepository]. */
+class FakeCommunalRepository : CommunalRepository {
+ override var isCommunalEnabled = false
+
+ fun setIsCommunalEnabled(value: Boolean) {
+ isCommunalEnabled = value
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt
index 2b13dca..322fb28 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt
@@ -56,20 +56,7 @@
_isLockedOut.value = isLockedOut
}
- private val faceAuthPaused = MutableStateFlow(false)
- override fun pauseFaceAuth() {
- faceAuthPaused.value = true
- }
-
- override fun resumeFaceAuth() {
- faceAuthPaused.value = false
- }
-
- fun isFaceAuthPaused(): Boolean {
- return faceAuthPaused.value
- }
-
- override suspend fun authenticate(uiEvent: FaceAuthUiEvent, fallbackToDetection: Boolean) {
+ override fun requestAuthenticate(uiEvent: FaceAuthUiEvent, fallbackToDetection: Boolean) {
_runningAuthRequest.value = uiEvent to fallbackToDetection
_isAuthRunning.value = true
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileDataInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileDataInteractor.kt
new file mode 100644
index 0000000..13437c9
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileDataInteractor.kt
@@ -0,0 +1,64 @@
+package com.android.systemui.qs.tiles.base.interactor
+
+import javax.annotation.CheckReturnValue
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.map
+
+class FakeQSTileDataInteractor<T>(
+ private val dataFlow: MutableSharedFlow<FakeData<T>> =
+ MutableSharedFlow(replay = Int.MAX_VALUE),
+ private val availabilityFlow: MutableSharedFlow<Boolean> =
+ MutableSharedFlow(replay = Int.MAX_VALUE),
+) : QSTileDataInteractor<T> {
+
+ private val mutableDataRequests = mutableListOf<QSTileDataRequest>()
+ val dataRequests: List<QSTileDataRequest> = mutableDataRequests
+
+ private val mutableAvailabilityRequests = mutableListOf<Unit>()
+ val availabilityRequests: List<Unit> = mutableAvailabilityRequests
+
+ @CheckReturnValue
+ fun emitData(data: T): FilterEmit =
+ object : FilterEmit {
+ override fun forRequest(request: QSTileDataRequest): Boolean =
+ dataFlow.tryEmit(FakeData(data, DataFilter.ForRequest(request)))
+ override fun forAnyRequest(): Boolean = dataFlow.tryEmit(FakeData(data, DataFilter.Any))
+ }
+
+ fun tryEmitAvailability(isAvailable: Boolean): Boolean = availabilityFlow.tryEmit(isAvailable)
+ suspend fun emitAvailability(isAvailable: Boolean) = availabilityFlow.emit(isAvailable)
+
+ override fun tileData(qsTileDataRequest: QSTileDataRequest): Flow<T> {
+ mutableDataRequests.add(qsTileDataRequest)
+ return dataFlow
+ .filter {
+ when (it.filter) {
+ is DataFilter.Any -> true
+ is DataFilter.ForRequest -> it.filter.request == qsTileDataRequest
+ }
+ }
+ .map { it.data }
+ }
+
+ override fun availability(): Flow<Boolean> {
+ mutableAvailabilityRequests.add(Unit)
+ return availabilityFlow
+ }
+
+ interface FilterEmit {
+ fun forRequest(request: QSTileDataRequest): Boolean
+ fun forAnyRequest(): Boolean
+ }
+
+ class FakeData<T>(
+ val data: T,
+ val filter: DataFilter,
+ )
+
+ sealed class DataFilter {
+ object Any : DataFilter()
+ class ForRequest(val request: QSTileDataRequest) : DataFilter()
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileUserActionInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileUserActionInteractor.kt
new file mode 100644
index 0000000..4e0266e
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileUserActionInteractor.kt
@@ -0,0 +1,21 @@
+package com.android.systemui.qs.tiles.base.interactor
+
+import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
+import kotlinx.coroutines.sync.Mutex
+import kotlinx.coroutines.sync.withLock
+
+class FakeQSTileUserActionInteractor<T> : QSTileUserActionInteractor<T> {
+
+ private val mutex: Mutex = Mutex()
+ private val mutableInputs: MutableList<FakeInput<T>> = mutableListOf()
+
+ val inputs: List<FakeInput<T>> = mutableInputs
+
+ fun lastInput(): FakeInput<T>? = inputs.lastOrNull()
+
+ override suspend fun handleInput(userAction: QSTileUserAction, currentData: T) {
+ mutex.withLock { mutableInputs.add(FakeInput(userAction, currentData)) }
+ }
+
+ data class FakeInput<T>(val userAction: QSTileUserAction, val data: T)
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
index 2d79e0f..1620dc27 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
@@ -31,6 +31,9 @@
import com.android.systemui.classifier.FalsingCollectorFake
import com.android.systemui.classifier.domain.interactor.FalsingInteractor
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
+import com.android.systemui.communal.data.repository.FakeCommunalRepository
+import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository
+import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.FakeCommandQueue
@@ -94,7 +97,10 @@
)
}
}
-
+ val communalRepository: FakeCommunalRepository by lazy { FakeCommunalRepository() }
+ private val communalWidgetRepository: FakeCommunalWidgetRepository by lazy {
+ FakeCommunalWidgetRepository()
+ }
val powerRepository: FakePowerRepository by lazy { FakePowerRepository() }
private val context = test.context
@@ -171,6 +177,13 @@
)
}
+ fun communalInteractor(): CommunalInteractor {
+ return CommunalInteractor(
+ communalRepository = communalRepository,
+ widgetRepository = communalWidgetRepository,
+ )
+ }
+
fun bouncerInteractor(
authenticationInteractor: AuthenticationInteractor,
sceneInteractor: SceneInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt
index 4242c16..f5f924d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt
@@ -22,7 +22,6 @@
import android.os.UserHandle
import android.test.mock.MockContentResolver
import com.android.systemui.util.mockito.mock
-import java.util.concurrent.CountDownLatch
import java.util.concurrent.Executor
/** A fake [UserTracker] to be used in tests. */
@@ -73,8 +72,7 @@
fun onUserChanging(userId: Int = _userId) {
val copy = callbacks.toList()
- val latch = CountDownLatch(copy.size)
- copy.forEach { it.onUserChanging(userId, userContext, latch) }
+ copy.forEach { it.onUserChanging(userId, userContext) {} }
}
fun onUserChanged(userId: Int = _userId) {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
index e72544a..8b721b2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
@@ -47,6 +47,17 @@
private val _legacyQsTracking = MutableStateFlow(false)
@Deprecated("Use ShadeInteractor instead") override val legacyQsTracking = _legacyQsTracking
+ private val _legacyExpandedOrAwaitingInputTransfer = MutableStateFlow(false)
+ @Deprecated("Use ShadeInteractor instead")
+ override val legacyExpandedOrAwaitingInputTransfer = _legacyExpandedOrAwaitingInputTransfer
+
+ @Deprecated("Use ShadeInteractor instead")
+ override fun setLegacyExpandedOrAwaitingInputTransfer(
+ legacyExpandedOrAwaitingInputTransfer: Boolean
+ ) {
+ _legacyExpandedOrAwaitingInputTransfer.value = legacyExpandedOrAwaitingInputTransfer
+ }
+
@Deprecated("Should only be called by NPVC and tests")
override fun setLegacyQsTracking(legacyQsTracking: Boolean) {
_legacyQsTracking.value = legacyQsTracking
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 05b6eb4..44ffb51 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -56,7 +56,6 @@
import android.graphics.Region;
import android.hardware.HardwareBuffer;
import android.hardware.display.DisplayManager;
-import android.hardware.display.DisplayManagerInternal;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
@@ -98,7 +97,6 @@
import com.android.internal.os.SomeArgs;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.function.pooled.PooledLambda;
-import com.android.server.LocalServices;
import com.android.server.accessibility.AccessibilityWindowManager.RemoteAccessibilityConnection;
import com.android.server.accessibility.magnification.MagnificationProcessor;
import com.android.server.inputmethod.InputMethodManagerInternal;
@@ -1441,19 +1439,24 @@
AccessibilityService.ERROR_TAKE_SCREENSHOT_INVALID_DISPLAY, callback);
return;
}
-
final long identity = Binder.clearCallingIdentity();
try {
- mMainHandler.post(PooledLambda.obtainRunnable((nonArg) -> {
- final ScreenshotHardwareBuffer screenshotBuffer = LocalServices
- .getService(DisplayManagerInternal.class).userScreenshot(displayId);
- if (screenshotBuffer != null) {
- sendScreenshotSuccess(screenshotBuffer, callback);
- } else {
- sendScreenshotFailure(
- AccessibilityService.ERROR_TAKE_SCREENSHOT_INVALID_DISPLAY, callback);
- }
- }, null).recycleOnUse());
+ ScreenCapture.ScreenCaptureListener screenCaptureListener = new
+ ScreenCapture.ScreenCaptureListener(
+ (screenshotBuffer, result) -> {
+ if (screenshotBuffer != null && result == 0) {
+ sendScreenshotSuccess(screenshotBuffer, callback);
+ } else {
+ sendScreenshotFailure(
+ AccessibilityService.ERROR_TAKE_SCREENSHOT_INVALID_DISPLAY,
+ callback);
+ }
+ }
+ );
+ mWindowManagerService.captureDisplay(displayId, null, screenCaptureListener);
+ } catch (Exception e) {
+ sendScreenshotFailure(AccessibilityService.ERROR_TAKE_SCREENSHOT_INVALID_DISPLAY,
+ callback);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -1461,22 +1464,24 @@
private void sendScreenshotSuccess(ScreenshotHardwareBuffer screenshotBuffer,
RemoteCallback callback) {
- final HardwareBuffer hardwareBuffer = screenshotBuffer.getHardwareBuffer();
- final ParcelableColorSpace colorSpace =
- new ParcelableColorSpace(screenshotBuffer.getColorSpace());
+ mMainHandler.post(PooledLambda.obtainRunnable((nonArg) -> {
+ final HardwareBuffer hardwareBuffer = screenshotBuffer.getHardwareBuffer();
+ final ParcelableColorSpace colorSpace =
+ new ParcelableColorSpace(screenshotBuffer.getColorSpace());
- final Bundle payload = new Bundle();
- payload.putInt(KEY_ACCESSIBILITY_SCREENSHOT_STATUS,
- AccessibilityService.TAKE_SCREENSHOT_SUCCESS);
- payload.putParcelable(KEY_ACCESSIBILITY_SCREENSHOT_HARDWAREBUFFER,
- hardwareBuffer);
- payload.putParcelable(KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE, colorSpace);
- payload.putLong(KEY_ACCESSIBILITY_SCREENSHOT_TIMESTAMP,
- SystemClock.uptimeMillis());
+ final Bundle payload = new Bundle();
+ payload.putInt(KEY_ACCESSIBILITY_SCREENSHOT_STATUS,
+ AccessibilityService.TAKE_SCREENSHOT_SUCCESS);
+ payload.putParcelable(KEY_ACCESSIBILITY_SCREENSHOT_HARDWAREBUFFER,
+ hardwareBuffer);
+ payload.putParcelable(KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE, colorSpace);
+ payload.putLong(KEY_ACCESSIBILITY_SCREENSHOT_TIMESTAMP,
+ SystemClock.uptimeMillis());
- // Send back the result.
- callback.sendResult(payload);
- hardwareBuffer.close();
+ // Send back the result.
+ callback.sendResult(payload);
+ hardwareBuffer.close();
+ }, null).recycleOnUse());
}
private void sendScreenshotFailure(@AccessibilityService.ScreenshotErrorCode int errorCode,
diff --git a/services/autofill/features.aconfig b/services/autofill/features.aconfig
index 1e44de6..92e00ee 100644
--- a/services/autofill/features.aconfig
+++ b/services/autofill/features.aconfig
@@ -1 +1,8 @@
-package: "android.service.autofill"
\ No newline at end of file
+package: "android.service.autofill"
+
+flag {
+ name: "autofill_credman_integration"
+ namespace: "autofill"
+ description: "Guards Autofill Framework against Autofill-Credman integration"
+ bug: "296907283"
+}
\ No newline at end of file
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 838aae8..cd87908 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -112,7 +112,14 @@
*/
public static final int INTEGRITY_VERIFICATION_REJECT = 0;
- /** Observer called whenever the list of packages changes */
+ /**
+ * Observer called whenever the list of packages changes.
+ *
+ * @deprecated please use {@link com.android.internal.content.PackageMonitor} instead.
+ * PackageMonitor covers more installation and uninstallation corner cases than
+ * PackageListObserver.
+ */
+ @Deprecated
public interface PackageListObserver {
/** A package was added to the system. */
default void onPackageAdded(@NonNull String packageName, int uid) {}
@@ -723,7 +730,12 @@
* notified if a package is updated.
* <p>The package list will not be updated automatically as packages are
* installed / uninstalled. Any changes must be handled within the observer.
+ *
+ * @deprecated please use {@link com.android.internal.content.PackageMonitor} instead.
+ * PackageMonitor covers more installation and uninstallation corner cases than
+ * PackageListObserver.
*/
+ @Deprecated
public abstract @NonNull PackageList getPackageList(@Nullable PackageListObserver observer);
/**
@@ -733,7 +745,12 @@
* <p>Does nothing if the observer isn't currently registered.
* <p>Observers are notified asynchronously and it's possible for an observer to be
* invoked after its been removed.
+ *
+ * @deprecated please use {@link com.android.internal.content.PackageMonitor} instead.
+ * PackageMonitor covers more installation and uninstallation corner cases than
+ * PackageListObserver.
*/
+ @Deprecated
public abstract void removePackageListObserver(@NonNull PackageListObserver observer);
/**
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 93fe0c9..553b085 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -97,6 +97,11 @@
import static android.os.Process.ZYGOTE_POLICY_FLAG_EMPTY;
import static com.android.internal.messages.nano.SystemMessageProto.SystemMessage.NOTE_FOREGROUND_SERVICE_BG_LAUNCH;
+import static com.android.internal.util.FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_DELEGATE;
+import static com.android.internal.util.FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_NA;
+import static com.android.internal.util.FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_NONE;
+import static com.android.internal.util.FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_START_FOREGROUND_SERVICE;
+import static com.android.internal.util.FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_START_SERVICE;
import static com.android.internal.util.FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__DENIED;
import static com.android.internal.util.FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER;
import static com.android.internal.util.FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT;
@@ -122,6 +127,7 @@
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
import android.Manifest;
+import android.Manifest.permission;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -901,7 +907,10 @@
showFgsBgRestrictedNotificationLocked(r);
logFGSStateChangeLocked(r,
FOREGROUND_SERVICE_STATE_CHANGED__STATE__DENIED,
- 0, FGS_STOP_REASON_UNKNOWN, FGS_TYPE_POLICY_CHECK_UNKNOWN);
+ 0, FGS_STOP_REASON_UNKNOWN, FGS_TYPE_POLICY_CHECK_UNKNOWN,
+ FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_NA,
+ false /* fgsRestrictionRecalculated */
+ );
if (CompatChanges.isChangeEnabled(FGS_START_EXCEPTION_CHANGE_ID, callingUid)) {
throw new ForegroundServiceStartNotAllowedException(msg);
}
@@ -2066,6 +2075,7 @@
boolean alreadyStartedOp = false;
boolean stopProcStatsOp = false;
+ final boolean origFgRequired = r.fgRequired;
if (r.fgRequired) {
if (DEBUG_SERVICE || DEBUG_BACKGROUND_CHECK) {
Slog.i(TAG, "Service called startForeground() as required: " + r);
@@ -2117,6 +2127,9 @@
// Whether to extend the SHORT_SERVICE time out.
boolean extendShortServiceTimeout = false;
+ // Whether setFgsRestrictionLocked() is called in here. Only used for logging.
+ boolean fgsRestrictionRecalculated = false;
+
int fgsTypeCheckCode = FGS_TYPE_POLICY_CHECK_UNKNOWN;
if (!ignoreForeground) {
if (foregroundServiceType == FOREGROUND_SERVICE_TYPE_SHORT_SERVICE
@@ -2182,6 +2195,7 @@
r.appInfo.uid, r.intent.getIntent(), r, r.userId,
BackgroundStartPrivileges.NONE,
false /* isBindService */);
+ fgsRestrictionRecalculated = true;
if (!r.isFgsAllowedStart()) {
Slog.w(TAG_SERVICE, "FGS type change to/from SHORT_SERVICE: "
+ " BFSL DENIED.");
@@ -2246,6 +2260,7 @@
r.appInfo.uid, r.intent.getIntent(), r, r.userId,
BackgroundStartPrivileges.NONE,
false /* isBindService */);
+ fgsRestrictionRecalculated = true;
final String temp = "startForegroundDelayMs:" + delayMs;
if (r.mInfoAllowStartForeground != null) {
r.mInfoAllowStartForeground += "; " + temp;
@@ -2266,6 +2281,25 @@
r.appInfo.uid, r.intent.getIntent(), r, r.userId,
BackgroundStartPrivileges.NONE,
false /* isBindService */);
+ fgsRestrictionRecalculated = true;
+ }
+
+ // When startForeground() is called on a bound service, without having
+ // it started (i.e. no Context.startService() or startForegroundService() was
+ // called.)
+ // called on it, then we probably didn't call setFgsRestrictionLocked()
+ // in startService(). If fgsRestrictionRecalculated is false, then we
+ // didn't call setFgsRestrictionLocked() here either.
+ //
+ // In this situation, we call setFgsRestrictionLocked() with
+ // forBoundFgs = false, so we'd set the FGS allowed reason to the
+ // by-bindings fields, so we can put it in the log, without affecting the
+ // logic.
+ if (!fgsRestrictionRecalculated && !r.startRequested) {
+ setFgsRestrictionLocked(r.serviceInfo.packageName, r.app.getPid(),
+ r.appInfo.uid, r.intent.getIntent(), r, r.userId,
+ BackgroundStartPrivileges.NONE,
+ false /* isBindService */, true /* forBoundFgs */);
}
// If the foreground service is not started from TOP process, do not allow it to
@@ -2291,7 +2325,10 @@
ignoreForeground = true;
logFGSStateChangeLocked(r,
FOREGROUND_SERVICE_STATE_CHANGED__STATE__DENIED,
- 0, FGS_STOP_REASON_UNKNOWN, FGS_TYPE_POLICY_CHECK_UNKNOWN);
+ 0, FGS_STOP_REASON_UNKNOWN, FGS_TYPE_POLICY_CHECK_UNKNOWN,
+ FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_NA,
+ false /* fgsRestrictionRecalculated */
+ );
if (CompatChanges.isChangeEnabled(FGS_START_EXCEPTION_CHANGE_ID,
r.appInfo.uid)) {
throw new ForegroundServiceStartNotAllowedException(msg);
@@ -2331,7 +2368,10 @@
if (fgsTypeResult.second != null) {
logFGSStateChangeLocked(r,
FOREGROUND_SERVICE_STATE_CHANGED__STATE__DENIED,
- 0, FGS_STOP_REASON_UNKNOWN, fgsTypeResult.first);
+ 0, FGS_STOP_REASON_UNKNOWN, fgsTypeResult.first,
+ FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_NA,
+ false /* fgsRestrictionRecalculated */
+ );
throw fgsTypeResult.second;
}
}
@@ -2403,9 +2443,24 @@
AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE);
registerAppOpCallbackLocked(r);
mAm.updateForegroundServiceUsageStats(r.name, r.userId, true);
+
+ int fgsStartApi = FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_NONE;
+ if (r.startRequested) {
+ if (origFgRequired) {
+ fgsStartApi =
+ FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_START_FOREGROUND_SERVICE;
+ } else {
+ fgsStartApi =
+ FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_START_SERVICE;
+ }
+ }
+
logFGSStateChangeLocked(r,
FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER,
- 0, FGS_STOP_REASON_UNKNOWN, fgsTypeCheckCode);
+ 0, FGS_STOP_REASON_UNKNOWN, fgsTypeCheckCode,
+ fgsStartApi,
+ fgsRestrictionRecalculated
+ );
synchronized (mFGSLogger) {
mFGSLogger.logForegroundServiceStart(r.appInfo.uid, 0, r);
}
@@ -2499,7 +2554,10 @@
r.mFgsExitTime > r.mFgsEnterTime
? (int) (r.mFgsExitTime - r.mFgsEnterTime) : 0,
FGS_STOP_REASON_STOP_FOREGROUND,
- FGS_TYPE_POLICY_CHECK_UNKNOWN);
+ FGS_TYPE_POLICY_CHECK_UNKNOWN,
+ FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_NA,
+ false /* fgsRestrictionRecalculated */
+ );
synchronized (mFGSLogger) {
mFGSLogger.logForegroundServiceStop(r.appInfo.uid, r);
@@ -3338,7 +3396,10 @@
FOREGROUND_SERVICE_STATE_CHANGED__STATE__TIMED_OUT,
nowUptime > sr.mFgsEnterTime ? (int) (nowUptime - sr.mFgsEnterTime) : 0,
FGS_STOP_REASON_UNKNOWN,
- FGS_TYPE_POLICY_CHECK_UNKNOWN);
+ FGS_TYPE_POLICY_CHECK_UNKNOWN,
+ FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_NA,
+ false /* fgsRestrictionRecalculated */
+ );
try {
sr.app.getThread().scheduleTimeoutService(sr, sr.getShortFgsInfo().getStartId());
} catch (RemoteException e) {
@@ -5705,7 +5766,10 @@
r.mFgsExitTime > r.mFgsEnterTime
? (int) (r.mFgsExitTime - r.mFgsEnterTime) : 0,
FGS_STOP_REASON_STOP_SERVICE,
- FGS_TYPE_POLICY_CHECK_UNKNOWN);
+ FGS_TYPE_POLICY_CHECK_UNKNOWN,
+ FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_NA,
+ false /* fgsRestrictionRecalculated */
+ );
synchronized (mFGSLogger) {
mFGSLogger.logForegroundServiceStop(r.appInfo.uid, r);
}
@@ -7452,6 +7516,13 @@
}
}
+ private void setFgsRestrictionLocked(String callingPackage,
+ int callingPid, int callingUid, Intent intent, ServiceRecord r, int userId,
+ BackgroundStartPrivileges backgroundStartPrivileges, boolean isBindService) {
+ setFgsRestrictionLocked(callingPackage, callingPid, callingUid, intent, r, userId,
+ backgroundStartPrivileges, isBindService, /*forBoundFgs*/ false);
+ }
+
/**
* There are two FGS restrictions:
* In R, mAllowWhileInUsePermissionInFgs is to allow while-in-use permissions in foreground
@@ -7463,11 +7534,14 @@
* @param intent intent to start/bind service.
* @param r the service to start.
* @param isBindService True if it's called from bindService().
+ * @param forBoundFgs set to true if it's called from Service.startForeground() for a
+ * service that's not started but bound.
* @return true if allow, false otherwise.
*/
private void setFgsRestrictionLocked(String callingPackage,
int callingPid, int callingUid, Intent intent, ServiceRecord r, int userId,
- BackgroundStartPrivileges backgroundStartPrivileges, boolean isBindService) {
+ BackgroundStartPrivileges backgroundStartPrivileges, boolean isBindService,
+ boolean forBoundFgs) {
@ReasonCode int allowWIU;
@ReasonCode int allowStart;
@@ -7511,9 +7585,19 @@
r.mAllowWIUInBindService = allowWIU;
r.mAllowStartInBindService = allowStart;
} else {
- r.mAllowWhileInUsePermissionInFgsReasonNoBinding = allowWIU;
- r.mAllowStartForegroundNoBinding = allowStart;
-
+ if (!forBoundFgs) {
+ // This is for "normal" situation.
+ r.mAllowWhileInUsePermissionInFgsReasonNoBinding = allowWIU;
+ r.mAllowStartForegroundNoBinding = allowStart;
+ } else {
+ // This logic is only for logging, so we only update the "by-binding" fields.
+ if (r.mAllowWIUByBindings == REASON_DENIED) {
+ r.mAllowWIUByBindings = allowWIU;
+ }
+ if (r.mAllowStartByBindings == REASON_DENIED) {
+ r.mAllowStartByBindings = allowStart;
+ }
+ }
// Also do a binding client check, unless called from bindService().
if (r.mAllowWIUByBindings == REASON_DENIED) {
r.mAllowWIUByBindings =
@@ -8137,7 +8221,10 @@
*/
private void logFGSStateChangeLocked(ServiceRecord r, int state, int durationMs,
@FgsStopReason int fgsStopReason,
- @ForegroundServicePolicyCheckCode int fgsTypeCheckCode) {
+ @ForegroundServicePolicyCheckCode int fgsTypeCheckCode,
+ int fgsStartApi, // from ForegroundServiceStateChanged.FgsStartApi
+ boolean fgsRestrictionRecalculated
+ ) {
if (!ActivityManagerUtils.shouldSamplePackageForAtom(
r.packageName, mAm.mConstants.mFgsAtomSampleRate)) {
return;
@@ -8194,7 +8281,9 @@
r.mAllowWIUByBindings,
r.mAllowStartForegroundNoBinding,
r.mAllowStartInBindService,
- r.mAllowStartByBindings);
+ r.mAllowStartByBindings,
+ fgsStartApi,
+ fgsRestrictionRecalculated);
int event = 0;
if (state == FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER) {
@@ -8373,7 +8462,10 @@
}
logFGSStateChangeLocked(r,
FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER,
- 0, FGS_STOP_REASON_UNKNOWN, FGS_TYPE_POLICY_CHECK_UNKNOWN);
+ 0, FGS_STOP_REASON_UNKNOWN, FGS_TYPE_POLICY_CHECK_UNKNOWN,
+ FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_DELEGATE,
+ false /* fgsRestrictionRecalculated */
+ );
// Notify the caller.
if (connection != null) {
mAm.mHandler.post(() -> {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 4c70db8..cbc9ff1 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -315,6 +315,7 @@
import android.net.Proxy;
import android.net.Uri;
import android.os.AppZygote;
+import android.os.BatteryManager;
import android.os.BatteryStats;
import android.os.Binder;
import android.os.BinderProxy;
@@ -4070,21 +4071,6 @@
profile.addPss(mi.getTotalPss(),
mi.getTotalUss(), mi.getTotalRss(), false,
ProcessStats.ADD_PSS_EXTERNAL_SLOW, duration);
- proc.getPkgList().forEachPackageProcessStats(holder -> {
- final ProcessState state = holder.state;
- FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_MEMORY_STAT_REPORTED,
- proc.info.uid,
- state != null ? state.getName() : proc.processName,
- state != null ? state.getPackage() : proc.info.packageName,
- mi.getTotalPss(),
- mi.getTotalUss(),
- mi.getTotalRss(),
- ProcessStats.ADD_PSS_EXTERNAL_SLOW,
- duration,
- holder.appVersion,
- profile.getCurrentHostingComponentTypes(),
- profile.getHistoricalHostingComponentTypes());
- });
}
}
}
@@ -4131,20 +4117,6 @@
// Record this for posterity if the process has been stable.
profile.addPss(pi, tmpUss[0], tmpUss[2], false,
ProcessStats.ADD_PSS_EXTERNAL, duration);
- proc.getPkgList().forEachPackageProcessStats(holder -> {
- FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_MEMORY_STAT_REPORTED,
- proc.info.uid,
- holder.state.getName(),
- holder.state.getPackage(),
- pi,
- tmpUss[0],
- tmpUss[2],
- ProcessStats.ADD_PSS_EXTERNAL,
- duration,
- holder.appVersion,
- profile.getCurrentHostingComponentTypes(),
- profile.getHistoricalHostingComponentTypes());
- });
}
}
}
@@ -12088,17 +12060,6 @@
// Record this for posterity if the process has been stable.
r.mProfile.addPss(myTotalPss, myTotalUss, myTotalRss, true,
reportType, endTime - startTime);
- r.getPkgList().forEachPackageProcessStats(holder -> {
- FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_MEMORY_STAT_REPORTED,
- r.info.uid,
- holder.state.getName(),
- holder.state.getPackage(),
- myTotalPss, myTotalUss, myTotalRss, reportType,
- endTime-startTime,
- holder.appVersion,
- r.mProfile.getCurrentHostingComponentTypes(),
- r.mProfile.getHistoricalHostingComponentTypes());
- });
}
}
@@ -12734,16 +12695,6 @@
// Record this for posterity if the process has been stable.
r.mProfile.addPss(myTotalPss, myTotalUss, myTotalRss, true,
reportType, endTime - startTime);
- r.getPkgList().forEachPackageProcessStats(holder -> {
- FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_MEMORY_STAT_REPORTED,
- r.info.uid,
- holder.state.getName(),
- holder.state.getPackage(),
- myTotalPss, myTotalUss, myTotalRss, reportType, endTime-startTime,
- holder.appVersion,
- r.mProfile.getCurrentHostingComponentTypes(),
- r.mProfile.getHistoricalHostingComponentTypes());
- });
}
}
@@ -15145,6 +15096,16 @@
}
}
+ // STOPSHIP(b/298884211): Remove this logging
+ if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) {
+ final int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
+ if (level < 0) {
+ Slog.wtf(BroadcastQueue.TAG, "Unexpected broadcast: " + intent
+ + "; callingUid: " + callingUid + ", callingPid: " + callingPid,
+ new Throwable());
+ }
+ }
+
int[] users;
if (userId == UserHandle.USER_ALL) {
// Caller wants broadcast to go to all started users.
@@ -16882,7 +16843,7 @@
for (int i = 0; i < N; i++) {
PendingTempAllowlist ptw = list[i];
mLocalDeviceIdleController.addPowerSaveTempWhitelistAppDirect(ptw.targetUid,
- ptw.duration, ptw.type, true, ptw.reasonCode, ptw.tag,
+ ptw.duration, ptw.type, false, ptw.reasonCode, ptw.tag,
ptw.callingUid);
}
}
diff --git a/services/core/java/com/android/server/am/AnrTimer.java b/services/core/java/com/android/server/am/AnrTimer.java
index cd6f009..378a386 100644
--- a/services/core/java/com/android/server/am/AnrTimer.java
+++ b/services/core/java/com/android/server/am/AnrTimer.java
@@ -38,11 +38,13 @@
import com.android.internal.annotations.Keep;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.ProcessCpuTracker;
+import com.android.internal.util.RingBuffer;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Date;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
@@ -75,7 +77,7 @@
*
* @hide
*/
-abstract class AnrTimer<V> {
+class AnrTimer<V> {
/**
* The log tag.
@@ -139,6 +141,8 @@
final String tag;
/** A partial stack that localizes the caller of the operation. */
final StackTraceElement[] stack;
+ /** The date, in local time, the error was created. */
+ final String date;
Error(@NonNull String issue, @NonNull String operation, @NonNull String tag,
@NonNull StackTraceElement[] stack, @NonNull String arg) {
@@ -147,21 +151,17 @@
this.tag = tag;
this.stack = stack;
this.arg = arg;
+ this.date = new Date().toString();
}
}
/**
* A list of errors detected during processing. Errors correspond to "timer not found"
* conditions. The stack trace identifies the source of the call. The list is
- * first-in/first-out, and the size is limited to MAX_SAVED_ERROR_COUNT.
+ * first-in/first-out, and the size is limited to 20.
*/
@GuardedBy("sErrors")
- private static final ArrayList<Error> sErrors = new ArrayList<>();
-
- /**
- * The maximum number of errors that are saved in the sErrors list.
- */
- private static final int MAX_SAVED_ERROR_COUNT = 20;
+ private static final RingBuffer<Error> sErrors = new RingBuffer<>(Error.class, 20);
/**
* A record of a single anr timer. The pid and uid are retained for reference but they do not
@@ -420,7 +420,7 @@
if (extension > 0) {
post(t, extension);
} else {
- onExpiredLocked(t, now());
+ onExpiredLocked(t);
}
}
return true;
@@ -706,7 +706,7 @@
* The notifier that a timer has fired. The timer is not modified.
*/
@GuardedBy("mLock")
- private void onExpiredLocked(@NonNull Timer timer, long when) {
+ private void onExpiredLocked(@NonNull Timer timer) {
if (DEBUG) report(timer, "expire");
traceBegin(timer, "expired");
mHandler.sendMessage(Message.obtain(mHandler, mWhat, timer.arg));
@@ -757,12 +757,7 @@
// This should be enough to isolate the location of the call.
StackTraceElement[] location = Arrays.copyOfRange(s, 6, 9);
synchronized (sErrors) {
- // Ensure the error list does not grow beyond the limit.
- while (sErrors.size() >= MAX_SAVED_ERROR_COUNT) {
- sErrors.remove(0);
- }
- // Add the new error to the list.
- sErrors.add(new Error(errorMsg, operation, mLabel, location, what));
+ sErrors.append(new Error(errorMsg, operation, mLabel, location, what));
}
if (DEBUG) Log.w(TAG, operation + " " + errorMsg + " " + mLabel + " timer " + what);
mTotalErrors++;
@@ -790,6 +785,7 @@
private static void dump(IndentingPrintWriter ipw, int seq, Error err) {
ipw.format("%2d: op:%s tag:%s issue:%s arg:%s\n", seq, err.operation, err.tag,
err.issue, err.arg);
+ ipw.format(" date:%s\n", err.date);
ipw.increaseIndent();
for (int i = 0; i < err.stack.length; i++) {
ipw.println(" " + err.stack[i].toString());
@@ -801,15 +797,15 @@
* Dump all errors to the output stream.
*/
private static void dumpErrors(IndentingPrintWriter ipw) {
- ArrayList<Error> errors;
+ Error errors[];
synchronized (sErrors) {
if (sErrors.size() == 0) return;
- errors = (ArrayList<Error>) sErrors.clone();
+ errors = sErrors.toArray();
}
ipw.println("Errors");
ipw.increaseIndent();
- for (int i = 0; i < errors.size(); i++) {
- dump(ipw, i, errors.get(i));
+ for (int i = 0; i < errors.length; i++) {
+ if (errors[i] != null) dump(ipw, i, errors[i]);
}
ipw.decreaseIndent();
}
diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java
index 46e5523..928b5d8 100644
--- a/services/core/java/com/android/server/am/AppProfiler.java
+++ b/services/core/java/com/android/server/am/AppProfiler.java
@@ -770,17 +770,6 @@
swapPss * 1024, rss * 1024, statType, procState, pssDuration);
profile.setLastPssTime(now);
profile.addPss(pss, uss, rss, true, statType, pssDuration);
- proc.getPkgList().forEachPackageProcessStats(holder -> {
- FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_MEMORY_STAT_REPORTED,
- proc.info.uid,
- holder.state.getName(),
- holder.state.getPackage(),
- pss, uss, rss,
- statType, pssDuration,
- holder.appVersion,
- profile.getCurrentHostingComponentTypes(),
- profile.getHistoricalHostingComponentTypes());
- });
if (DEBUG_PSS) {
Slog.d(TAG_PSS,
"pss of " + proc.toShortString() + ": " + pss
diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
index c35a3b2..5d31d15 100644
--- a/services/core/java/com/android/server/am/BroadcastProcessQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
@@ -1347,8 +1347,8 @@
* Set the timeout flag to indicate that an ANR timer has been started. A value of true means a
* timer is running; a value of false means there is no timer running.
*/
- void setTimeoutScheduled(boolean timeoutStarted) {
- mTimeoutScheduled = timeoutStarted;
+ void setTimeoutScheduled(boolean timeoutScheduled) {
+ mTimeoutScheduled = timeoutScheduled;
}
/**
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index 5b71595..a3dac6d 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -59,6 +59,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.os.BatteryManager;
import android.os.Bundle;
import android.os.BundleMerger;
import android.os.Handler;
@@ -1077,6 +1078,16 @@
queue.lastProcessState = app.mState.getCurProcState();
if (receiver instanceof BroadcastFilter) {
notifyScheduleRegisteredReceiver(app, r, (BroadcastFilter) receiver);
+ // STOPSHIP(b/298884211): Remove this logging
+ if (Intent.ACTION_BATTERY_CHANGED.equals(receiverIntent.getAction())) {
+ int level = receiverIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
+ if (level < 0) {
+ Slog.wtf(TAG, "Dispatching unexpected broadcast: " + receiverIntent
+ + " to " + receiver
+ + "; callingUid: " + r.callingUid
+ + ", callingPid: " + r.callingPid);
+ }
+ }
thread.scheduleRegisteredReceiver(
((BroadcastFilter) receiver).receiverList.receiver,
receiverIntent, r.resultCode, r.resultData, r.resultExtras,
@@ -1172,7 +1183,7 @@
}
private class BroadcastAnrTimer extends AnrTimer<BroadcastProcessQueue> {
- BroadcastAnrTimer(Handler handler) {
+ BroadcastAnrTimer(@NonNull Handler handler) {
super(Objects.requireNonNull(handler),
MSG_DELIVERY_TIMEOUT, "BROADCAST_TIMEOUT", true);
}
diff --git a/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java b/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java
index f6859d1..e0a71d4 100644
--- a/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java
+++ b/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java
@@ -27,6 +27,8 @@
import static android.app.ActivityManager.FOREGROUND_SERVICE_API_TYPE_USB;
import static android.os.Process.INVALID_UID;
+import static com.android.internal.util.FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_NA;
+
import android.annotation.IntDef;
import android.app.ActivityManager;
import android.app.ActivityManager.ForegroundServiceApiType;
@@ -520,7 +522,10 @@
r.mAllowWIUByBindings,
r.mAllowStartForegroundNoBinding,
r.mAllowStartInBindService,
- r.mAllowStartByBindings);
+ r.mAllowStartByBindings,
+ FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_NA,
+ false
+ );
}
/**
@@ -578,7 +583,10 @@
0,
0,
0,
- 0);
+ 0,
+ FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_NA,
+ false
+ );
}
/**
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 60af280..e08fdd6 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -1437,6 +1437,7 @@
}
});
new MediaMetrics.Item(mMetricsId + "disconnectA2dp")
+ .set(MediaMetrics.Property.EVENT, "disconnectA2dp")
.record();
if (toRemove.size() > 0) {
final int delay = checkSendBecomingNoisyIntentInt(
@@ -1459,6 +1460,7 @@
}
});
new MediaMetrics.Item(mMetricsId + "disconnectA2dpSink")
+ .set(MediaMetrics.Property.EVENT, "disconnectA2dpSink")
.record();
toRemove.stream().forEach(deviceAddress -> makeA2dpSrcUnavailable(deviceAddress));
}
@@ -1474,6 +1476,7 @@
}
});
new MediaMetrics.Item(mMetricsId + "disconnectHearingAid")
+ .set(MediaMetrics.Property.EVENT, "disconnectHearingAid")
.record();
if (toRemove.size() > 0) {
final int delay = checkSendBecomingNoisyIntentInt(
@@ -1531,6 +1534,7 @@
}
});
new MediaMetrics.Item(mMetricsId + "disconnectLeAudio")
+ .set(MediaMetrics.Property.EVENT, "disconnectLeAudio")
.record();
if (toRemove.size() > 0) {
final int delay = checkSendBecomingNoisyIntentInt(device,
diff --git a/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java b/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java
index 6edbfb7..4df2581 100644
--- a/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java
+++ b/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java
@@ -57,6 +57,7 @@
@NonNull private final FaceManager mFaceManager;
@NonNull private final FingerprintManager mFingerprintManager;
+ private final boolean mEnabled;
private final float mThreshold;
private final int mModality;
@@ -80,6 +81,7 @@
public AuthenticationStatsCollector(@NonNull Context context, int modality,
@NonNull BiometricNotification biometricNotification) {
mContext = context;
+ mEnabled = context.getResources().getBoolean(R.bool.config_biometricFrrNotificationEnabled);
mThreshold = context.getResources()
.getFraction(R.fraction.config_biometricNotificationFrrThreshold, 1, 1);
mUserAuthenticationStatsMap = new HashMap<>();
@@ -110,6 +112,11 @@
/** Update total authentication and rejected attempts. */
public void authenticate(int userId, boolean authenticated) {
+ // Don't collect data if the feature is disabled.
+ if (!mEnabled) {
+ return;
+ }
+
// Don't collect data for single-modality devices or user has both biometrics enrolled.
if (isSingleModalityDevice()
|| (hasEnrolledFace(userId) && hasEnrolledFingerprint(userId))) {
diff --git a/services/core/java/com/android/server/display/BrightnessRangeController.java b/services/core/java/com/android/server/display/BrightnessRangeController.java
index 64d2314..21273e0 100644
--- a/services/core/java/com/android/server/display/BrightnessRangeController.java
+++ b/services/core/java/com/android/server/display/BrightnessRangeController.java
@@ -19,10 +19,9 @@
import android.hardware.display.BrightnessInfo;
import android.os.Handler;
import android.os.IBinder;
-import android.provider.DeviceConfigInterface;
import com.android.server.display.brightness.clamper.HdrClamper;
-import com.android.server.display.feature.DeviceConfigParameterProvider;
+import com.android.server.display.feature.DisplayManagerFlags;
import java.io.PrintWriter;
import java.util.function.BooleanSupplier;
@@ -42,19 +41,19 @@
BrightnessRangeController(HighBrightnessModeController hbmController,
- Runnable modeChangeCallback, DisplayDeviceConfig displayDeviceConfig, Handler handler) {
+ Runnable modeChangeCallback, DisplayDeviceConfig displayDeviceConfig, Handler handler,
+ DisplayManagerFlags flags) {
this(hbmController, modeChangeCallback, displayDeviceConfig,
- new HdrClamper(modeChangeCallback::run, new Handler(handler.getLooper())),
- new DeviceConfigParameterProvider(DeviceConfigInterface.REAL));
+ new HdrClamper(modeChangeCallback::run, new Handler(handler.getLooper())), flags);
}
BrightnessRangeController(HighBrightnessModeController hbmController,
Runnable modeChangeCallback, DisplayDeviceConfig displayDeviceConfig,
- HdrClamper hdrClamper, DeviceConfigParameterProvider configParameterProvider) {
+ HdrClamper hdrClamper, DisplayManagerFlags flags) {
mHbmController = hbmController;
mModeChangeCallback = modeChangeCallback;
- mUseNbmController = configParameterProvider.isNormalBrightnessControllerFeatureEnabled();
mUseHdrClamper = false;
+ mUseNbmController = flags.isNbmControllerEnabled();
mNormalBrightnessModeController.resetNbmData(displayDeviceConfig.getLuxThrottlingData());
mHdrClamper = hdrClamper;
}
@@ -64,7 +63,6 @@
pw.println(" mUseNormalBrightnessController=" + mUseNbmController);
mHbmController.dump(pw);
mNormalBrightnessModeController.dump(pw);
-
}
void onAmbientLuxChange(float ambientLux) {
diff --git a/services/core/java/com/android/server/display/ColorFade.java b/services/core/java/com/android/server/display/ColorFade.java
index 2d763bc..cd867f6 100644
--- a/services/core/java/com/android/server/display/ColorFade.java
+++ b/services/core/java/com/android/server/display/ColorFade.java
@@ -43,6 +43,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.wm.WindowManagerInternal;
import libcore.io.Streams;
@@ -573,8 +574,21 @@
}
private ScreenCapture.ScreenshotHardwareBuffer captureScreen() {
- ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer =
- mDisplayManagerInternal.systemScreenshot(mDisplayId);
+ WindowManagerInternal windowManagerService = LocalServices.getService(
+ WindowManagerInternal.class);
+ ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer;
+ ScreenCapture.SynchronousScreenCaptureListener screenCaptureListener =
+ ScreenCapture.createSyncCaptureListener();
+ ScreenCapture.CaptureArgs captureArgs = new ScreenCapture.CaptureArgs.Builder<>()
+ .setCaptureSecureLayers(true)
+ .setAllowProtected(true)
+ .build();
+ try {
+ windowManagerService.captureDisplay(mDisplayId, captureArgs, screenCaptureListener);
+ screenshotBuffer = screenCaptureListener.getBuffer();
+ } catch (Exception e) {
+ screenshotBuffer = null;
+ }
if (screenshotBuffer == null) {
Slog.e(TAG, "Failed to take screenshot. Buffer is null");
return null;
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index e3dafa4..3a6e5f93 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -53,6 +53,7 @@
import com.android.server.display.config.DisplayConfiguration;
import com.android.server.display.config.DisplayQuirks;
import com.android.server.display.config.HbmTiming;
+import com.android.server.display.config.HdrBrightnessData;
import com.android.server.display.config.HighBrightnessMode;
import com.android.server.display.config.IntegerArray;
import com.android.server.display.config.LuxThrottling;
@@ -232,7 +233,22 @@
* </point>
* </sdrHdrRatioMap>
* </highBrightnessMode>
- *
+ * <hdrBrightnessConfig>
+ * <brightnessMap>
+ * <point>
+ * <first>500</first>
+ * <second>0.3</second>
+ * </point>
+ * <point>
+ * <first>1200</first>
+ * <second>0.6</second>
+ * </point>
+ * </brightnessMap>
+ * <brightnessIncreaseDebounceMillis>1000</brightnessIncreaseDebounceMillis>
+ * <brightnessIncreaseDurationMillis>10000</brightnessIncreaseDurationMillis>
+ * <brightnessDecreaseDebounceMillis>13000</brightnessDecreaseDebounceMillis>
+ * <brightnessDecreaseDurationMillis>10000</brightnessDecreaseDurationMillis>
+ * </hdrBrightnessConfig>
* <luxThrottling>
* <brightnessLimitMap>
* <type>default</type>
@@ -769,6 +785,9 @@
@Nullable
private HostUsiVersion mHostUsiVersion;
+ @Nullable
+ private HdrBrightnessData mHdrBrightnessData;
+
@VisibleForTesting
DisplayDeviceConfig(Context context) {
mContext = context;
@@ -1544,6 +1563,14 @@
}
/**
+ * @return HDR brightness related configuration
+ */
+ @Nullable
+ public HdrBrightnessData getHdrBrightnessData() {
+ return mHdrBrightnessData;
+ }
+
+ /**
* @return Refresh rate range for specific profile id or null
*/
@Nullable
@@ -1759,7 +1786,8 @@
+ "mScreenOffBrightnessSensorValueToLux=" + Arrays.toString(
mScreenOffBrightnessSensorValueToLux)
+ "\n"
- + "mUsiVersion= " + mHostUsiVersion
+ + "mUsiVersion= " + mHostUsiVersion + "\n"
+ + "mHdrBrightnessData" + mHdrBrightnessData
+ "}";
}
@@ -1823,6 +1851,7 @@
loadRefreshRateSetting(config);
loadScreenOffBrightnessSensorValueToLuxFromDdc(config);
loadUsiVersion(config);
+ mHdrBrightnessData = HdrBrightnessData.loadConfig(config);
} else {
Slog.w(TAG, "DisplayDeviceConfig file is null");
}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 90c7ce7..df45001 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -119,6 +119,7 @@
import android.os.UserManager;
import android.provider.DeviceConfigInterface;
import android.provider.Settings;
+import android.sysprop.DisplayProperties;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.EventLog;
@@ -136,7 +137,6 @@
import android.view.SurfaceControl;
import android.view.SurfaceControl.RefreshRateRange;
import android.window.DisplayWindowPolicyController;
-import android.window.ScreenCapture;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -489,6 +489,9 @@
private boolean mBootCompleted = false;
+ // If we would like to keep a particular eye on a package, we can set the package name.
+ private final boolean mExtraDisplayEventLogging;
+
private final BroadcastReceiver mIdleModeReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -572,6 +575,8 @@
mOverlayProperties = SurfaceControl.getOverlaySupport();
mSystemReady = false;
mConfigParameterProvider = new DeviceConfigParameterProvider(DeviceConfigInterface.REAL);
+ final String name = DisplayProperties.debug_vri_package().orElse(null);
+ mExtraDisplayEventLogging = !TextUtils.isEmpty(name);
}
public void setupSchedulerPolicies() {
@@ -2668,42 +2673,6 @@
return null;
}
- private ScreenCapture.ScreenshotHardwareBuffer systemScreenshotInternal(int displayId) {
- final ScreenCapture.DisplayCaptureArgs captureArgs;
- synchronized (mSyncRoot) {
- final IBinder token = getDisplayToken(displayId);
- if (token == null) {
- return null;
- }
- final LogicalDisplay logicalDisplay = mLogicalDisplayMapper.getDisplayLocked(displayId);
- if (logicalDisplay == null) {
- return null;
- }
-
- final DisplayInfo displayInfo = logicalDisplay.getDisplayInfoLocked();
- captureArgs = new ScreenCapture.DisplayCaptureArgs.Builder(token)
- .setSize(displayInfo.getNaturalWidth(), displayInfo.getNaturalHeight())
- .setCaptureSecureLayers(true)
- .setAllowProtected(true)
- .build();
- }
- return ScreenCapture.captureDisplay(captureArgs);
- }
-
- private ScreenCapture.ScreenshotHardwareBuffer userScreenshotInternal(int displayId) {
- synchronized (mSyncRoot) {
- final IBinder token = getDisplayToken(displayId);
- if (token == null) {
- return null;
- }
-
- final ScreenCapture.DisplayCaptureArgs captureArgs =
- new ScreenCapture.DisplayCaptureArgs.Builder(token)
- .build();
- return ScreenCapture.captureDisplay(captureArgs);
- }
- }
-
@VisibleForTesting
DisplayedContentSamplingAttributes getDisplayedContentSamplingAttributesInternal(
int displayId) {
@@ -2956,9 +2925,10 @@
// Delivers display event notifications to callbacks.
private void deliverDisplayEvent(int displayId, ArraySet<Integer> uids,
@DisplayEvent int event) {
- if (DEBUG) {
+ if (DEBUG || mExtraDisplayEventLogging) {
Slog.d(TAG, "Delivering display event: displayId="
- + displayId + ", event=" + event);
+ + displayId + ", event=" + event
+ + (uids != null ? ", uids=" + uids : ""));
}
// Grab the lock and copy the callbacks.
@@ -3267,12 +3237,12 @@
displayPowerController = new DisplayPowerController2(
mContext, /* injector= */ null, mDisplayPowerCallbacks, mPowerHandler,
mSensorManager, mDisplayBlanker, display, mBrightnessTracker, brightnessSetting,
- () -> handleBrightnessChange(display), hbmMetadata, mBootCompleted);
+ () -> handleBrightnessChange(display), hbmMetadata, mBootCompleted, mFlags);
} else {
displayPowerController = new DisplayPowerController(
mContext, /* injector= */ null, mDisplayPowerCallbacks, mPowerHandler,
mSensorManager, mDisplayBlanker, display, mBrightnessTracker, brightnessSetting,
- () -> handleBrightnessChange(display), hbmMetadata, mBootCompleted);
+ () -> handleBrightnessChange(display), hbmMetadata, mBootCompleted, mFlags);
}
mDisplayPowerControllers.append(display.getDisplayIdLocked(), displayPowerController);
return displayPowerController;
@@ -4471,16 +4441,6 @@
}
@Override
- public ScreenCapture.ScreenshotHardwareBuffer systemScreenshot(int displayId) {
- return systemScreenshotInternal(displayId);
- }
-
- @Override
- public ScreenCapture.ScreenshotHardwareBuffer userScreenshot(int displayId) {
- return userScreenshotInternal(displayId);
- }
-
- @Override
public DisplayInfo getDisplayInfo(int displayId) {
return getDisplayInfoInternal(displayId, Process.myUid());
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index b7b46ea..320684f 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -75,6 +75,7 @@
import com.android.server.display.brightness.BrightnessReason;
import com.android.server.display.color.ColorDisplayService.ColorDisplayServiceInternal;
import com.android.server.display.color.ColorDisplayService.ReduceBrightColorsListener;
+import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.display.layout.Layout;
import com.android.server.display.utils.SensorUtils;
import com.android.server.display.whitebalance.DisplayWhiteBalanceController;
@@ -592,7 +593,7 @@
SensorManager sensorManager, DisplayBlanker blanker, LogicalDisplay logicalDisplay,
BrightnessTracker brightnessTracker, BrightnessSetting brightnessSetting,
Runnable onBrightnessChangeRunnable, HighBrightnessModeMetadata hbmMetadata,
- boolean bootCompleted) {
+ boolean bootCompleted, DisplayManagerFlags flags) {
mInjector = injector != null ? injector : new Injector();
mClock = mInjector.getClock();
@@ -677,7 +678,7 @@
HighBrightnessModeController hbmController = createHbmControllerLocked(modeChangeCallback);
mBrightnessRangeController = new BrightnessRangeController(hbmController,
- modeChangeCallback, mDisplayDeviceConfig, mHandler);
+ modeChangeCallback, mDisplayDeviceConfig, mHandler, flags);
mBrightnessThrottler = createBrightnessThrottlerLocked();
@@ -1921,8 +1922,25 @@
if (isValidBrightnessValue(animateValue)
&& (animateValue != currentBrightness
|| sdrAnimateValue != currentSdrBrightness)) {
- if (initialRampSkip || hasBrightnessBuckets
- || !isDisplayContentVisible || brightnessIsTemporary) {
+ boolean skipAnimation = initialRampSkip || hasBrightnessBuckets
+ || !isDisplayContentVisible || brightnessIsTemporary;
+ if (!skipAnimation && BrightnessSynchronizer.floatEquals(
+ sdrAnimateValue, currentSdrBrightness)) {
+ // Going from HDR to no HDR; visually this should be a "no-op" anyway
+ // as the remaining SDR content's brightness should be holding steady
+ // due to the sdr brightness not shifting
+ if (BrightnessSynchronizer.floatEquals(sdrAnimateValue, animateValue)) {
+ skipAnimation = true;
+ }
+
+ // Going from no HDR to HDR; visually this is a significant scene change
+ // and the animation just prevents advanced clients from doing their own
+ // handling of enter/exit animations if they would like to do such a thing
+ if (BrightnessSynchronizer.floatEquals(sdrAnimateValue, currentBrightness)) {
+ skipAnimation = true;
+ }
+ }
+ if (skipAnimation) {
animateScreenBrightness(animateValue, sdrAnimateValue,
SCREEN_ANIMATION_RATE_MINIMUM);
} else {
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
index 7021eed..597738e 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -77,6 +77,7 @@
import com.android.server.display.brightness.strategy.AutomaticBrightnessStrategy;
import com.android.server.display.color.ColorDisplayService.ColorDisplayServiceInternal;
import com.android.server.display.color.ColorDisplayService.ReduceBrightColorsListener;
+import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.display.layout.Layout;
import com.android.server.display.state.DisplayStateController;
import com.android.server.display.utils.SensorUtils;
@@ -472,7 +473,7 @@
SensorManager sensorManager, DisplayBlanker blanker, LogicalDisplay logicalDisplay,
BrightnessTracker brightnessTracker, BrightnessSetting brightnessSetting,
Runnable onBrightnessChangeRunnable, HighBrightnessModeMetadata hbmMetadata,
- boolean bootCompleted) {
+ boolean bootCompleted, DisplayManagerFlags flags) {
mInjector = injector != null ? injector : new Injector();
mClock = mInjector.getClock();
@@ -540,7 +541,7 @@
mBrightnessThrottler = createBrightnessThrottlerLocked();
mBrightnessRangeController = mInjector.getBrightnessRangeController(hbmController,
- modeChangeCallback, mDisplayDeviceConfig, mHandler);
+ modeChangeCallback, mDisplayDeviceConfig, mHandler, flags);
mDisplayBrightnessController =
new DisplayBrightnessController(context, null,
@@ -1523,8 +1524,25 @@
if (BrightnessUtils.isValidBrightnessValue(animateValue)
&& (animateValue != currentBrightness
|| sdrAnimateValue != currentSdrBrightness)) {
- if (initialRampSkip || hasBrightnessBuckets
- || !isDisplayContentVisible || brightnessIsTemporary) {
+ boolean skipAnimation = initialRampSkip || hasBrightnessBuckets
+ || !isDisplayContentVisible || brightnessIsTemporary;
+ if (!skipAnimation && BrightnessSynchronizer.floatEquals(
+ sdrAnimateValue, currentSdrBrightness)) {
+ // Going from HDR to no HDR; visually this should be a "no-op" anyway
+ // as the remaining SDR content's brightness should be holding steady
+ // due to the sdr brightness not shifting
+ if (BrightnessSynchronizer.floatEquals(sdrAnimateValue, animateValue)) {
+ skipAnimation = true;
+ }
+
+ // Going from no HDR to HDR; visually this is a significant scene change
+ // and the animation just prevents advanced clients from doing their own
+ // handling of enter/exit animations if they would like to do such a thing
+ if (BrightnessSynchronizer.floatEquals(sdrAnimateValue, currentBrightness)) {
+ skipAnimation = true;
+ }
+ }
+ if (skipAnimation) {
animateScreenBrightness(animateValue, sdrAnimateValue,
SCREEN_ANIMATION_RATE_MINIMUM);
} else if (customTransitionRate > 0) {
@@ -2977,9 +2995,10 @@
BrightnessRangeController getBrightnessRangeController(
HighBrightnessModeController hbmController, Runnable modeChangeCallback,
- DisplayDeviceConfig displayDeviceConfig, Handler handler) {
+ DisplayDeviceConfig displayDeviceConfig, Handler handler,
+ DisplayManagerFlags flags) {
return new BrightnessRangeController(hbmController,
- modeChangeCallback, displayDeviceConfig, handler);
+ modeChangeCallback, displayDeviceConfig, handler, flags);
}
DisplayWhiteBalanceController getDisplayWhiteBalanceController(Handler handler,
diff --git a/services/core/java/com/android/server/display/OWNERS b/services/core/java/com/android/server/display/OWNERS
index fcaa957..9439eaa 100644
--- a/services/core/java/com/android/server/display/OWNERS
+++ b/services/core/java/com/android/server/display/OWNERS
@@ -6,5 +6,6 @@
flc@google.com
wilczynskip@google.com
brup@google.com
+petsjonkin@google.com
per-file ColorDisplayService.java=christyfranks@google.com
diff --git a/services/core/java/com/android/server/display/RampAnimator.java b/services/core/java/com/android/server/display/RampAnimator.java
index 52b92c4..378cdba 100644
--- a/services/core/java/com/android/server/display/RampAnimator.java
+++ b/services/core/java/com/android/server/display/RampAnimator.java
@@ -31,7 +31,12 @@
private final FloatProperty<T> mProperty;
private float mCurrentValue;
- private float mTargetValue;
+
+ // target in HLG space
+ private float mTargetHlgValue;
+
+ // target in linear space
+ private float mTargetLinearValue;
private float mRate;
private float mAnimationIncreaseMaxTimeSecs;
private float mAnimationDecreaseMaxTimeSecs;
@@ -78,7 +83,8 @@
if (mFirstTime || target != mCurrentValue) {
mFirstTime = false;
mRate = 0;
- mTargetValue = target;
+ mTargetHlgValue = target;
+ mTargetLinearValue = targetLinear;
mCurrentValue = target;
setPropertyValue(target);
mAnimating = false;
@@ -105,13 +111,14 @@
// Otherwise, continue at the previous rate.
if (!mAnimating
|| rate > mRate
- || (target <= mCurrentValue && mCurrentValue <= mTargetValue)
- || (mTargetValue <= mCurrentValue && mCurrentValue <= target)) {
+ || (target <= mCurrentValue && mCurrentValue <= mTargetHlgValue)
+ || (mTargetHlgValue <= mCurrentValue && mCurrentValue <= target)) {
mRate = rate;
}
- final boolean changed = (mTargetValue != target);
- mTargetValue = target;
+ final boolean changed = (mTargetHlgValue != target);
+ mTargetHlgValue = target;
+ mTargetLinearValue = targetLinear;
// Start animating.
if (!mAnimating && target != mCurrentValue) {
@@ -135,7 +142,11 @@
* into linear space.
*/
private void setPropertyValue(float val) {
- final float linearVal = BrightnessUtils.convertGammaToLinear(val);
+ // To avoid linearVal inconsistency when converting to HLG and back to linear space
+ // used original target linear value for final animation step
+ float linearVal =
+ val == mTargetHlgValue ? mTargetLinearValue : BrightnessUtils.convertGammaToLinear(
+ val);
mProperty.setValue(mObject, linearVal);
}
@@ -150,13 +161,13 @@
final float scale = ValueAnimator.getDurationScale();
if (scale == 0) {
// Animation off.
- mAnimatedValue = mTargetValue;
+ mAnimatedValue = mTargetHlgValue;
} else {
final float amount = timeDelta * mRate / scale;
- if (mTargetValue > mCurrentValue) {
- mAnimatedValue = Math.min(mAnimatedValue + amount, mTargetValue);
+ if (mTargetHlgValue > mCurrentValue) {
+ mAnimatedValue = Math.min(mAnimatedValue + amount, mTargetHlgValue);
} else {
- mAnimatedValue = Math.max(mAnimatedValue - amount, mTargetValue);
+ mAnimatedValue = Math.max(mAnimatedValue - amount, mTargetHlgValue);
}
}
final float oldCurrentValue = mCurrentValue;
@@ -164,7 +175,7 @@
if (oldCurrentValue != mCurrentValue) {
setPropertyValue(mCurrentValue);
}
- if (mTargetValue == mCurrentValue) {
+ if (mTargetHlgValue == mCurrentValue) {
mAnimating = false;
}
}
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index 6936112..d910e16 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -167,6 +167,8 @@
int width, int height, int densityDpi) {
VirtualDisplayDevice device = mVirtualDisplayDevices.get(appToken);
if (device != null) {
+ Slog.v(TAG, "Resize VirtualDisplay " + device.mName + " to " + width
+ + " " + height);
device.resizeLocked(width, height, densityDpi);
}
}
@@ -183,6 +185,7 @@
public void setVirtualDisplaySurfaceLocked(IBinder appToken, Surface surface) {
VirtualDisplayDevice device = mVirtualDisplayDevices.get(appToken);
if (device != null) {
+ Slog.v(TAG, "Update surface for VirtualDisplay " + device.mName);
device.setSurfaceLocked(surface);
}
}
@@ -197,6 +200,7 @@
public DisplayDevice releaseVirtualDisplayLocked(IBinder appToken) {
VirtualDisplayDevice device = mVirtualDisplayDevices.remove(appToken);
if (device != null) {
+ Slog.v(TAG, "Release VirtualDisplay " + device.mName);
device.destroyLocked(true);
appToken.unlinkToDeath(device, 0);
}
diff --git a/services/core/java/com/android/server/display/config/HdrBrightnessData.java b/services/core/java/com/android/server/display/config/HdrBrightnessData.java
new file mode 100644
index 0000000..06d3c5b
--- /dev/null
+++ b/services/core/java/com/android/server/display/config/HdrBrightnessData.java
@@ -0,0 +1,98 @@
+/*
+ * 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.display.config;
+
+import android.annotation.Nullable;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Brightness config for HDR content
+ */
+public class HdrBrightnessData {
+
+ /**
+ * Lux to brightness map
+ */
+ public final Map<Float, Float> mMaxBrightnessLimits;
+
+ /**
+ * Debounce time for brightness increase
+ */
+ public final long mBrightnessIncreaseDebounceMillis;
+
+ /**
+ * Brightness increase animation duration
+ */
+ public final long mBrightnessIncreaseDurationMillis;
+
+ /**
+ * Debounce time for brightness decrease
+ */
+ public final long mBrightnessDecreaseDebounceMillis;
+
+ /**
+ * Brightness decrease animation duration
+ */
+ public final long mBrightnessDecreaseDurationMillis;
+
+ private HdrBrightnessData(Map<Float, Float> maxBrightnessLimits,
+ long brightnessIncreaseDebounceMillis, long brightnessIncreaseDurationMillis,
+ long brightnessDecreaseDebounceMillis, long brightnessDecreaseDurationMillis) {
+ mMaxBrightnessLimits = maxBrightnessLimits;
+ mBrightnessIncreaseDebounceMillis = brightnessIncreaseDebounceMillis;
+ mBrightnessIncreaseDurationMillis = brightnessIncreaseDurationMillis;
+ mBrightnessDecreaseDebounceMillis = brightnessDecreaseDebounceMillis;
+ mBrightnessDecreaseDurationMillis = brightnessDecreaseDurationMillis;
+ }
+
+ @Override
+ public String toString() {
+ return "HdrBrightnessData {"
+ + "mMaxBrightnessLimits: " + mMaxBrightnessLimits
+ + ", mBrightnessIncreaseDebounceMillis: " + mBrightnessIncreaseDebounceMillis
+ + ", mBrightnessIncreaseDurationMillis: " + mBrightnessIncreaseDurationMillis
+ + ", mBrightnessDecreaseDebounceMillis: " + mBrightnessDecreaseDebounceMillis
+ + ", mBrightnessDecreaseDurationMillis: " + mBrightnessDecreaseDurationMillis
+ + "} ";
+ }
+
+ /**
+ * Loads HdrBrightnessData from DisplayConfiguration
+ */
+ @Nullable
+ public static HdrBrightnessData loadConfig(DisplayConfiguration config) {
+ HdrBrightnessConfig hdrConfig = config.getHdrBrightnessConfig();
+ if (hdrConfig == null) {
+ return null;
+ }
+
+ List<NonNegativeFloatToFloatPoint> points = hdrConfig.getBrightnessMap().getPoint();
+ Map<Float, Float> brightnessLimits = new HashMap<>();
+ for (NonNegativeFloatToFloatPoint point: points) {
+ brightnessLimits.put(point.getFirst().floatValue(), point.getSecond().floatValue());
+ }
+
+ return new HdrBrightnessData(brightnessLimits,
+ hdrConfig.getBrightnessIncreaseDebounceMillis().longValue(),
+ hdrConfig.getBrightnessIncreaseDurationMillis().longValue(),
+ hdrConfig.getBrightnessDecreaseDebounceMillis().longValue(),
+ hdrConfig.getBrightnessDecreaseDurationMillis().longValue());
+ }
+}
diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
index fddac6d..aebd8a0 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -30,43 +30,68 @@
public class DisplayManagerFlags {
private static final boolean DEBUG = false;
private static final String TAG = "DisplayManagerFlags";
- private boolean mIsConnectedDisplayManagementEnabled = false;
- private boolean mIsConnectedDisplayManagementEnabledSet = false;
- private boolean flagOrSystemProperty(Supplier<Boolean> flagFunction, String flagName) {
- // TODO(b/299462337) Remove when the infrastructure is ready.
- if ((Build.IS_ENG || Build.IS_USERDEBUG)
- && SystemProperties.getBoolean("persist.sys." + flagName, false)) {
- return true;
- }
- try {
- return flagFunction.get();
- } catch (Throwable ex) {
- if (DEBUG) {
- Slog.i(TAG, "Flags not ready yet. Return false for " + flagName, ex);
- }
- return false;
- }
- }
+ private final FlagState mConnectedDisplayManagementFlagState = new FlagState(
+ Flags.FLAG_ENABLE_CONNECTED_DISPLAY_MANAGEMENT,
+ Flags::enableConnectedDisplayManagement);
- // TODO(b/297159910): Simplify using READ-ONLY flags when available.
+ private final FlagState mNbmControllerFlagState = new FlagState(
+ Flags.FLAG_ENABLE_NBM_CONTROLLER,
+ Flags::enableNbmController);
+
/** Returns whether connected display management is enabled or not. */
public boolean isConnectedDisplayManagementEnabled() {
- if (mIsConnectedDisplayManagementEnabledSet) {
- if (DEBUG) {
- Slog.d(TAG, "isConnectedDisplayManagementEnabled. Recall = "
- + mIsConnectedDisplayManagementEnabled);
+ return mConnectedDisplayManagementFlagState.isEnabled();
+ }
+
+ /** Returns whether hdr clamper is enabled on not*/
+ public boolean isNbmControllerEnabled() {
+ return mNbmControllerFlagState.isEnabled();
+ }
+
+ private static class FlagState {
+
+ private final String mName;
+
+ private final Supplier<Boolean> mFlagFunction;
+ private boolean mEnabledSet;
+ private boolean mEnabled;
+
+ private FlagState(String name, Supplier<Boolean> flagFunction) {
+ mName = name;
+ mFlagFunction = flagFunction;
+ }
+
+ // TODO(b/297159910): Simplify using READ-ONLY flags when available.
+ private boolean isEnabled() {
+ if (mEnabledSet) {
+ if (DEBUG) {
+ Slog.d(TAG, mName + ": mEnabled. Recall = " + mEnabled);
+ }
+ return mEnabled;
}
- return mIsConnectedDisplayManagementEnabled;
+ mEnabled = flagOrSystemProperty(mFlagFunction, mName);
+ if (DEBUG) {
+ Slog.d(TAG, mName + ": mEnabled. Flag value = " + mEnabled);
+ }
+ mEnabledSet = true;
+ return mEnabled;
}
- mIsConnectedDisplayManagementEnabled =
- flagOrSystemProperty(Flags::enableConnectedDisplayManagement,
- Flags.FLAG_ENABLE_CONNECTED_DISPLAY_MANAGEMENT);
- if (DEBUG) {
- Slog.d(TAG, "isConnectedDisplayManagementEnabled. Flag value = "
- + mIsConnectedDisplayManagementEnabled);
+
+ private boolean flagOrSystemProperty(Supplier<Boolean> flagFunction, String flagName) {
+ // TODO(b/299462337) Remove when the infrastructure is ready.
+ if ((Build.IS_ENG || Build.IS_USERDEBUG)
+ && SystemProperties.getBoolean("persist.sys." + flagName, false)) {
+ return true;
+ }
+ try {
+ return flagFunction.get();
+ } catch (Throwable ex) {
+ if (DEBUG) {
+ Slog.i(TAG, "Flags not ready yet. Return false for " + flagName, ex);
+ }
+ return false;
+ }
}
- mIsConnectedDisplayManagementEnabledSet = true;
- return mIsConnectedDisplayManagementEnabled;
}
}
diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig
index 2c3c66e..12306b0 100644
--- a/services/core/java/com/android/server/display/feature/display_flags.aconfig
+++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig
@@ -9,3 +9,11 @@
bug: "280739508"
is_fixed_read_only: true
}
+
+flag {
+ name: "enable_nbm_controller"
+ namespace: "display_manager"
+ description: "Feature flag for Normal Brightness Mode Controller"
+ bug: "277877297"
+ is_fixed_read_only: true
+}
diff --git a/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java b/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java
index a2c8748..2ede56d 100644
--- a/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java
+++ b/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java
@@ -62,10 +62,10 @@
mWindowHandle.ownerUid = uid;
mWindowHandle.scaleFactor = 1.0f;
mWindowHandle.replaceTouchableRegionWithCrop(null /* use this surface's bounds */);
- mWindowHandle.inputConfig = InputConfig.NOT_FOCUSABLE | InputConfig.SPY;
+ mWindowHandle.inputConfig =
+ InputConfig.NOT_FOCUSABLE | InputConfig.SPY | InputConfig.TRUSTED_OVERLAY;
final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
- mWindowHandle.setTrustedOverlay(t, mInputSurface, true);
t.setInputWindowInfo(mInputSurface, mWindowHandle);
t.setLayer(mInputSurface, InputManagerService.INPUT_OVERLAY_LAYER_GESTURE_MONITOR);
t.setPosition(mInputSurface, 0, 0);
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 62660c4..6b399de 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -95,6 +95,7 @@
import android.view.InputDevice;
import android.view.InputEvent;
import android.view.InputMonitor;
+import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.PointerIcon;
import android.view.Surface;
@@ -682,6 +683,12 @@
return mNative.getKeyCodeForKeyLocation(deviceId, locationKeyCode);
}
+ @Override // Binder call
+ public KeyCharacterMap getKeyCharacterMap(@NonNull String layoutDescriptor) {
+ Objects.requireNonNull(layoutDescriptor, "layoutDescriptor must not be null");
+ return mKeyboardLayoutManager.getKeyCharacterMap(layoutDescriptor);
+ }
+
/**
* Transfer the current touch gesture to the provided window.
*
diff --git a/services/core/java/com/android/server/input/KeyboardLayoutManager.java b/services/core/java/com/android/server/input/KeyboardLayoutManager.java
index a5162c0..0eb620f 100644
--- a/services/core/java/com/android/server/input/KeyboardLayoutManager.java
+++ b/services/core/java/com/android/server/input/KeyboardLayoutManager.java
@@ -63,6 +63,7 @@
import android.util.Slog;
import android.util.SparseArray;
import android.view.InputDevice;
+import android.view.KeyCharacterMap;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.InputMethodSubtype;
@@ -430,6 +431,23 @@
return result[0];
}
+ @AnyThread
+ public KeyCharacterMap getKeyCharacterMap(@NonNull String layoutDescriptor) {
+ final String[] overlay = new String[1];
+ visitKeyboardLayout(layoutDescriptor,
+ (resources, keyboardLayoutResId, layout) -> {
+ try (InputStreamReader stream = new InputStreamReader(
+ resources.openRawResource(keyboardLayoutResId))) {
+ overlay[0] = Streams.readFully(stream);
+ } catch (IOException | Resources.NotFoundException ignored) {
+ }
+ });
+ if (TextUtils.isEmpty(overlay[0])) {
+ return KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
+ }
+ return KeyCharacterMap.load(layoutDescriptor, overlay[0]);
+ }
+
private void visitAllKeyboardLayouts(KeyboardLayoutVisitor visitor) {
final PackageManager pm = mContext.getPackageManager();
Intent intent = new Intent(InputManager.ACTION_QUERY_KEYBOARD_LAYOUTS);
diff --git a/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java b/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java
index dbbbed3..7726f40 100644
--- a/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java
+++ b/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java
@@ -57,13 +57,13 @@
InputConfig.NOT_FOCUSABLE
| InputConfig.NOT_TOUCHABLE
| InputConfig.SPY
- | InputConfig.INTERCEPTS_STYLUS;
+ | InputConfig.INTERCEPTS_STYLUS
+ | InputConfig.TRUSTED_OVERLAY;
// Configure the surface to receive stylus events across the entire display.
mWindowHandle.replaceTouchableRegionWithCrop(null /* use this surface's bounds */);
final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
- mWindowHandle.setTrustedOverlay(t, mInputSurface, true);
t.setInputWindowInfo(mInputSurface, mWindowHandle);
t.setLayer(mInputSurface, InputManagerService.INPUT_OVERLAY_LAYER_HANDWRITING_SURFACE);
t.setPosition(mInputSurface, 0, 0);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 699e9c8..032778c 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -116,7 +116,6 @@
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.proto.ProtoOutputStream;
-import android.view.IWindowManager;
import android.view.InputChannel;
import android.view.InputDevice;
import android.view.MotionEvent;
@@ -124,7 +123,6 @@
import android.view.WindowManager.DisplayImePolicy;
import android.view.WindowManager.LayoutParams;
import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
-import android.view.WindowManagerGlobal;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.ImeTracker;
import android.view.inputmethod.InputBinding;
@@ -2989,9 +2987,13 @@
ConcurrentUtils.waitForFutureNoInterrupt(mImeDrawsImeNavBarResLazyInitFuture,
"Waiting for the lazy init of mImeDrawsImeNavBarRes");
}
+ // Whether the current display has a navigation bar. When this is false (e.g. emulator),
+ // the IME should not draw the IME navigation bar.
+ final boolean hasNavigationBar = mWindowManagerInternal
+ .hasNavigationBar(mCurTokenDisplayId != INVALID_DISPLAY
+ ? mCurTokenDisplayId : DEFAULT_DISPLAY);
final boolean canImeDrawsImeNavBar =
- mImeDrawsImeNavBarRes != null && mImeDrawsImeNavBarRes.get()
- && hasNavigationBarOnCurrentDisplay();
+ mImeDrawsImeNavBarRes != null && mImeDrawsImeNavBarRes.get() && hasNavigationBar;
final boolean shouldShowImeSwitcherWhenImeIsShown = shouldShowImeSwitcherLocked(
InputMethodService.IME_ACTIVE | InputMethodService.IME_VISIBLE);
return (canImeDrawsImeNavBar ? InputMethodNavButtonFlags.IME_DRAWS_IME_NAV_BAR : 0)
@@ -2999,21 +3001,6 @@
? InputMethodNavButtonFlags.SHOW_IME_SWITCHER_WHEN_IME_IS_SHOWN : 0);
}
- /**
- * Whether the current display has a navigation bar. When this is {@code false} (e.g. emulator),
- * the IME should <em>not</em> draw the IME navigation bar.
- */
- @GuardedBy("ImfLock.class")
- private boolean hasNavigationBarOnCurrentDisplay() {
- final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
- try {
- return wm.hasNavigationBar(mCurTokenDisplayId != INVALID_DISPLAY
- ? mCurTokenDisplayId : DEFAULT_DISPLAY);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
@GuardedBy("ImfLock.class")
private boolean shouldShowImeSwitcherLocked(int visibility) {
if (!mShowOngoingImeSwitcherForPhones) return false;
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 0cfdaf2..10b6052 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -195,8 +195,12 @@
mApplicationKeyStorage = applicationKeyStorage;
mTestCertHelper = testOnlyInsecureCertificateHelper;
mCleanupManager = cleanupManager;
- // Clears data for removed users.
- mCleanupManager.verifyKnownUsers();
+ try {
+ // Clears data for removed users.
+ mCleanupManager.verifyKnownUsers();
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to verify known users", e);
+ }
try {
mRecoverableKeyGenerator = RecoverableKeyGenerator.newInstance(mDatabase);
} catch (NoSuchAlgorithmException e) {
diff --git a/services/core/java/com/android/server/media/LegacyBluetoothRouteController.java b/services/core/java/com/android/server/media/LegacyBluetoothRouteController.java
index e31a7fc..1980403 100644
--- a/services/core/java/com/android/server/media/LegacyBluetoothRouteController.java
+++ b/services/core/java/com/android/server/media/LegacyBluetoothRouteController.java
@@ -508,7 +508,11 @@
case BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED:
clearActiveRoutesWithType(MediaRoute2Info.TYPE_BLUETOOTH_A2DP);
if (device != null) {
- addActiveRoute(mBluetoothRoutes.get(device.getAddress()));
+ if (DEBUG) {
+ Log.d(TAG, "Setting active a2dp devices. device=" + device);
+ }
+
+ addActiveDevices(device);
}
notifyBluetoothRoutesUpdated();
break;
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
index 515c7fb..13d1662 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
@@ -932,6 +932,7 @@
if (callback == null) {
throw new IllegalArgumentException("callback must not be null");
}
+ Slog.v(TAG, "Start the token instance " + this);
// Cache result of calling into ActivityManagerService outside of the lock, to prevent
// deadlock with WindowManagerService.
final boolean hasFGS = mActivityManagerInternal.hasRunningForegroundService(
@@ -951,9 +952,6 @@
throw new SecurityException("Media projections require a foreground service"
+ " of type ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION");
}
-
- mCallback = callback;
- registerCallback(mCallback);
try {
mToken = callback.asBinder();
mDeathEater = () -> {
@@ -998,6 +996,11 @@
}
}
startProjectionLocked(this);
+
+ // Register new callbacks after stop has been dispatched to previous session.
+ mCallback = callback;
+ registerCallback(mCallback);
+
// Mark this token as used when the app gets the MediaProjection instance.
mCountStarts++;
}
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLogger.java b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
index b015a72..d2e980b 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLogger.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
@@ -39,6 +39,7 @@
import com.android.internal.logging.UiEventLogger;
import com.android.internal.util.FrameworkStatsLog;
+import java.time.Duration;
import java.util.ArrayList;
import java.util.Objects;
@@ -497,6 +498,7 @@
final boolean is_non_dismissible;
final int fsi_state;
final boolean is_locked;
+ final int age_in_minutes;
@DurationMillisLong long post_duration_millis; // Not final; calculated at the end.
NotificationReported(NotificationRecordPair p,
@@ -541,6 +543,9 @@
hasFullScreenIntent, hasFsiRequestedButDeniedFlag, eventType);
this.is_locked = p.r.isLocked();
+
+ this.age_in_minutes = NotificationRecordLogger.getAgeInMinutes(
+ p.r.getSbn().getPostTime(), p.r.getSbn().getNotification().when);
}
}
@@ -601,4 +606,13 @@
}
return FrameworkStatsLog.NOTIFICATION_REPORTED__FSI_STATE__NO_FSI;
}
+
+ /**
+ * @param postTimeMs time (in {@link System#currentTimeMillis} time) the notification was posted
+ * @param whenMs A timestamp related to this notification, in milliseconds since the epoch.
+ * @return difference in duration as an integer in minutes
+ */
+ static int getAgeInMinutes(long postTimeMs, long whenMs) {
+ return (int) Duration.ofMillis(postTimeMs - whenMs).toMinutes();
+ }
}
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
index 9da0e98..fc0a776 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
@@ -77,7 +77,8 @@
notificationReported.is_non_dismissible,
notificationReported.post_duration_millis,
notificationReported.fsi_state,
- notificationReported.is_locked);
+ notificationReported.is_locked,
+ notificationReported.age_in_minutes);
}
@Override
diff --git a/services/core/java/com/android/server/notification/PermissionHelper.java b/services/core/java/com/android/server/notification/PermissionHelper.java
index 85c140c..e14f7c0 100644
--- a/services/core/java/com/android/server/notification/PermissionHelper.java
+++ b/services/core/java/com/android/server/notification/PermissionHelper.java
@@ -16,7 +16,6 @@
package com.android.server.notification;
-import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET;
import static android.content.pm.PackageManager.GET_PERMISSIONS;
@@ -203,7 +202,6 @@
Context.DEVICE_ID_DEFAULT, userId, TAG);
}
int flagMask = FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_USER_FIXED;
- flagMask = userSet || !grant ? flagMask | FLAG_PERMISSION_GRANTED_BY_DEFAULT : flagMask;
if (userSet) {
mPermManager.updatePermissionFlags(packageName, NOTIFICATION_PERMISSION, flagMask,
FLAG_PERMISSION_USER_SET, true, Context.DEVICE_ID_DEFAULT, userId);
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index a700d32..71562dc 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -1022,6 +1022,8 @@
@VisibleForTesting
protected void setZenModeSetting(int zen) {
Global.putInt(mContext.getContentResolver(), Global.ZEN_MODE, zen);
+ ZenLog.traceSetZenMode(Global.getInt(mContext.getContentResolver(), Global.ZEN_MODE, -1),
+ "updated setting");
showZenUpgradeNotification(zen);
}
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index ffa2af1..316c4ac 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -1024,7 +1024,7 @@
if ("android".equals(packageName) || "system".equals(packageName)) {
return androidApplication();
}
- if ((flags & MATCH_KNOWN_PACKAGES) != 0) {
+ if ((flags & (MATCH_KNOWN_PACKAGES | MATCH_ARCHIVED_PACKAGES)) != 0) {
// Already generates the external package name
return generateApplicationInfoFromSettings(packageName,
flags, filterCallingUid, userId);
@@ -1518,7 +1518,6 @@
pi.sharedUserId = (sharedUser != null) ? sharedUser.getName() : null;
pi.firstInstallTime = state.getFirstInstallTimeMillis();
pi.lastUpdateTime = ps.getLastUpdateTime();
- pi.isArchived = isArchived(state);
ApplicationInfo ai = new ApplicationInfo();
ai.packageName = ps.getPackageName();
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index 7d59210..caf263d 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -24,6 +24,7 @@
import static android.content.pm.PackageManager.DELETE_SUCCEEDED;
import static android.content.pm.PackageManager.MATCH_KNOWN_PACKAGES;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
import static com.android.server.pm.PackageManagerService.DEBUG_COMPRESSION;
import static com.android.server.pm.PackageManagerService.DEBUG_REMOVE;
@@ -119,8 +120,6 @@
*/
public int deletePackageX(String packageName, long versionCode, int userId, int deleteFlags,
boolean removedBySystem) {
- final boolean isArchived = false; // TODO(b/278553670) Pass true during archival.
-
final PackageRemovedInfo info = new PackageRemovedInfo(mPm);
final boolean res;
@@ -250,6 +249,7 @@
if (res) {
final boolean killApp = (deleteFlags & PackageManager.DELETE_DONT_KILL_APP) == 0;
+ final boolean isArchived = (deleteFlags & PackageManager.DELETE_ARCHIVE) != 0;
info.sendPackageRemovedBroadcasts(killApp, removedBySystem, isArchived);
info.sendSystemPackageUpdatedBroadcasts();
PackageMetrics.onUninstallSucceeded(info, deleteFlags, removeUser);
diff --git a/services/core/java/com/android/server/pm/InstallRequest.java b/services/core/java/com/android/server/pm/InstallRequest.java
index ff347ac..a4ee3c8 100644
--- a/services/core/java/com/android/server/pm/InstallRequest.java
+++ b/services/core/java/com/android/server/pm/InstallRequest.java
@@ -22,6 +22,8 @@
import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
import static android.os.Process.INVALID_UID;
+import static com.android.server.art.model.DexoptResult.DexContainerFileDexoptResult;
+import static com.android.server.art.model.DexoptResult.PackageDexoptResult;
import static com.android.server.pm.PackageManagerService.EMPTY_INT_ARRAY;
import static com.android.server.pm.PackageManagerService.SCAN_AS_INSTANT_APP;
import static com.android.server.pm.PackageManagerService.TAG;
@@ -57,6 +59,7 @@
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.LinkedHashSet;
import java.util.List;
final class InstallRequest {
@@ -148,6 +151,9 @@
@NonNull
private int[] mUpdateBroadcastInstantUserIds = EMPTY_INT_ARRAY;
+ @NonNull
+ private ArrayList<String> mWarnings = new ArrayList<>();
+
// New install
InstallRequest(InstallingSession params) {
mUserId = params.getUser().getIdentifier();
@@ -658,6 +664,11 @@
return mUpdateBroadcastInstantUserIds;
}
+ @NonNull
+ public ArrayList<String> getWarnings() {
+ return mWarnings;
+ }
+
public void setScanFlags(int scanFlags) {
mScanFlags = scanFlags;
}
@@ -855,6 +866,10 @@
}
}
+ public void addWarning(@NonNull String warning) {
+ mWarnings.add(warning);
+ }
+
public void onPrepareStarted() {
if (mPackageMetrics != null) {
mPackageMetrics.onStepStarted(PackageMetrics.STEP_PREPARE);
@@ -904,22 +919,37 @@
}
public void onDexoptFinished(DexoptResult dexoptResult) {
- if (mPackageMetrics == null) {
- return;
- }
- mDexoptStatus = dexoptResult.getFinalStatus();
- if (mDexoptStatus != DexoptResult.DEXOPT_PERFORMED) {
- return;
- }
- long durationMillis = 0;
- for (DexoptResult.PackageDexoptResult packageResult :
- dexoptResult.getPackageDexoptResults()) {
- for (DexoptResult.DexContainerFileDexoptResult fileResult :
- packageResult.getDexContainerFileDexoptResults()) {
- durationMillis += fileResult.getDex2oatWallTimeMillis();
+ // Only report external profile warnings when installing from adb. The goal is to warn app
+ // developers if they have provided bad external profiles, so it's not beneficial to report
+ // those warnings in the normal app install workflow.
+ if (isInstallFromAdb()) {
+ var externalProfileErrors = new LinkedHashSet<String>();
+ for (PackageDexoptResult packageResult : dexoptResult.getPackageDexoptResults()) {
+ for (DexContainerFileDexoptResult fileResult :
+ packageResult.getDexContainerFileDexoptResults()) {
+ externalProfileErrors.addAll(fileResult.getExternalProfileErrors());
+ }
+ }
+ if (!externalProfileErrors.isEmpty()) {
+ addWarning("Error occurred during dexopt when processing external profiles:\n "
+ + String.join("\n ", externalProfileErrors));
}
}
- mPackageMetrics.onStepFinished(PackageMetrics.STEP_DEXOPT, durationMillis);
+
+ // Report dexopt metrics.
+ if (mPackageMetrics != null) {
+ mDexoptStatus = dexoptResult.getFinalStatus();
+ if (mDexoptStatus == DexoptResult.DEXOPT_PERFORMED) {
+ long durationMillis = 0;
+ for (PackageDexoptResult packageResult : dexoptResult.getPackageDexoptResults()) {
+ for (DexContainerFileDexoptResult fileResult :
+ packageResult.getDexContainerFileDexoptResults()) {
+ durationMillis += fileResult.getDex2oatWallTimeMillis();
+ }
+ }
+ mPackageMetrics.onStepFinished(PackageMetrics.STEP_DEXOPT, durationMillis);
+ }
+ }
}
public void onInstallCompleted() {
diff --git a/services/core/java/com/android/server/pm/PackageArchiver.java b/services/core/java/com/android/server/pm/PackageArchiver.java
index 551b1ae..803b6e4 100644
--- a/services/core/java/com/android/server/pm/PackageArchiver.java
+++ b/services/core/java/com/android/server/pm/PackageArchiver.java
@@ -17,6 +17,7 @@
package com.android.server.pm;
import static android.app.ComponentOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED;
+import static android.content.pm.PackageManager.DELETE_ARCHIVE;
import static android.content.pm.PackageManager.DELETE_KEEP_DATA;
import static android.os.PowerExemptionManager.REASON_PACKAGE_UNARCHIVE;
import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
@@ -142,7 +143,10 @@
mPm.mInstallerService.uninstall(
new VersionedPackage(packageName,
PackageManager.VERSION_CODE_HIGHEST),
- callerPackageName, DELETE_KEEP_DATA, intentSender, userId,
+ callerPackageName,
+ DELETE_ARCHIVE | DELETE_KEEP_DATA,
+ intentSender,
+ userId,
binderUid);
})
.exceptionally(
@@ -307,6 +311,46 @@
mPm.mHandler.post(() -> unarchiveInternal(packageName, userHandle, installerPackage));
}
+ /**
+ * Returns the icon of an archived app. This is the icon of the main activity of the app.
+ *
+ * <p> The icon is returned without any treatment/overlay. In the rare case the app had multiple
+ * launcher activities, only one of the icons is returned arbitrarily.
+ */
+ public Bitmap getArchivedAppIcon(@NonNull String packageName, @NonNull UserHandle user) {
+ Objects.requireNonNull(packageName);
+ Objects.requireNonNull(user);
+
+ Computer snapshot = mPm.snapshotComputer();
+ int callingUid = Binder.getCallingUid();
+ int userId = user.getIdentifier();
+ PackageStateInternal ps;
+ try {
+ ps = getPackageState(packageName, snapshot, callingUid, userId);
+ snapshot.enforceCrossUserPermission(callingUid, userId, true, false,
+ "getArchivedAppIcon");
+ verifyArchived(ps, userId);
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new ParcelableException(e);
+ }
+
+ List<ArchiveActivityInfo> activityInfos = ps.getUserStateOrDefault(
+ userId).getArchiveState().getActivityInfos();
+ if (activityInfos.size() == 0) {
+ return null;
+ }
+
+ // TODO(b/298452477) Handle monochrome icons.
+ // In the rare case the archived app defined more than two launcher activities, we choose
+ // the first one arbitrarily.
+ return decodeIcon(activityInfos.get(0));
+ }
+
+ @VisibleForTesting
+ Bitmap decodeIcon(ArchiveActivityInfo archiveActivityInfo) {
+ return BitmapFactory.decodeFile(archiveActivityInfo.getIconBitmap().toString());
+ }
+
private void verifyArchived(PackageStateInternal ps, int userId)
throws PackageManager.NameNotFoundException {
PackageUserStateInternal userState = ps.getUserStateOrDefault(userId);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 512d338..d699baa 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -5138,6 +5138,10 @@
if (!TextUtils.isEmpty(existing)) {
fillIn.putExtra(PackageInstaller.EXTRA_OTHER_PACKAGE_NAME, existing);
}
+ ArrayList<String> warnings = extras.getStringArrayList(PackageInstaller.EXTRA_WARNINGS);
+ if (!ArrayUtils.isEmpty(warnings)) {
+ fillIn.putStringArrayListExtra(PackageInstaller.EXTRA_WARNINGS, warnings);
+ }
}
try {
final BroadcastOptions options = BroadcastOptions.makeBasic();
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index b5b6ce0..700fae9 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1433,6 +1433,9 @@
break;
}
}
+ if (!request.getWarnings().isEmpty()) {
+ extras.putStringArrayList(PackageInstaller.EXTRA_WARNINGS, request.getWarnings());
+ }
return extras;
}
@@ -6389,6 +6392,11 @@
return getArchivedPackageInternal(packageName, userId);
}
+ @Override
+ public Bitmap getArchivedAppIcon(@NonNull String packageName, @NonNull UserHandle user) {
+ return mInstallerService.mPackageArchiver.getArchivedAppIcon(packageName, user);
+ }
+
/**
* Wait for the handler to finish handling all pending messages.
* @param timeoutMillis Maximum time in milliseconds to wait.
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 1b30c4b..72a7370 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -4397,10 +4397,21 @@
session.commit(receiver.getIntentSender());
if (!session.isStaged()) {
final Intent result = receiver.getResult();
- final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
- PackageInstaller.STATUS_FAILURE);
+ int status = result.getIntExtra(
+ PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE);
+ List<String> warnings =
+ result.getStringArrayListExtra(PackageInstaller.EXTRA_WARNINGS);
if (status == PackageInstaller.STATUS_SUCCESS) {
- if (logSuccess) {
+ if (!ArrayUtils.isEmpty(warnings)) {
+ // Don't start the output string with "Success" because that will make adb
+ // treat this as a success.
+ for (String warning : warnings) {
+ pw.println("Warning: " + warning);
+ }
+ // Treat warnings as failure to draw app developers' attention.
+ status = PackageInstaller.STATUS_FAILURE;
+ pw.println("Completed with warning(s)");
+ } else if (logSuccess) {
pw.println("Success");
}
} else {
diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
index 4eceb77..cc8e624 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -378,7 +378,6 @@
ai.privateFlags |= flag(state.isInstantApp(), ApplicationInfo.PRIVATE_FLAG_INSTANT)
| flag(state.isVirtualPreload(), ApplicationInfo.PRIVATE_FLAG_VIRTUAL_PRELOAD)
| flag(state.isHidden(), ApplicationInfo.PRIVATE_FLAG_HIDDEN);
-
if ((flags & PackageManager.FILTER_OUT_QUARANTINED_COMPONENTS) != 0
&& state.isQuarantined()) {
ai.enabled = false;
@@ -402,6 +401,14 @@
ai.resourceDirs = overlayPaths.getResourceDirs().toArray(new String[0]);
ai.overlayPaths = overlayPaths.getOverlayPaths().toArray(new String[0]);
}
+ ai.isArchived = isArchived(state);
+ }
+
+ // TODO(b/288142708) Check for userState.isInstalled() here once this bug is fixed.
+ // If an app has isInstalled() == true - it should not be filtered above in any case, currently
+ // it is.
+ private static boolean isArchived(PackageUserState userState) {
+ return userState.getArchiveState() != null;
}
@Nullable
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 c44b8852..bf20653 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -1490,12 +1490,9 @@
if (dir.isDirectory() && dir.canRead()) {
Collections.addAll(ret, dir.listFiles());
}
- // For IoT devices, we check the oem partition for default permissions for each app.
- if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_EMBEDDED, 0)) {
- dir = new File(Environment.getOemDirectory(), "etc/default-permissions");
- if (dir.isDirectory() && dir.canRead()) {
- Collections.addAll(ret, dir.listFiles());
- }
+ dir = new File(Environment.getOemDirectory(), "etc/default-permissions");
+ if (dir.isDirectory() && dir.canRead()) {
+ Collections.addAll(ret, dir.listFiles());
}
return ret.isEmpty() ? null : ret.toArray(new File[0]);
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 4d38239..b420acd 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -4773,7 +4773,7 @@
case KeyEvent.KEYCODE_STYLUS_BUTTON_SECONDARY:
case KeyEvent.KEYCODE_STYLUS_BUTTON_TERTIARY:
case KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL: {
- if (down && mStylusButtonsEnabled) {
+ if (mStylusButtonsEnabled) {
sendSystemKeyToStatusBarAsync(event);
}
result &= ~ACTION_PASS_TO_USER;
diff --git a/services/core/java/com/android/server/security/TEST_MAPPING b/services/core/java/com/android/server/security/TEST_MAPPING
index 673456f..29d52ff 100644
--- a/services/core/java/com/android/server/security/TEST_MAPPING
+++ b/services/core/java/com/android/server/security/TEST_MAPPING
@@ -1,5 +1,5 @@
{
- "postsubmit": [
+ "presubmit": [
{
"name": "CtsSecurityTestCases",
"options": [
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
index c526016..a5c0fb3 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
@@ -1454,6 +1454,7 @@
boolean hasDesiredDemuxCap = request.desiredFilterTypes
!= DemuxFilterMainType.UNDEFINED;
int smallestNumOfSupportedCaps = Integer.SIZE + 1;
+ int smallestNumOfSupportedCapsInUse = Integer.SIZE + 1;
for (DemuxResource dr : getDemuxResources().values()) {
if (!hasDesiredDemuxCap || dr.hasSufficientCaps(request.desiredFilterTypes)) {
if (!dr.isInUse()) {
@@ -1476,12 +1477,18 @@
currentLowestPriority = priority;
isRequestFromSameProcess = (requestClient.getProcessId()
== (getClientProfile(dr.getOwnerClientId())).getProcessId());
+
+ // reset the smallest caps when lower priority resource is found
+ smallestNumOfSupportedCapsInUse = numOfSupportedCaps;
+
shouldUpdate = true;
- }
- // update smallest caps
- if (smallestNumOfSupportedCaps > numOfSupportedCaps) {
- smallestNumOfSupportedCaps = numOfSupportedCaps;
- shouldUpdate = true;
+ } else {
+ // This is the case when the priority is the same as previously found
+ // one. Update smallest caps when priority.
+ if (smallestNumOfSupportedCapsInUse > numOfSupportedCaps) {
+ smallestNumOfSupportedCapsInUse = numOfSupportedCaps;
+ shouldUpdate = true;
+ }
}
if (shouldUpdate) {
inUseLowestPriorityDrHandle = dr.getHandle();
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 00992a0..4c525e9 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -3749,7 +3749,11 @@
mContext.getMainThreadHandler().removeCallbacks(
wallpaper.connection.mTryToRebindRunnable);
- mContext.unbindService(wallpaper.connection);
+ try {
+ mContext.unbindService(wallpaper.connection);
+ } catch (IllegalArgumentException e) {
+ Slog.w(TAG, "Error unbinding wallpaper when detaching", e);
+ }
wallpaper.connection = null;
if (wallpaper == mLastWallpaper) {
mLastWallpaper = null;
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index dca2b6f..5d0a457 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -565,8 +565,8 @@
boolean forceNewConfig; // force re-create with new config next time
boolean supportsEnterPipOnTaskSwitch; // This flag is set by the system to indicate that the
// activity can enter picture in picture while pausing (only when switching to another task)
+ // The PiP params used when deferring the entering of picture-in-picture.
PictureInPictureParams pictureInPictureArgs = new PictureInPictureParams.Builder().build();
- // The PiP params used when deferring the entering of picture-in-picture.
boolean shouldDockBigOverlays;
int launchCount; // count of launches since last state
long lastLaunchTime; // time of last launch of this activity
@@ -580,7 +580,7 @@
IBinder mRequestedLaunchingTaskFragmentToken;
// Tracking splash screen status from previous activity
- boolean mSplashScreenStyleSolidColor = false;
+ boolean mAllowIconSplashScreen = true;
boolean mPauseSchedulePendingForPip = false;
@@ -1732,6 +1732,16 @@
mAnimatingActivityRegistry = registry;
}
+ boolean canAutoEnterPip() {
+ // beforeStopping=false since the actual pip-ing will take place after startPausing()
+ final boolean activityCanPip = checkEnterPictureInPictureState(
+ "startActivityUnchecked", false /* beforeStopping */);
+
+ // check if this activity is about to auto-enter pip
+ return activityCanPip && pictureInPictureArgs != null
+ && pictureInPictureArgs.isAutoEnterEnabled();
+ }
+
/**
* Sets {@link #mLastParentBeforePip} to the current parent Task, it's caller's job to ensure
* {@link #getTask()} is set before this is called.
@@ -2398,8 +2408,7 @@
@VisibleForTesting
boolean addStartingWindow(String pkg, int resolvedTheme, ActivityRecord from, boolean newTask,
boolean taskSwitch, boolean processRunning, boolean allowTaskSnapshot,
- boolean activityCreated, boolean isSimple,
- boolean activityAllDrawn) {
+ boolean activityCreated, boolean allowIcon, boolean activityAllDrawn) {
// If the display is frozen, we won't do anything until the actual window is
// displayed so there is no reason to put in the starting window.
if (!okToDisplay()) {
@@ -2434,8 +2443,8 @@
final int typeParameter = StartingSurfaceController
.makeStartingWindowTypeParameter(newTask, taskSwitch, processRunning,
- allowTaskSnapshot, activityCreated, isSimple, useLegacy, activityAllDrawn,
- type, packageName, mUserId);
+ allowTaskSnapshot, activityCreated, allowIcon, useLegacy,
+ activityAllDrawn, type, packageName, mUserId);
if (type == STARTING_WINDOW_TYPE_SNAPSHOT) {
if (isActivityTypeHome()) {
@@ -6737,7 +6746,7 @@
void onFirstWindowDrawn(WindowState win) {
firstWindowDrawn = true;
// stop tracking
- mSplashScreenStyleSolidColor = true;
+ mAllowIconSplashScreen = false;
if (mStartingWindow != null) {
ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Finish starting %s"
@@ -6786,7 +6795,7 @@
void onStartingWindowDrawn() {
boolean wasTaskVisible = false;
if (task != null) {
- mSplashScreenStyleSolidColor = true;
+ mAllowIconSplashScreen = false;
wasTaskVisible = !setTaskHasBeenVisible();
}
@@ -7309,52 +7318,69 @@
}
return false;
}
- private boolean shouldUseSolidColorSplashScreen(ActivityRecord sourceRecord,
+
+ /**
+ * Checks whether an icon splash screen can be used in the starting window based on the
+ * preference in the {@code options} and this activity's theme, giving higher priority to the
+ * {@code options}'s preference.
+ *
+ * When no preference is specified, a default behaviour is defined:
+ * - if the activity is started from the home or shell app, an icon can be used
+ * - if the activity is started from SystemUI, an icon should not be used
+ * - if there is a launching activity, use its preference
+ * - if none of the above is met, only use an icon when the activity is started for the first
+ * time from a System app
+ *
+ * The returned value is sent to WmShell, which will make the final decision on what splash
+ * screen type will be used.
+ *
+ * @return true if an icon can be used in the splash screen
+ * false when an icon should not be used in the splash screen
+ */
+ private boolean canUseIconSplashScreen(ActivityRecord sourceRecord,
boolean startActivity, ActivityOptions options, int resolvedTheme) {
if (sourceRecord == null && !startActivity) {
- // Use simple style if this activity is not top activity. This could happen when adding
- // a splash screen window to the warm start activity which is re-create because top is
- // finishing.
+ // Shouldn't use an icon if this activity is not top activity. This could happen when
+ // adding a splash screen window to the warm start activity which is re-create because
+ // top is finishing.
final ActivityRecord above = task.getActivityAbove(this);
if (above != null) {
- return true;
+ return false;
}
}
// setSplashScreenStyle decide in priority of windowSplashScreenBehavior.
- if (options != null) {
- final int optionsStyle = options.getSplashScreenStyle();
- if (optionsStyle == SplashScreen.SPLASH_SCREEN_STYLE_SOLID_COLOR) {
- return true;
- } else if (optionsStyle == SplashScreen.SPLASH_SCREEN_STYLE_ICON
- || isIconStylePreferred(resolvedTheme)) {
- return false;
- }
- // Choose the default behavior for Launcher and SystemUI when the SplashScreen style is
- // not specified in the ActivityOptions.
- if (mLaunchSourceType == LAUNCH_SOURCE_TYPE_HOME
- || launchedFromUid == Process.SHELL_UID) {
- return false;
- } else if (mLaunchSourceType == LAUNCH_SOURCE_TYPE_SYSTEMUI) {
- return true;
- }
- } else if (isIconStylePreferred(resolvedTheme)) {
+ final int optionsStyle = options != null ? options.getSplashScreenStyle() :
+ SplashScreen.SPLASH_SCREEN_STYLE_UNDEFINED;
+ if (optionsStyle == SplashScreen.SPLASH_SCREEN_STYLE_SOLID_COLOR) {
return false;
- }
- if (sourceRecord == null) {
- sourceRecord = searchCandidateLaunchingActivity();
+ } else if (optionsStyle == SplashScreen.SPLASH_SCREEN_STYLE_ICON
+ || isIconStylePreferred(resolvedTheme)) {
+ return true;
}
- if (sourceRecord != null && !sourceRecord.isActivityTypeHome()) {
- return sourceRecord.mSplashScreenStyleSolidColor;
- }
+ // Choose the default behavior when neither the ActivityRecord nor the activity theme have
+ // specified a splash screen style.
- // If this activity was launched from Launcher or System for first start, never use a
- // solid color splash screen.
- // Need to check sourceRecord before in case this activity is launched from service.
- return !startActivity || !(mLaunchSourceType == LAUNCH_SOURCE_TYPE_SYSTEM
- || mLaunchSourceType == LAUNCH_SOURCE_TYPE_HOME
- || launchedFromUid == Process.SHELL_UID);
+ if (mLaunchSourceType == LAUNCH_SOURCE_TYPE_HOME || launchedFromUid == Process.SHELL_UID) {
+ return true;
+ } else if (mLaunchSourceType == LAUNCH_SOURCE_TYPE_SYSTEMUI) {
+ return false;
+ } else {
+ // Need to check sourceRecord in case this activity is launched from a service or a
+ // trampoline activity.
+ if (sourceRecord == null) {
+ sourceRecord = searchCandidateLaunchingActivity();
+ }
+
+ if (sourceRecord != null) {
+ return sourceRecord.mAllowIconSplashScreen;
+ }
+
+ // Use an icon if the activity was launched from System for the first start.
+ // Otherwise, can't use an icon splash screen.
+ return mLaunchSourceType == LAUNCH_SOURCE_TYPE_SYSTEM && startActivity;
+ }
}
private int getSplashscreenTheme(ActivityOptions options) {
@@ -7417,7 +7443,7 @@
final int resolvedTheme = evaluateStartingWindowTheme(prev, packageName, theme,
splashScreenTheme);
- mSplashScreenStyleSolidColor = shouldUseSolidColorSplashScreen(sourceRecord, startActivity,
+ mAllowIconSplashScreen = canUseIconSplashScreen(sourceRecord, startActivity,
startOptions, resolvedTheme);
final boolean activityCreated =
@@ -7429,7 +7455,7 @@
final boolean scheduled = addStartingWindow(packageName, resolvedTheme,
prev, newTask || newSingleActivity, taskSwitch, processRunning,
- allowTaskSnapshot(), activityCreated, mSplashScreenStyleSolidColor, allDrawn);
+ allowTaskSnapshot(), activityCreated, mAllowIconSplashScreen, allDrawn);
if (DEBUG_STARTING_WINDOW_VERBOSE && scheduled) {
Slog.d(TAG, "Scheduled starting window for " + this);
}
@@ -9180,7 +9206,13 @@
getRequestedOverrideWindowingMode() == WINDOWING_MODE_UNDEFINED
? newParentConfig.windowConfiguration.getWindowingMode()
: getRequestedOverrideWindowingMode();
- if (getWindowingMode() != projectedWindowingMode) {
+ if (getWindowingMode() != projectedWindowingMode
+ // Do not collect a pip activity about to enter pinned mode
+ // as a part of WindowOrganizerController#finishTransition().
+ // If not checked the activity might be collected for the wrong transition,
+ // such as a TRANSIT_OPEN transition requested right after TRANSIT_PIP.
+ && !(mWaitForEnteringPinnedMode
+ && mTransitionController.inFinishingTransition(this))) {
mTransitionController.collect(this);
}
}
@@ -9869,7 +9901,6 @@
ProtoLog.i(WM_DEBUG_STATES, "Moving to %s Relaunching %s callers=%s" ,
(andResume ? "RESUMED" : "PAUSED"), this, Debug.getCallers(6));
forceNewConfig = false;
- startRelaunching();
final ClientTransactionItem callbackItem = ActivityRelaunchItem.obtain(token,
pendingResults, pendingNewIntents, configChangeFlags,
new MergedConfiguration(getProcessGlobalConfiguration(),
@@ -9886,11 +9917,12 @@
transaction.addCallback(callbackItem);
transaction.setLifecycleStateRequest(lifecycleItem);
mAtmService.getLifecycleManager().scheduleTransaction(transaction);
+ startRelaunching();
// Note: don't need to call pauseIfSleepingLocked() here, because the caller will only
// request resume if this activity is currently resumed, which implies we aren't
// sleeping.
} catch (RemoteException e) {
- ProtoLog.i(WM_DEBUG_STATES, "Relaunch failed %s", e);
+ Slog.w(TAG, "Failed to relaunch " + this + ": " + e);
}
if (andResume) {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 6999c6a..f38f6b0 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -313,6 +313,8 @@
public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
private static final String GRAMMATICAL_GENDER_PROPERTY = "persist.sys.grammatical_gender";
private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityTaskManagerService" : TAG_ATM;
+ private static final String ENABLE_PIP2_IMPLEMENTATION =
+ "persist.wm.debug.enable_pip2_implementation";
static final String TAG_ROOT_TASK = TAG + POSTFIX_ROOT_TASK;
static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
@@ -1841,8 +1843,12 @@
RemoteCallback navigationObserver, BackAnimationAdapter adapter) {
mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS,
"startBackNavigation()");
-
- return mBackNavigationController.startBackNavigation(navigationObserver, adapter);
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ return mBackNavigationController.startBackNavigation(navigationObserver, adapter);
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
}
/**
@@ -3613,6 +3619,28 @@
}
}
+ /**
+ * Prepare to enter PiP mode after {@link TransitionController#requestStartTransition}.
+ *
+ * @param r activity auto entering pip
+ * @return true if the activity is about to auto-enter pip or is already in pip mode.
+ */
+ boolean prepareAutoEnterPictureAndPictureMode(ActivityRecord r) {
+ // If the activity is already in picture in picture mode, then just return early
+ if (r.inPinnedWindowingMode()) {
+ return true;
+ }
+
+ if (r.canAutoEnterPip() && getTransitionController().getCollectingTransition() != null) {
+ // This will be used later to construct TransitionRequestInfo for Shell to resolve.
+ // It will also be passed into a direct moveActivityToPinnedRootTask() call via
+ // startTransition()
+ getTransitionController().getCollectingTransition().setPipActivity(r);
+ return true;
+ }
+ return false;
+ }
+
boolean enterPictureInPictureMode(@NonNull ActivityRecord r,
@NonNull PictureInPictureParams params, boolean fromClient) {
return enterPictureInPictureMode(r, params, fromClient, false /* isAutoEnter */);
@@ -7183,4 +7211,8 @@
ActivityTaskManagerService.this.unregisterTaskStackListener(listener);
}
}
+
+ static boolean isPip2ExperimentEnabled() {
+ return SystemProperties.getBoolean(ENABLE_PIP2_IMPLEMENTATION, false);
+ }
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index e523119..9bfc553 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -795,17 +795,13 @@
return false;
}
- // Try pausing the existing resumed activity in the same TaskFragment if any.
- final TaskFragment taskFragment = r.getTaskFragment();
- if (taskFragment != null && taskFragment.getResumedActivity() != null) {
- if (taskFragment.startPausing(mUserLeaving, false /* uiSleeping */, r, "realStart")) {
- return false;
- }
+ // Try pausing the existing resumed activity in the Task if any.
+ final Task task = r.getTask();
+ if (task.pauseActivityIfNeeded(r, "realStart")) {
+ return false;
}
- final Task task = r.getTask();
final Task rootTask = task.getRootTask();
-
beginDeferResume();
// The LaunchActivityItem also contains process configuration, so the configuration change
// from WindowProcessController#setProcess can be deferred. The major reason is that if
diff --git a/services/core/java/com/android/server/wm/ContentRecorder.java b/services/core/java/com/android/server/wm/ContentRecorder.java
index 7cd07d6..b499dad 100644
--- a/services/core/java/com/android/server/wm/ContentRecorder.java
+++ b/services/core/java/com/android/server/wm/ContentRecorder.java
@@ -83,6 +83,11 @@
@Nullable private Rect mLastRecordedBounds = null;
/**
+ * The last size of the surface mirrored out to.
+ */
+ @Nullable private Point mLastConsumingSurfaceSize = new Point(0, 0);
+
+ /**
* The last configuration orientation.
*/
@Configuration.Orientation
@@ -141,60 +146,64 @@
*/
void onConfigurationChanged(@Configuration.Orientation int lastOrientation) {
// Update surface for MediaProjection, if this DisplayContent is being used for recording.
- if (isCurrentlyRecording() && mLastRecordedBounds != null) {
- // Recording has already begun, but update recording since the display is now on.
- if (mRecordedWindowContainer == null) {
+ if (!isCurrentlyRecording() || mLastRecordedBounds == null) {
+ return;
+ }
+
+ // Recording has already begun, but update recording since the display is now on.
+ if (mRecordedWindowContainer == null) {
+ ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
+ "Content Recording: Unexpectedly null window container; unable to update "
+ + "recording for display %d",
+ mDisplayContent.getDisplayId());
+ return;
+ }
+
+ // TODO(b/297514518) Do not start capture if the app is in PIP, the bounds are
+ // inaccurate.
+ if (mContentRecordingSession.getContentToRecord() == RECORD_CONTENT_TASK) {
+ final Task capturedTask = mRecordedWindowContainer.asTask();
+ if (capturedTask.inPinnedWindowingMode()) {
ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
- "Content Recording: Unexpectedly null window container; unable to update "
- + "recording for display %d",
+ "Content Recording: Display %d was already recording, but "
+ + "pause capture since the task is in PIP",
mDisplayContent.getDisplayId());
+ pauseRecording();
return;
}
+ }
- // TODO(b/297514518) Do not start capture if the app is in PIP, the bounds are
- // inaccurate.
- if (mContentRecordingSession.getContentToRecord() == RECORD_CONTENT_TASK) {
- final Task capturedTask = mRecordedWindowContainer.asTask();
- if (capturedTask.inPinnedWindowingMode()) {
- ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
- "Content Recording: Display %d was already recording, but "
- + "pause capture since the task is in PIP",
- mDisplayContent.getDisplayId());
- pauseRecording();
- return;
- }
- }
-
- ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
- "Content Recording: Display %d was already recording, so apply "
- + "transformations if necessary",
- mDisplayContent.getDisplayId());
- // Retrieve the size of the region to record, and continue with the update
- // if the bounds or orientation has changed.
- final Rect recordedContentBounds = mRecordedWindowContainer.getBounds();
- @Configuration.Orientation int recordedContentOrientation =
- mRecordedWindowContainer.getConfiguration().orientation;
- if (!mLastRecordedBounds.equals(recordedContentBounds)
- || lastOrientation != recordedContentOrientation) {
- Point surfaceSize = fetchSurfaceSizeIfPresent();
- if (surfaceSize != null) {
- ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
- "Content Recording: Going ahead with updating recording for display "
- + "%d to new bounds %s and/or orientation %d.",
- mDisplayContent.getDisplayId(), recordedContentBounds,
- recordedContentOrientation);
- updateMirroredSurface(mRecordedWindowContainer.getSyncTransaction(),
- recordedContentBounds, surfaceSize);
- } else {
- // If the surface removed, do nothing. We will handle this via onDisplayChanged
- // (the display will be off if the surface is removed).
- ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
- "Content Recording: Unable to update recording for display %d to new "
- + "bounds %s and/or orientation %d, since the surface is not "
- + "available.",
- mDisplayContent.getDisplayId(), recordedContentBounds,
- recordedContentOrientation);
- }
+ ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
+ "Content Recording: Display %d was already recording, so apply "
+ + "transformations if necessary",
+ mDisplayContent.getDisplayId());
+ // Retrieve the size of the region to record, and continue with the update
+ // if the bounds or orientation has changed.
+ final Rect recordedContentBounds = mRecordedWindowContainer.getBounds();
+ @Configuration.Orientation int recordedContentOrientation =
+ mRecordedWindowContainer.getConfiguration().orientation;
+ final Point surfaceSize = fetchSurfaceSizeIfPresent();
+ if (!mLastRecordedBounds.equals(recordedContentBounds)
+ || lastOrientation != recordedContentOrientation
+ || !mLastConsumingSurfaceSize.equals(surfaceSize)) {
+ if (surfaceSize != null) {
+ ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
+ "Content Recording: Going ahead with updating recording for display "
+ + "%d to new bounds %s and/or orientation %d and/or surface "
+ + "size %s",
+ mDisplayContent.getDisplayId(), recordedContentBounds,
+ recordedContentOrientation, surfaceSize);
+ updateMirroredSurface(mRecordedWindowContainer.getSyncTransaction(),
+ recordedContentBounds, surfaceSize);
+ } else {
+ // If the surface removed, do nothing. We will handle this via onDisplayChanged
+ // (the display will be off if the surface is removed).
+ ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
+ "Content Recording: Unable to update recording for display %d to new "
+ + "bounds %s and/or orientation %d and/or surface size %s, "
+ + "since the surface is not available.",
+ mDisplayContent.getDisplayId(), recordedContentBounds,
+ recordedContentOrientation, surfaceSize);
}
}
}
@@ -498,10 +507,13 @@
}
ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
- "Content Recording: Apply transformations of shift %d x %d, scale %f, crop %d x "
- + "%d for display %d",
+ "Content Recording: Apply transformations of shift %d x %d, scale %f, crop (aka "
+ + "recorded content size) %d x %d for display %d; display has size %d x "
+ + "%d; surface has size %d x %d",
shiftedX, shiftedY, scale, recordedContentBounds.width(),
- recordedContentBounds.height(), mDisplayContent.getDisplayId());
+ recordedContentBounds.height(), mDisplayContent.getDisplayId(),
+ mDisplayContent.getConfiguration().screenWidthDp,
+ mDisplayContent.getConfiguration().screenHeightDp, surfaceSize.x, surfaceSize.y);
transaction
// Crop the area to capture to exclude the 'extra' wallpaper that is used
@@ -515,6 +527,8 @@
// the content will no longer be centered in the output surface.
.setPosition(mRecordedSurface, shiftedX /* x */, shiftedY /* y */);
mLastRecordedBounds = new Rect(recordedContentBounds);
+ mLastConsumingSurfaceSize.x = surfaceSize.x;
+ mLastConsumingSurfaceSize.y = surfaceSize.y;
// Request to notify the client about the resize.
mMediaProjectionManager.notifyActiveProjectionCapturedContentResized(
mLastRecordedBounds.width(), mLastRecordedBounds.height());
@@ -523,6 +537,7 @@
/**
* Returns a non-null {@link Point} if the surface is present, or null otherwise
*/
+ @Nullable
private Point fetchSurfaceSizeIfPresent() {
// Retrieve the default size of the surface the app provided to
// MediaProjection#createVirtualDisplay. Note the app is the consumer of the surface,
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 996d666..4309e72 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -6040,8 +6040,9 @@
mOffTokenAcquirer.release(mDisplayId);
}
ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
- "Content Recording: Display %d state is now (%d), so update recording?",
- mDisplayId, displayState);
+ "Content Recording: Display %d state was (%d), is now (%d), so update "
+ + "recording?",
+ mDisplayId, lastDisplayState, displayState);
if (lastDisplayState != displayState) {
// If state is on due to surface being added, then start recording.
// If state is off due to surface being removed, then stop recording.
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 8519e4d..0f1a105 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -21,6 +21,7 @@
import static android.content.ClipDescription.MIMETYPE_APPLICATION_TASK;
import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP;
+
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
import static com.android.server.wm.DragDropController.MSG_ANIMATION_END;
@@ -32,6 +33,7 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.MY_PID;
import static com.android.server.wm.WindowManagerService.MY_UID;
+
import static java.util.concurrent.CompletableFuture.completedFuture;
import android.animation.Animator;
@@ -46,6 +48,7 @@
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
+import android.os.InputConfig;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
@@ -183,13 +186,8 @@
// Crop the input surface to the display size.
mTmpClipRect.set(0, 0, mDisplaySize.x, mDisplaySize.y);
- // Make trusted overlay to not block any touches while D&D ongoing and allowing
- // touches to pass through to windows underneath. This allows user to interact with the
- // UI to navigate while dragging.
- h.setTrustedOverlay(mTransaction, mInputSurface, true);
mTransaction.show(mInputSurface)
.setInputWindowInfo(mInputSurface, h)
- .setTrustedOverlay(mInputSurface, true)
.setLayer(mInputSurface, Integer.MAX_VALUE)
.setCrop(mInputSurface, mTmpClipRect);
@@ -379,6 +377,11 @@
mDragWindowHandle.ownerUid = MY_UID;
mDragWindowHandle.scaleFactor = 1.0f;
+ // InputConfig.TRUSTED_OVERLAY: To not block any touches while D&D ongoing and allowing
+ // touches to pass through to windows underneath. This allows user to interact with the
+ // UI to navigate while dragging.
+ mDragWindowHandle.inputConfig = InputConfig.TRUSTED_OVERLAY;
+
// The drag window cannot receive new touches.
mDragWindowHandle.touchableRegion.setEmpty();
diff --git a/services/core/java/com/android/server/wm/InputConsumerImpl.java b/services/core/java/com/android/server/wm/InputConsumerImpl.java
index c21930d..39622c1 100644
--- a/services/core/java/com/android/server/wm/InputConsumerImpl.java
+++ b/services/core/java/com/android/server/wm/InputConsumerImpl.java
@@ -74,7 +74,7 @@
mWindowHandle.ownerPid = WindowManagerService.MY_PID;
mWindowHandle.ownerUid = WindowManagerService.MY_UID;
mWindowHandle.scaleFactor = 1.0f;
- mWindowHandle.inputConfig = InputConfig.NOT_FOCUSABLE;
+ mWindowHandle.inputConfig = InputConfig.NOT_FOCUSABLE | InputConfig.TRUSTED_OVERLAY;
mInputSurface = mService.makeSurfaceBuilder(
mService.mRoot.getDisplayContent(displayId).getSession())
@@ -129,14 +129,12 @@
void show(SurfaceControl.Transaction t, WindowContainer w) {
t.show(mInputSurface);
- mWindowHandle.setTrustedOverlay(t, mInputSurface, true);
t.setInputWindowInfo(mInputSurface, mWindowHandle);
t.setRelativeLayer(mInputSurface, w.getSurfaceControl(), 1);
}
void show(SurfaceControl.Transaction t, int layer) {
t.show(mInputSurface);
- mWindowHandle.setTrustedOverlay(t, mInputSurface, true);
t.setInputWindowInfo(mInputSurface, mWindowHandle);
t.setLayer(mInputSurface, layer);
}
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index f77da62..825d38b 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -40,10 +40,12 @@
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL;
import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.LOGTAG_INPUT_FOCUS;
+
import static java.lang.Integer.MAX_VALUE;
import android.annotation.Nullable;
@@ -730,7 +732,7 @@
new InputWindowHandle(null /* inputApplicationHandle */, displayId));
inputWindowHandle.setName(name);
inputWindowHandle.setLayoutParamsType(TYPE_SECURE_SYSTEM_OVERLAY);
- inputWindowHandle.setTrustedOverlay(t, sc, true);
+ inputWindowHandle.setTrustedOverlay(true);
populateOverlayInputInfo(inputWindowHandle);
setInputWindowInfoIfNeeded(t, sc, inputWindowHandle);
}
diff --git a/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java b/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java
index 90d81bd..64b7a60 100644
--- a/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java
+++ b/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java
@@ -195,11 +195,6 @@
mChanged = true;
}
- void setTrustedOverlay(SurfaceControl.Transaction t, SurfaceControl sc,
- boolean trustedOverlay) {
- mHandle.setTrustedOverlay(t, sc, trustedOverlay);
- }
-
void setOwnerPid(int pid) {
if (mHandle.ownerPid == pid) {
return;
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index 01786be..d2d6552 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -661,6 +661,10 @@
@ScreenOrientation
int overrideOrientationIfNeeded(@ScreenOrientation int candidate) {
if (shouldApplyUserFullscreenOverride()) {
+ Slog.v(TAG, "Requested orientation " + screenOrientationToString(candidate) + " for "
+ + mActivityRecord + " is overridden to "
+ + screenOrientationToString(SCREEN_ORIENTATION_USER)
+ + " by user aspect ratio settings.");
return SCREEN_ORIENTATION_USER;
}
@@ -668,6 +672,14 @@
// orientation.
candidate = mActivityRecord.mWmService.mapOrientationRequest(candidate);
+ if (shouldApplyUserMinAspectRatioOverride() && !isFixedOrientation(candidate)) {
+ Slog.v(TAG, "Requested orientation " + screenOrientationToString(candidate) + " for "
+ + mActivityRecord + " is overridden to "
+ + screenOrientationToString(SCREEN_ORIENTATION_PORTRAIT)
+ + " by user aspect ratio settings.");
+ return SCREEN_ORIENTATION_PORTRAIT;
+ }
+
if (FALSE.equals(mBooleanPropertyAllowOrientationOverride)) {
return candidate;
}
@@ -1126,11 +1138,25 @@
return computeAspectRatio(bounds);
}
+ /**
+ * Whether we should enable users to resize the current app.
+ */
+ boolean shouldEnableUserAspectRatioSettings() {
+ // We use mBooleanPropertyAllowUserAspectRatioOverride to allow apps to opt-out which has
+ // effect only if explicitly false. If mBooleanPropertyAllowUserAspectRatioOverride is null,
+ // the current app doesn't opt-out so the first part of the predicate is true.
+ return !FALSE.equals(mBooleanPropertyAllowUserAspectRatioOverride)
+ && mLetterboxConfiguration.isUserAppAspectRatioSettingsEnabled()
+ && mActivityRecord.mDisplayContent != null
+ && mActivityRecord.mDisplayContent.getIgnoreOrientationRequest();
+ }
+
+ /**
+ * Whether we should apply the user aspect ratio override to the min aspect ratio for the
+ * current app.
+ */
boolean shouldApplyUserMinAspectRatioOverride() {
- if (FALSE.equals(mBooleanPropertyAllowUserAspectRatioOverride)
- || !mLetterboxConfiguration.isUserAppAspectRatioSettingsEnabled()
- || mActivityRecord.mDisplayContent == null
- || !mActivityRecord.mDisplayContent.getIgnoreOrientationRequest()) {
+ if (!shouldEnableUserAspectRatioSettings()) {
return false;
}
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index be90588..ee05e35 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.app.ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
@@ -117,6 +118,11 @@
return;
}
if (targetActivity.attachedToProcess()) {
+ if (targetActivity.app.getCurrentProcState() >= PROCESS_STATE_CACHED_ACTIVITY) {
+ Slog.v(TAG, "Skip preload recents for cached proc " + targetActivity.app);
+ // The process may be frozen that cannot receive binder call.
+ return;
+ }
// The activity may be relaunched if it cannot handle the current configuration
// changes. The activity will be paused state if it is relaunched, otherwise it
// keeps the original stopped state.
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index bbb8563..2c142cb 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -40,11 +40,9 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.HardwareBuffer;
-import android.os.IBinder;
import android.os.Trace;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
-import android.view.DisplayAddress;
import android.view.DisplayInfo;
import android.view.Surface;
import android.view.Surface.OutOfResourcesException;
@@ -57,7 +55,6 @@
import com.android.internal.R;
import com.android.internal.policy.TransitionAnimation;
import com.android.internal.protolog.common.ProtoLog;
-import com.android.server.display.DisplayControl;
import com.android.server.wm.SurfaceAnimator.AnimationType;
import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
@@ -171,32 +168,10 @@
try {
final ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer;
if (isSizeChanged) {
- final DisplayAddress address = displayInfo.address;
- if (!(address instanceof DisplayAddress.Physical)) {
- Slog.e(TAG, "Display does not have a physical address: " + displayId);
- return;
- }
- final DisplayAddress.Physical physicalAddress =
- (DisplayAddress.Physical) address;
- final IBinder displayToken = DisplayControl.getPhysicalDisplayToken(
- physicalAddress.getPhysicalDisplayId());
- if (displayToken == null) {
- Slog.e(TAG, "Display token is null.");
- return;
- }
// Temporarily not skip screenshot for the rounded corner overlays and screenshot
// the whole display to include the rounded corner overlays.
setSkipScreenshotForRoundedCornerOverlays(false, t);
- mRoundedCornerOverlay = displayContent.findRoundedCornerOverlays();
- final ScreenCapture.DisplayCaptureArgs captureArgs =
- new ScreenCapture.DisplayCaptureArgs.Builder(displayToken)
- .setSourceCrop(new Rect(0, 0, width, height))
- .setAllowProtected(true)
- .setCaptureSecureLayers(true)
- .setHintForSeamlessTransition(true)
- .build();
- screenshotBuffer = ScreenCapture.captureDisplay(captureArgs);
- } else {
+ }
ScreenCapture.LayerCaptureArgs captureArgs =
new ScreenCapture.LayerCaptureArgs.Builder(
displayContent.getSurfaceControl())
@@ -206,7 +181,6 @@
.setHintForSeamlessTransition(true)
.build();
screenshotBuffer = ScreenCapture.captureLayers(captureArgs);
- }
if (screenshotBuffer == null) {
Slog.w(TAG, "Unable to take screenshot of display " + displayId);
diff --git a/services/core/java/com/android/server/wm/StartingSurfaceController.java b/services/core/java/com/android/server/wm/StartingSurfaceController.java
index a55c232..a0517be 100644
--- a/services/core/java/com/android/server/wm/StartingSurfaceController.java
+++ b/services/core/java/com/android/server/wm/StartingSurfaceController.java
@@ -19,12 +19,12 @@
import static android.window.StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_CREATED;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_DRAWN;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_ALLOW_HANDLE_SOLID_COLOR_SCREEN;
+import static android.window.StartingWindowInfo.TYPE_PARAMETER_ALLOW_ICON;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_LEGACY_SPLASH_SCREEN;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_NEW_TASK;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_PROCESS_RUNNING;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_TASK_SWITCH;
-import static android.window.StartingWindowInfo.TYPE_PARAMETER_USE_SOLID_COLOR_SPLASH_SCREEN;
import static com.android.server.wm.ActivityRecord.STARTING_WINDOW_TYPE_SNAPSHOT;
import static com.android.server.wm.ActivityRecord.STARTING_WINDOW_TYPE_SPLASH_SCREEN;
@@ -102,7 +102,7 @@
static int makeStartingWindowTypeParameter(boolean newTask, boolean taskSwitch,
boolean processRunning, boolean allowTaskSnapshot, boolean activityCreated,
- boolean isSolidColor, boolean useLegacy, boolean activityDrawn, int startingWindowType,
+ boolean allowIcon, boolean useLegacy, boolean activityDrawn, int startingWindowType,
String packageName, int userId) {
int parameter = 0;
if (newTask) {
@@ -120,8 +120,8 @@
if (activityCreated || startingWindowType == STARTING_WINDOW_TYPE_SNAPSHOT) {
parameter |= TYPE_PARAMETER_ACTIVITY_CREATED;
}
- if (isSolidColor) {
- parameter |= TYPE_PARAMETER_USE_SOLID_COLOR_SPLASH_SCREEN;
+ if (allowIcon) {
+ parameter |= TYPE_PARAMETER_ALLOW_ICON;
}
if (useLegacy) {
parameter |= TYPE_PARAMETER_LEGACY_SPLASH_SCREEN;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 21526e7..b4b8a74 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -1262,6 +1262,37 @@
return null;
}
+ boolean pauseActivityIfNeeded(@Nullable ActivityRecord resuming, @NonNull String reason) {
+ if (!isLeafTask()) {
+ return false;
+ }
+
+ final int[] someActivityPaused = {0};
+ // Check if the direct child resumed activity in the leaf task needed to be paused if
+ // the leaf task is not a leaf task fragment.
+ if (!isLeafTaskFragment()) {
+ final ActivityRecord top = topRunningActivity();
+ final ActivityRecord resumedActivity = getResumedActivity();
+ if (resumedActivity != null && top.getTaskFragment() != this) {
+ // Pausing the resumed activity because it is occluded by other task fragment.
+ if (startPausing(false /* uiSleeping*/, resuming, reason)) {
+ someActivityPaused[0]++;
+ }
+ }
+ }
+
+ forAllLeafTaskFragments((taskFrag) -> {
+ final ActivityRecord resumedActivity = taskFrag.getResumedActivity();
+ if (resumedActivity != null && !taskFrag.canBeResumed(resuming)) {
+ if (taskFrag.startPausing(false /* uiSleeping*/, resuming, reason)) {
+ someActivityPaused[0]++;
+ }
+ }
+ }, true /* traverseTopToBottom */);
+
+ return someActivityPaused[0] > 0;
+ }
+
void updateTaskMovement(boolean toTop, boolean toBottom, int position) {
EventLogTags.writeWmTaskMoved(mTaskId, getRootTaskId(), getDisplayId(), toTop ? 1 : 0,
position);
@@ -3478,9 +3509,9 @@
}
}
// User Aspect Ratio Settings is enabled if the app is not in SCM
- info.topActivityEligibleForUserAspectRatioButton =
- mWmService.mLetterboxConfiguration.isUserAppAspectRatioSettingsEnabled()
- && top != null && !info.topActivityInSizeCompat;
+ info.topActivityEligibleForUserAspectRatioButton = top != null
+ && !info.topActivityInSizeCompat
+ && top.mLetterboxUiController.shouldEnableUserAspectRatioSettings();
info.topActivityBoundsLetterboxed = top != null && top.areBoundsLetterboxed();
}
@@ -5102,7 +5133,6 @@
void startActivityLocked(ActivityRecord r, @Nullable Task topTask, boolean newTask,
boolean isTaskSwitch, ActivityOptions options, @Nullable ActivityRecord sourceRecord) {
- final ActivityRecord pipCandidate = findEnterPipOnTaskSwitchCandidate(topTask);
Task rTask = r.getTask();
final boolean allowMoveToFront = options == null || !options.getAvoidMoveToFront();
final boolean isOrhasTask = rTask == this || hasChild(rTask);
@@ -5166,8 +5196,10 @@
// supporting picture-in-picture while pausing only if the starting activity
// would not be considered an overlay on top of the current activity
// (eg. not fullscreen, or the assistant)
- enableEnterPipOnTaskSwitch(pipCandidate,
- null /* toFrontTask */, r, options);
+ if (!ActivityTaskManagerService.isPip2ExperimentEnabled()) {
+ final ActivityRecord pipCandidate = findEnterPipOnTaskSwitchCandidate(topTask);
+ enableEnterPipOnTaskSwitch(pipCandidate, null /* toFrontTask */, r, options);
+ }
}
boolean doShow = true;
if (newTask) {
@@ -5240,7 +5272,7 @@
* enter PiP while it is pausing (if supported). Only one of {@param toFrontTask} or
* {@param toFrontActivity} should be set.
*/
- private static void enableEnterPipOnTaskSwitch(@Nullable ActivityRecord pipCandidate,
+ static void enableEnterPipOnTaskSwitch(@Nullable ActivityRecord pipCandidate,
@Nullable Task toFrontTask, @Nullable ActivityRecord toFrontActivity,
@Nullable ActivityOptions opts) {
if (pipCandidate == null) {
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 9af12ad..ae794a8 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -1271,27 +1271,9 @@
boolean pauseBackTasks(ActivityRecord resuming) {
final int[] someActivityPaused = {0};
forAllLeafTasks(leafTask -> {
- // Check if the direct child resumed activity in the leaf task needed to be paused if
- // the leaf task is not a leaf task fragment.
- if (!leafTask.isLeafTaskFragment()) {
- final ActivityRecord top = topRunningActivity();
- final ActivityRecord resumedActivity = leafTask.getResumedActivity();
- if (resumedActivity != null && top.getTaskFragment() != leafTask) {
- // Pausing the resumed activity because it is occluded by other task fragment.
- if (leafTask.startPausing(false /* uiSleeping*/, resuming, "pauseBackTasks")) {
- someActivityPaused[0]++;
- }
- }
+ if (leafTask.pauseActivityIfNeeded(resuming, "pauseBackTasks")) {
+ someActivityPaused[0]++;
}
-
- leafTask.forAllLeafTaskFragments((taskFrag) -> {
- final ActivityRecord resumedActivity = taskFrag.getResumedActivity();
- if (resumedActivity != null && !taskFrag.canBeResumed(resuming)) {
- if (taskFrag.startPausing(false /* uiSleeping*/, resuming, "pauseBackTasks")) {
- someActivityPaused[0]++;
- }
- }
- }, true /* traverseTopToBottom */);
}, true /* traverseTopToBottom */);
return someActivityPaused[0] > 0;
}
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 57f44cb..d1b5350 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -1643,7 +1643,17 @@
// next activity.
final boolean lastResumedCanPip = prev.checkEnterPictureInPictureState(
"shouldAutoPipWhilePausing", userLeaving);
- if (userLeaving && resumingOccludesParent && lastResumedCanPip
+
+ if (ActivityTaskManagerService.isPip2ExperimentEnabled()) {
+ // If a new task is being launched, then mark the existing top activity as
+ // supporting picture-in-picture while pausing only if the starting activity
+ // would not be considered an overlay on top of the current activity
+ // (eg. not fullscreen, or the assistant)
+ Task.enableEnterPipOnTaskSwitch(prev, resuming.getTask(),
+ resuming, resuming.getOptions());
+ }
+ if (prev.supportsEnterPipOnTaskSwitch && userLeaving
+ && resumingOccludesParent && lastResumedCanPip
&& prev.pictureInPictureArgs.isAutoEnterEnabled()) {
shouldAutoPip = true;
} else if (!lastResumedCanPip) {
@@ -1656,7 +1666,12 @@
}
if (prev.attachedToProcess()) {
- if (shouldAutoPip) {
+ if (shouldAutoPip && ActivityTaskManagerService.isPip2ExperimentEnabled()) {
+ prev.mPauseSchedulePendingForPip = true;
+ boolean willAutoPip = mAtmService.prepareAutoEnterPictureAndPictureMode(prev);
+ ProtoLog.d(WM_DEBUG_STATES, "Auto-PIP allowed, requesting PIP mode "
+ + "via requestStartTransition(): %s, willAutoPip: %b", prev, willAutoPip);
+ } else if (shouldAutoPip) {
prev.mPauseSchedulePendingForPip = true;
boolean didAutoPip = mAtmService.enterPictureInPictureMode(
prev, prev.pictureInPictureArgs, false /* fromClient */);
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 843e6d1..bbafa25 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -174,6 +174,8 @@
private final Token mToken;
private IApplicationThread mRemoteAnimApp;
+ private @Nullable ActivityRecord mPipActivity;
+
/** Only use for clean-up after binder death! */
private SurfaceControl.Transaction mStartTransaction = null;
private SurfaceControl.Transaction mFinishTransaction = null;
@@ -510,6 +512,21 @@
}
/**
+ * Set the pip-able activity participating in this transition.
+ * @param pipActivity activity about to enter pip
+ */
+ void setPipActivity(@Nullable ActivityRecord pipActivity) {
+ mPipActivity = pipActivity;
+ }
+
+ /**
+ * @return pip-able activity participating in this transition.
+ */
+ @Nullable ActivityRecord getPipActivity() {
+ return mPipActivity;
+ }
+
+ /**
* Only set flag to the parent tasks and activity itself.
*/
private void setTransientLaunchToChanges(@NonNull WindowContainer wc) {
@@ -1597,13 +1614,6 @@
}
}
- // Take task snapshots before the animation so that we can capture IME before it gets
- // transferred. If transition is transient, IME won't be moved during the transition and
- // the tasks are still live, so we take the snapshot at the end of the transition instead.
- if (mTransientLaunches == null) {
- mController.mSnapshotController.onTransactionReady(mType, mTargets);
- }
-
// This is non-null only if display has changes. It handles the visible windows that don't
// need to be participated in the transition.
for (int i = 0; i < mTargetDisplays.size(); ++i) {
@@ -1654,6 +1664,16 @@
reportStartReasonsToLogger();
+ // Take snapshots for closing tasks/activities before the animation finished but after
+ // dispatching onTransitionReady, so IME (if there is) can be captured together and the
+ // time spent on snapshot won't delay the start of animation. Note that if this transition
+ // is transient (mTransientLaunches != null), the snapshot will be captured at the end of
+ // the transition, because IME won't move be moved during the transition and the tasks are
+ // still live.
+ if (mTransientLaunches == null) {
+ mController.mSnapshotController.onTransactionReady(mType, mTargets);
+ }
+
// Since we created root-leash but no longer reference it from core, release it now
info.releaseAnimSurfaces();
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 0d78701..7d2933a 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -705,13 +705,21 @@
try {
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
"Requesting StartTransition: %s", transition);
- ActivityManager.RunningTaskInfo info = null;
+ ActivityManager.RunningTaskInfo startTaskInfo = null;
+ ActivityManager.RunningTaskInfo pipTaskInfo = null;
if (startTask != null) {
- info = new ActivityManager.RunningTaskInfo();
- startTask.fillTaskInfo(info);
+ startTaskInfo = startTask.getTaskInfo();
}
- final TransitionRequestInfo request = new TransitionRequestInfo(
- transition.mType, info, remoteTransition, displayChange, transition.getFlags());
+
+ // set the pip task in the request if provided
+ if (mCollectingTransition.getPipActivity() != null) {
+ pipTaskInfo = mCollectingTransition.getPipActivity().getTask().getTaskInfo();
+ }
+
+ final TransitionRequestInfo request = new TransitionRequestInfo(transition.mType,
+ startTaskInfo, pipTaskInfo, remoteTransition, displayChange,
+ transition.getFlags());
+
transition.mLogger.mRequestTimeNs = SystemClock.elapsedRealtimeNanos();
transition.mLogger.mRequest = request;
mTransitionPlayer.requestStartTransition(transition.getToken(), request);
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 805e7ff..a4d43d8 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -45,6 +45,7 @@
import android.view.WindowInfo;
import android.view.WindowManager.DisplayImePolicy;
import android.view.inputmethod.ImeTracker;
+import android.window.ScreenCapture;
import com.android.internal.policy.KeyInterceptionInfo;
import com.android.server.input.InputManagerService;
@@ -954,4 +955,19 @@
/** Returns the SurfaceControl accessibility services should use for accessibility overlays. */
public abstract SurfaceControl getA11yOverlayLayer(int displayId);
+
+ /**
+ * Captures the entire display specified by the displayId using the args provided. If the args
+ * are null or if the sourceCrop is invalid or null, the entire display bounds will be captured.
+ */
+ public abstract void captureDisplay(int displayId,
+ @Nullable ScreenCapture.CaptureArgs captureArgs,
+ ScreenCapture.ScreenCaptureListener listener);
+
+ /**
+ * Device has a software navigation bar (separate from the status bar) on specific display.
+ *
+ * @param displayId the id of display to check if there is a software navigation bar.
+ */
+ public abstract boolean hasNavigationBar(int displayId);
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 77e1f5b..8fe104c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2493,6 +2493,14 @@
outInsetsState.set(win.getCompatInsetsState(), true /* copySources */);
}
+ // TODO (b/298562855): Remove this after identifying the reason why the frame is empty.
+ if (win.mAttrs.providedInsets != null && win.getFrame().isEmpty()) {
+ Slog.w(TAG, "Empty frame of " + win
+ + " configChanged=" + configChanged
+ + " frame=" + win.getFrame().toShortString()
+ + " attrs=" + attrs);
+ }
+
ProtoLog.v(WM_DEBUG_FOCUS, "Relayout of %s: focusMayChange=%b",
win, focusMayChange);
@@ -8407,6 +8415,17 @@
}
@Override
+ public void captureDisplay(int displayId, @Nullable ScreenCapture.CaptureArgs captureArgs,
+ ScreenCapture.ScreenCaptureListener listener) {
+ WindowManagerService.this.captureDisplay(displayId, captureArgs, listener);
+ }
+
+ @Override
+ public boolean hasNavigationBar(int displayId) {
+ return WindowManagerService.this.hasNavigationBar(displayId);
+ }
+
+ @Override
public void setInputMethodTargetChangeListener(@NonNull ImeTargetChangeListener listener) {
synchronized (mGlobalLock) {
mImeTargetChangeListener = listener;
@@ -8868,6 +8887,11 @@
h.inputConfig |= InputConfig.NOT_FOCUSABLE;
}
+ // Check private trusted overlay flag to set trustedOverlay field of input window handle.
+ if ((privateFlags & PRIVATE_FLAG_TRUSTED_OVERLAY) != 0) {
+ h.inputConfig |= InputConfig.TRUSTED_OVERLAY;
+ }
+
h.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
h.ownerUid = callingUid;
h.ownerPid = callingPid;
@@ -8887,8 +8911,6 @@
}
final SurfaceControl.Transaction t = mTransactionFactory.get();
- // Check private trusted overlay flag to set trustedOverlay field of input window handle.
- h.setTrustedOverlay(t, surface, (privateFlags & PRIVATE_FLAG_TRUSTED_OVERLAY) != 0);
t.setInputWindowInfo(surface, h);
t.apply();
t.close();
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index bfe0553..74a0bafd3 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -1279,6 +1279,7 @@
mLetterboxConfiguration.resetLetterboxBackgroundWallpaperBlurRadiusPx();
mLetterboxConfiguration.resetLetterboxBackgroundWallpaperDarkScrimAlpha();
mLetterboxConfiguration.resetLetterboxHorizontalPositionMultiplier();
+ mLetterboxConfiguration.resetLetterboxVerticalPositionMultiplier();
mLetterboxConfiguration.resetIsHorizontalReachabilityEnabled();
mLetterboxConfiguration.resetIsVerticalReachabilityEnabled();
mLetterboxConfiguration.resetEnabledAutomaticReachabilityInBookMode();
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 822082b..b12cc0b 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -28,7 +28,6 @@
import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
import static android.os.PowerManager.DRAW_WAKE_LOCK;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
-import static android.view.InputWindowHandle.USE_SURFACE_TRUSTED_OVERLAY;
import static android.view.SurfaceControl.Transaction;
import static android.view.SurfaceControl.getGlobalTransaction;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
@@ -99,6 +98,7 @@
import static android.view.WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME;
import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_MULTIPLIER;
import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_OFFSET;
+
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ANIM;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
@@ -1112,9 +1112,7 @@
mInputWindowHandle.setName(getName());
mInputWindowHandle.setPackageName(mAttrs.packageName);
mInputWindowHandle.setLayoutParamsType(mAttrs.type);
- if (!USE_SURFACE_TRUSTED_OVERLAY) {
- mInputWindowHandle.setTrustedOverlay(isWindowTrustedOverlay());
- }
+ mInputWindowHandle.setTrustedOverlay(shouldWindowHandleBeTrusted(s));
if (DEBUG) {
Slog.v(TAG, "Window " + this + " client=" + c.asBinder()
+ " token=" + token + " (" + mAttrs.token + ")" + " params=" + a);
@@ -1195,12 +1193,12 @@
: service.mAtmService.getProcessController(s.mPid, s.mUid);
}
- private boolean isWindowTrustedOverlay() {
+ boolean shouldWindowHandleBeTrusted(Session s) {
return InputMonitor.isTrustedOverlay(mAttrs.type)
|| ((mAttrs.privateFlags & PRIVATE_FLAG_TRUSTED_OVERLAY) != 0
- && mSession.mCanAddInternalSystemWindow)
+ && s.mCanAddInternalSystemWindow)
|| ((mAttrs.privateFlags & PRIVATE_FLAG_SYSTEM_APPLICATION_OVERLAY) != 0
- && mSession.mCanCreateSystemApplicationOverlay);
+ && s.mCanCreateSystemApplicationOverlay);
}
int getTouchOcclusionMode() {
@@ -5194,9 +5192,6 @@
updateFrameRateSelectionPriorityIfNeeded();
updateScaleIfNeeded();
mWinAnimator.prepareSurfaceLocked(getSyncTransaction());
- if (USE_SURFACE_TRUSTED_OVERLAY) {
- getSyncTransaction().setTrustedOverlay(mSurfaceControl, isWindowTrustedOverlay());
- }
}
super.prepareSurfaces();
}
@@ -5949,13 +5944,7 @@
}
boolean isTrustedOverlay() {
- if (USE_SURFACE_TRUSTED_OVERLAY) {
- WindowState parentWindow = getParentWindow();
- return isWindowTrustedOverlay() || (parentWindow != null
- && parentWindow.isWindowTrustedOverlay());
- } else {
- return mInputWindowHandle.isTrustedOverlay();
- }
+ return mInputWindowHandle.isTrustedOverlay();
}
public boolean receiveFocusFromTapOutside() {
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 d22e02e..4203e89 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -50,6 +50,13 @@
maxOccurs="1"/>
<xs:element type="highBrightnessMode" name="highBrightnessMode" minOccurs="0"
maxOccurs="1"/>
+
+ <xs:element name="hdrBrightnessConfig" type="hdrBrightnessConfig"
+ minOccurs="0" maxOccurs="1">
+ <xs:annotation name="nullable"/>
+ <xs:annotation name="final"/>
+ </xs:element>
+
<xs:element type="displayQuirks" name="quirks" minOccurs="0" maxOccurs="1"/>
<xs:element type="autoBrightness" name="autoBrightness" minOccurs="0"
maxOccurs="1"/>
@@ -238,6 +245,31 @@
</xs:all>
</xs:complexType>
+ <!-- brightness config for HDR content -->
+ <xs:complexType name="hdrBrightnessConfig">
+ <!-- lux level from light sensor to screen brightness recommended max value map. -->
+ <xs:element name="brightnessMap" type="nonNegativeFloatToFloatMap">
+ <xs:annotation name="nonnull"/>
+ <xs:annotation name="final"/>
+ </xs:element>
+ <!-- Debounce for brightness increase in millis -->
+ <xs:element name="brightnessIncreaseDebounceMillis" type="xs:nonNegativeInteger">
+ <xs:annotation name="final"/>
+ </xs:element>
+ <!-- Debounce for brightness decrease in millis -->
+ <xs:element name="brightnessDecreaseDebounceMillis" type="xs:nonNegativeInteger">
+ <xs:annotation name="final"/>
+ </xs:element>
+ <!-- Animation time for brightness increase in millis -->
+ <xs:element name="brightnessIncreaseDurationMillis" type="xs:nonNegativeInteger">
+ <xs:annotation name="final"/>
+ </xs:element>
+ <!-- Animation time for brightness decrease in millis -->
+ <xs:element name="brightnessDecreaseDurationMillis" type="xs:nonNegativeInteger">
+ <xs:annotation name="final"/>
+ </xs:element>
+ </xs:complexType>
+
<!-- Maps to PowerManager.THERMAL_STATUS_* values. -->
<xs:simpleType name="thermalStatus">
<xs:restriction base="xs:string">
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index 6364c1f..ebd9b1c 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -101,6 +101,7 @@
method @Nullable public final com.android.server.display.config.DensityMapping getDensityMapping();
method @NonNull public final com.android.server.display.config.Thresholds getDisplayBrightnessChangeThresholds();
method public final com.android.server.display.config.Thresholds getDisplayBrightnessChangeThresholdsIdle();
+ method @Nullable public final com.android.server.display.config.HdrBrightnessConfig getHdrBrightnessConfig();
method public com.android.server.display.config.HighBrightnessMode getHighBrightnessMode();
method public final com.android.server.display.config.SensorDetails getLightSensor();
method public com.android.server.display.config.LuxThrottling getLuxThrottling();
@@ -130,6 +131,7 @@
method public final void setDensityMapping(@Nullable com.android.server.display.config.DensityMapping);
method public final void setDisplayBrightnessChangeThresholds(@NonNull com.android.server.display.config.Thresholds);
method public final void setDisplayBrightnessChangeThresholdsIdle(com.android.server.display.config.Thresholds);
+ method public final void setHdrBrightnessConfig(@Nullable com.android.server.display.config.HdrBrightnessConfig);
method public void setHighBrightnessMode(com.android.server.display.config.HighBrightnessMode);
method public final void setLightSensor(com.android.server.display.config.SensorDetails);
method public void setLuxThrottling(com.android.server.display.config.LuxThrottling);
@@ -168,6 +170,20 @@
method public final void setTimeWindowSecs_all(@NonNull java.math.BigInteger);
}
+ public class HdrBrightnessConfig {
+ ctor public HdrBrightnessConfig();
+ method public final java.math.BigInteger getBrightnessDecreaseDebounceMillis();
+ method public final java.math.BigInteger getBrightnessDecreaseDurationMillis();
+ method public final java.math.BigInteger getBrightnessIncreaseDebounceMillis();
+ method public final java.math.BigInteger getBrightnessIncreaseDurationMillis();
+ method @NonNull public final com.android.server.display.config.NonNegativeFloatToFloatMap getBrightnessMap();
+ method public final void setBrightnessDecreaseDebounceMillis(java.math.BigInteger);
+ method public final void setBrightnessDecreaseDurationMillis(java.math.BigInteger);
+ method public final void setBrightnessIncreaseDebounceMillis(java.math.BigInteger);
+ method public final void setBrightnessIncreaseDurationMillis(java.math.BigInteger);
+ method public final void setBrightnessMap(@NonNull com.android.server.display.config.NonNegativeFloatToFloatMap);
+ }
+
public class HighBrightnessMode {
ctor public HighBrightnessMode();
method @NonNull public final boolean getAllowInLowPowerMode_all();
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
index 4979274..a4adf58 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
@@ -35,11 +35,13 @@
import android.credentials.CreateCredentialRequest;
import android.credentials.CredentialOption;
import android.credentials.CredentialProviderInfo;
+import android.credentials.GetCandidateCredentialsException;
import android.credentials.GetCredentialException;
import android.credentials.GetCredentialRequest;
import android.credentials.IClearCredentialStateCallback;
import android.credentials.ICreateCredentialCallback;
import android.credentials.ICredentialManager;
+import android.credentials.IGetCandidateCredentialsCallback;
import android.credentials.IGetCredentialCallback;
import android.credentials.IPrepareGetCredentialCallback;
import android.credentials.ISetEnabledProvidersCallback;
@@ -301,9 +303,9 @@
ComponentName compName = ComponentName.unflattenFromString(serviceName);
if (compName == null) {
Slog.w(
- TAG,
- "Primary provider component name unflatten from string error: "
- + serviceName);
+ TAG,
+ "Primary provider component name unflatten from string error: "
+ + serviceName);
continue;
}
services.add(compName);
@@ -471,6 +473,59 @@
final class CredentialManagerServiceStub extends ICredentialManager.Stub {
@Override
+ public ICancellationSignal getCandidateCredentials(
+ GetCredentialRequest request,
+ IGetCandidateCredentialsCallback callback,
+ final String callingPackage) {
+ Slog.i(TAG, "starting getCandidateCredentials with callingPackage: "
+ + callingPackage);
+ ICancellationSignal cancelTransport = CancellationSignal.createTransport();
+
+ final int userId = UserHandle.getCallingUserId();
+ final int callingUid = Binder.getCallingUid();
+
+ // New request session, scoped for this request only.
+ final GetCandidateRequestSession session =
+ new GetCandidateRequestSession(
+ getContext(),
+ mSessionManager,
+ mLock,
+ userId,
+ callingUid,
+ callback,
+ request,
+ constructCallingAppInfo(callingPackage, userId, request.getOrigin()),
+ getEnabledProvidersForUser(userId),
+ CancellationSignal.fromTransport(cancelTransport)
+ );
+ addSessionLocked(userId, session);
+
+ List<ProviderSession> providerSessions =
+ initiateProviderSessions(
+ session,
+ request.getCredentialOptions().stream()
+ .map(CredentialOption::getType)
+ .collect(Collectors.toList()));
+
+ if (providerSessions.isEmpty()) {
+ try {
+ callback.onError(
+ GetCandidateCredentialsException.TYPE_NO_CREDENTIAL,
+ "No credentials available on this device.");
+ } catch (RemoteException e) {
+ Slog.i(
+ TAG,
+ "Issue invoking onError on IGetCredentialCallback "
+ + "callback: "
+ + e.getMessage());
+ }
+ }
+
+ invokeProviderSessions(providerSessions);
+ return cancelTransport;
+ }
+
+ @Override
public ICancellationSignal executeGetCredential(
GetCredentialRequest request,
IGetCredentialCallback callback,
@@ -724,7 +779,7 @@
@Override
public void setEnabledProviders(
- List<String> primaryProviders, List<String> providers, int userId,
+ List<String> primaryProviders, List<String> providers, int userId,
ISetEnabledProvidersCallback callback) {
final int callingUid = Binder.getCallingUid();
if (!hasWriteSecureSettingsPermission()) {
@@ -849,9 +904,9 @@
ApiName.GET_CREDENTIAL_PROVIDER_SERVICES,
ApiStatus.SUCCESS, callingUid);
return CredentialProviderInfoFactory
- .getCredentialProviderServices(
- mContext, userId, providerFilter, getEnabledProvidersForUser(userId),
- getPrimaryProvidersForUserId(mContext, userId));
+ .getCredentialProviderServices(
+ mContext, userId, providerFilter, getEnabledProvidersForUser(userId),
+ getPrimaryProvidersForUserId(mContext, userId));
}
@@ -881,13 +936,14 @@
private Set<ComponentName> getEnabledProvidersForUser(int userId) {
final int resolvedUserId = ActivityManager.handleIncomingUser(
- Binder.getCallingPid(), Binder.getCallingUid(),
- userId, false, false,
- "getEnabledProvidersForUser", null);
+ Binder.getCallingPid(), Binder.getCallingUid(),
+ userId, false, false,
+ "getEnabledProvidersForUser", null);
Set<ComponentName> enabledProviders = new HashSet<>();
String directValue = Settings.Secure.getStringForUser(
- mContext.getContentResolver(), Settings.Secure.CREDENTIAL_SERVICE, resolvedUserId);
+ mContext.getContentResolver(), Settings.Secure.CREDENTIAL_SERVICE,
+ resolvedUserId);
if (!TextUtils.isEmpty(directValue)) {
String[] components = directValue.split(":");
diff --git a/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java b/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java
new file mode 100644
index 0000000..6d9b7e8
--- /dev/null
+++ b/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java
@@ -0,0 +1,157 @@
+/*
+ * 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.credentials;
+
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.credentials.CredentialProviderInfo;
+import android.credentials.GetCandidateCredentialsException;
+import android.credentials.GetCandidateCredentialsResponse;
+import android.credentials.GetCredentialRequest;
+import android.credentials.IGetCandidateCredentialsCallback;
+import android.credentials.ui.GetCredentialProviderData;
+import android.credentials.ui.ProviderData;
+import android.credentials.ui.RequestInfo;
+import android.os.CancellationSignal;
+import android.os.RemoteException;
+import android.service.credentials.CallingAppInfo;
+import android.util.Slog;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Central session for a single getCandidateCredentials request. This class listens to the
+ * responses from providers, and updates the provider(s) state.
+ */
+public class GetCandidateRequestSession extends RequestSession<GetCredentialRequest,
+ IGetCandidateCredentialsCallback, GetCandidateCredentialsResponse>
+ implements ProviderSession.ProviderInternalCallback<GetCandidateCredentialsResponse> {
+ private static final String TAG = "GetCandidateRequestSession";
+
+ public GetCandidateRequestSession(
+ Context context, SessionLifetime sessionCallback,
+ Object lock, int userId, int callingUid,
+ IGetCandidateCredentialsCallback callback, GetCredentialRequest request,
+ CallingAppInfo callingAppInfo, Set<ComponentName> enabledProviders,
+ CancellationSignal cancellationSignal) {
+ super(context, sessionCallback, lock, userId, callingUid, request, callback,
+ RequestInfo.TYPE_GET, callingAppInfo, enabledProviders,
+ cancellationSignal, 0L);
+ }
+
+ /**
+ * Creates a new provider session, and adds it list of providers that are contributing to
+ * this session.
+ *
+ * @return the provider session created within this request session, for the given provider
+ * info.
+ */
+ @Override
+ @Nullable
+ public ProviderSession initiateProviderSession(CredentialProviderInfo providerInfo,
+ RemoteCredentialService remoteCredentialService) {
+ ProviderGetSession providerGetCandidateSessions = ProviderGetSession
+ .createNewSession(mContext, mUserId, providerInfo,
+ this, remoteCredentialService);
+ if (providerGetCandidateSessions != null) {
+ Slog.d(TAG, "In startProviderSession - provider session created and "
+ + "being added for: " + providerInfo.getComponentName());
+ mProviders.put(providerGetCandidateSessions.getComponentName().flattenToString(),
+ providerGetCandidateSessions);
+ }
+ return providerGetCandidateSessions;
+ }
+
+ /**
+ * Even though there is no UI involved, this is called when all providers are ready
+ * in our current flow. Eventually can completely separate UI and non UI flows.
+ */
+ @Override
+ protected void launchUiWithProviderData(ArrayList<ProviderData> providerDataList) {
+ if (providerDataList == null || providerDataList.isEmpty()) {
+ respondToClientWithErrorAndFinish(
+ GetCandidateCredentialsException.TYPE_NO_CREDENTIAL,
+ "No credentials found");
+ return;
+ }
+
+ List<GetCredentialProviderData> candidateProviderDataList = new ArrayList<>();
+ for (ProviderData providerData : providerDataList) {
+ candidateProviderDataList.add((GetCredentialProviderData) (providerData));
+ }
+ respondToClientWithResponseAndFinish(new GetCandidateCredentialsResponse(
+ candidateProviderDataList));
+ }
+
+ @Override
+ protected void invokeClientCallbackSuccess(GetCandidateCredentialsResponse response)
+ throws RemoteException {
+ mClientCallback.onResponse(response);
+ }
+
+ @Override
+ protected void invokeClientCallbackError(String errorType, String errorMsg)
+ throws RemoteException {
+ mClientCallback.onError(errorType, errorMsg);
+ }
+
+ @Override
+ public void onFinalErrorReceived(ComponentName componentName, String errorType,
+ String message) {
+ // Not applicable for session without UI
+ }
+
+ @Override
+ public void onUiCancellation(boolean isUserCancellation) {
+ // Not applicable for session without UI
+ }
+
+ @Override
+ public void onUiSelectorInvocationFailure() {
+ // Not applicable for session without UI
+ }
+
+ @Override
+ public void onProviderStatusChanged(ProviderSession.Status status,
+ ComponentName componentName, ProviderSession.CredentialsSource source) {
+ Slog.d(TAG, "in onStatusChanged with status: " + status + ", and source: " + source);
+
+ // For any other status, we check if all providers are done and then invoke UI if needed
+ if (!isAnyProviderPending()) {
+ // If all provider responses have been received, we can either need the UI,
+ // or we need to respond with error. The only other case is the entry being
+ // selected after the UI has been invoked which has a separate code path.
+ if (isUiInvocationNeeded()) {
+ Slog.d(TAG, "in onProviderStatusChanged - isUiInvocationNeeded");
+ getProviderDataAndInitiateUi();
+ } else {
+ respondToClientWithErrorAndFinish(
+ GetCandidateCredentialsException.TYPE_NO_CREDENTIAL,
+ "No credentials available");
+ }
+ }
+ }
+
+ @Override
+ public void onFinalResponseReceived(ComponentName componentName,
+ GetCandidateCredentialsResponse response) {
+ // Not applicable for session without UI
+ }
+}
diff --git a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
index 3c1432a..3eb6718 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
@@ -119,6 +119,42 @@
return null;
}
+ /** Creates a new provider session to be used by the request session. */
+ @Nullable
+ public static ProviderGetSession createNewSession(
+ Context context,
+ @UserIdInt int userId,
+ CredentialProviderInfo providerInfo,
+ GetCandidateRequestSession getRequestSession,
+ RemoteCredentialService remoteCredentialService) {
+ android.credentials.GetCredentialRequest filteredRequest =
+ filterOptions(providerInfo.getCapabilities(),
+ getRequestSession.mClientRequest,
+ providerInfo);
+ if (filteredRequest != null) {
+ Map<String, CredentialOption> beginGetOptionToCredentialOptionMap =
+ new HashMap<>();
+ return new ProviderGetSession(
+ context,
+ providerInfo,
+ getRequestSession,
+ userId,
+ remoteCredentialService,
+ constructQueryPhaseRequest(
+ filteredRequest, getRequestSession.mClientAppInfo,
+ getRequestSession.mClientRequest.alwaysSendAppInfoToProvider(),
+ beginGetOptionToCredentialOptionMap),
+ filteredRequest,
+ getRequestSession.mClientAppInfo,
+ beginGetOptionToCredentialOptionMap,
+ getRequestSession.mHybridService
+ );
+ }
+ Slog.i(TAG, "Unable to create provider session for: "
+ + providerInfo.getComponentName());
+ return null;
+ }
+
private static BeginGetCredentialRequest constructQueryPhaseRequest(
android.credentials.GetCredentialRequest filteredRequest,
CallingAppInfo callingAppInfo,
@@ -192,7 +228,7 @@
public ProviderGetSession(Context context,
CredentialProviderInfo info,
- ProviderInternalCallback<GetCredentialResponse> callbacks,
+ ProviderInternalCallback callbacks,
int userId, RemoteCredentialService remoteCredentialService,
BeginGetCredentialRequest beginGetRequest,
android.credentials.GetCredentialRequest completeGetRequest,
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
index 21b1291..d0ead14 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
@@ -1472,11 +1472,10 @@
synchronized (mLock) {
clear();
new DevicePoliciesReaderWriter().readFromFileLocked();
- reapplyAllPoliciesLocked();
}
}
- private <V> void reapplyAllPoliciesLocked() {
+ <V> void reapplyAllPoliciesLocked() {
for (PolicyKey policy : mGlobalPolicies.keySet()) {
PolicyState<?> policyState = mGlobalPolicies.get(policy);
// Policy definition and value will always be of the same type
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 50dc061..84d1a45 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -3347,6 +3347,11 @@
mOwners.systemReady();
applyManagedSubscriptionsPolicyIfRequired();
break;
+ case SystemService.PHASE_SYSTEM_SERVICES_READY:
+ synchronized (getLockObject()) {
+ mDevicePolicyEngine.reapplyAllPoliciesLocked();
+ }
+ break;
case SystemService.PHASE_ACTIVITY_MANAGER_READY:
synchronized (getLockObject()) {
migrateToProfileOnOrganizationOwnedDeviceIfCompLocked();
@@ -15836,6 +15841,83 @@
}
/**
+ * @param restriction The restriction enforced by admin. It could be any user restriction or
+ * policy like {@link DevicePolicyManager#POLICY_DISABLE_CAMERA},
+ * {@link DevicePolicyManager#POLICY_DISABLE_SCREEN_CAPTURE} and {@link
+ * DevicePolicyManager#POLICY_SUSPEND_PACKAGES}.
+ */
+ private Set<android.app.admin.EnforcingAdmin> getEnforcingAdminsForRestrictionInternal(
+ int userId, @NonNull String restriction) {
+ Objects.requireNonNull(restriction);
+ Set<android.app.admin.EnforcingAdmin> admins = new HashSet<>();
+ // For POLICY_SUSPEND_PACKAGES return PO or DO to keep the behavior same as
+ // before the bug fix for b/192245204.
+ if (DevicePolicyManager.POLICY_SUSPEND_PACKAGES.equals(
+ restriction)) {
+ ComponentName profileOwner = mOwners.getProfileOwnerComponent(userId);
+ if (profileOwner != null) {
+ EnforcingAdmin admin = EnforcingAdmin.createEnterpriseEnforcingAdmin(
+ profileOwner, userId);
+ admins.add(admin.getParcelableAdmin());
+ return admins;
+ }
+ final Pair<Integer, ComponentName> deviceOwner =
+ mOwners.getDeviceOwnerUserIdAndComponent();
+ if (deviceOwner != null && deviceOwner.first == userId) {
+ EnforcingAdmin admin = EnforcingAdmin.createEnterpriseEnforcingAdmin(
+ deviceOwner.second, deviceOwner.first);
+ admins.add(admin.getParcelableAdmin());
+ return admins;
+ }
+ } else {
+ long ident = mInjector.binderClearCallingIdentity();
+ try {
+ PolicyDefinition<Boolean> policyDefinition = getPolicyDefinitionForRestriction(
+ restriction);
+ Boolean value = mDevicePolicyEngine.getResolvedPolicy(policyDefinition, userId);
+ if (value != null && value) {
+ Map<EnforcingAdmin, PolicyValue<Boolean>> globalPolicies =
+ mDevicePolicyEngine.getGlobalPoliciesSetByAdmins(policyDefinition);
+ for (EnforcingAdmin admin : globalPolicies.keySet()) {
+ if (globalPolicies.get(admin) != null
+ && Boolean.TRUE.equals(globalPolicies.get(admin).getValue())) {
+ admins.add(admin.getParcelableAdmin());
+ }
+ }
+
+ Map<EnforcingAdmin, PolicyValue<Boolean>> localPolicies =
+ mDevicePolicyEngine.getLocalPoliciesSetByAdmins(
+ policyDefinition, userId);
+ for (EnforcingAdmin admin : localPolicies.keySet()) {
+ if (localPolicies.get(admin) != null
+ && Boolean.TRUE.equals(localPolicies.get(admin).getValue())) {
+ admins.add(admin.getParcelableAdmin());
+ }
+ }
+ return admins;
+ }
+ } finally {
+ mInjector.binderRestoreCallingIdentity(ident);
+ }
+ }
+ return admins;
+ }
+
+ private static PolicyDefinition<Boolean> getPolicyDefinitionForRestriction(
+ @NonNull String restriction) {
+ Objects.requireNonNull(restriction);
+ if (DevicePolicyManager.POLICY_DISABLE_CAMERA.equals(restriction)) {
+ return PolicyDefinition.getPolicyDefinitionForUserRestriction(
+ UserManager.DISALLOW_CAMERA);
+ } else if (DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE.equals(restriction)) {
+ return PolicyDefinition.SCREEN_CAPTURE_DISABLED;
+ } else {
+ return PolicyDefinition.getPolicyDefinitionForUserRestriction(restriction);
+ }
+ }
+
+
+ /**
* Excludes restrictions imposed by UserManager.
*/
private List<UserManager.EnforcingUser> getDevicePolicySources(
@@ -15873,6 +15955,13 @@
return getEnforcingAdminAndUserDetailsInternal(userId, restriction);
}
+ @Override
+ public List<android.app.admin.EnforcingAdmin> getEnforcingAdminsForRestriction(
+ int userId, String restriction) {
+ Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()));
+ return new ArrayList<>(getEnforcingAdminsForRestrictionInternal(userId, restriction));
+ }
+
/**
* @param restriction The restriction enforced by admin. It could be any user restriction or
* policy like {@link DevicePolicyManager#POLICY_DISABLE_CAMERA} and
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java
index 5243d14..0066422 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java
@@ -228,7 +228,8 @@
return new android.app.admin.EnforcingAdmin(
mPackageName,
authority,
- UserHandle.of(mUserId));
+ UserHandle.of(mUserId),
+ mComponentName);
}
/**
diff --git a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
index 8279600..1e052c0 100644
--- a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
@@ -1753,10 +1753,19 @@
}
override fun addOnPermissionsChangeListener(listener: IOnPermissionsChangeListener) {
+ context.enforceCallingOrSelfPermission(
+ Manifest.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS, "addOnPermissionsChangeListener"
+ )
+
onPermissionsChangeListeners.addListener(listener)
}
override fun removeOnPermissionsChangeListener(listener: IOnPermissionsChangeListener) {
+ context.enforceCallingOrSelfPermission(
+ Manifest.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS,
+ "removeOnPermissionsChangeListener"
+ )
+
onPermissionsChangeListeners.removeListener(listener)
}
diff --git a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyTest.kt b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyTest.kt
index 3ab2547..3cf57a3 100644
--- a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyTest.kt
+++ b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyTest.kt
@@ -1445,6 +1445,192 @@
.isEqualTo(expectedNewFlags)
}
+ @Test
+ fun testOnPackageAdded_runtimeExistingImplicitPermissions_sourceFlagsNotInherited() {
+ val oldImplicitPermissionFlags = PermissionFlags.USER_FIXED
+ testInheritImplicitPermissionStates(
+ implicitPermissionFlags = oldImplicitPermissionFlags,
+ isNewInstallAndNewPermission = false
+ )
+
+ val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
+ val expectedNewFlags = oldImplicitPermissionFlags or PermissionFlags.IMPLICIT_GRANTED or
+ PermissionFlags.APP_OP_REVOKED
+ assertWithMessage(
+ "After onPackageAdded() is called for a package that requests a permission that is" +
+ " implicit, existing and runtime, it should not inherit the runtime flags from" +
+ " the source permission. Hence the actual permission flags $actualFlags should" +
+ " match the expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ @Test
+ fun testOnPackageAdded_nonRuntimeNewImplicitPermissions_sourceFlagsNotInherited() {
+ testInheritImplicitPermissionStates(
+ implicitPermissionProtectionLevel = PermissionInfo.PROTECTION_NORMAL
+ )
+
+ val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
+ val expectedNewFlags = PermissionFlags.INSTALL_GRANTED
+ assertWithMessage(
+ "After onPackageAdded() is called for a package that requests a permission that is" +
+ " implicit, new and non-runtime, it should not inherit the runtime flags from" +
+ " the source permission. Hence the actual permission flags $actualFlags should" +
+ " match the expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ @Test
+ fun testOnPackageAdded_runtimeNewImplicitPermissions_sourceFlagsInherited() {
+ val sourceRuntimeFlags = PermissionFlags.RUNTIME_GRANTED or PermissionFlags.USER_SET
+ testInheritImplicitPermissionStates(sourceRuntimeFlags = sourceRuntimeFlags)
+
+ val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
+ val expectedNewFlags = sourceRuntimeFlags or PermissionFlags.IMPLICIT_GRANTED or
+ PermissionFlags.IMPLICIT
+ assertWithMessage(
+ "After onPackageAdded() is called for a package that requests a permission that is" +
+ " implicit, new and runtime, it should inherit the runtime flags from" +
+ " the source permission. Hence the actual permission flags $actualFlags should" +
+ " match the expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ @Test
+ fun testOnPackageAdded_grantingNewFromRevokeImplicitPermissions_onlySourceFlagsInherited() {
+ val sourceRuntimeFlags = PermissionFlags.RUNTIME_GRANTED or PermissionFlags.USER_SET
+ testInheritImplicitPermissionStates(
+ implicitPermissionFlags = PermissionFlags.POLICY_FIXED,
+ sourceRuntimeFlags = sourceRuntimeFlags,
+ isAnySourcePermissionNonRuntime = false
+ )
+
+ val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
+ val expectedNewFlags = sourceRuntimeFlags or PermissionFlags.IMPLICIT
+ assertWithMessage(
+ "After onPackageAdded() is called for a package that requests a permission that is" +
+ " implicit, existing, runtime and revoked, it should only inherit runtime flags" +
+ " from source permission. Hence the actual permission flags $actualFlags should" +
+ " match the expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ /**
+ * If it's a media implicit permission (one of RETAIN_IMPLICIT_FLAGS_PERMISSIONS), we want to
+ * remove the IMPLICIT flag so that they will be granted when they are no longer implicit.
+ * (instead of revoking it)
+ */
+ @Test
+ fun testOnPackageAdded_mediaImplicitPermissions_getsImplicitFlagRemoved() {
+ val sourceRuntimeFlags = PermissionFlags.RUNTIME_GRANTED or PermissionFlags.USER_SET
+ testInheritImplicitPermissionStates(
+ implicitPermissionName = PERMISSION_ACCESS_MEDIA_LOCATION,
+ sourceRuntimeFlags = sourceRuntimeFlags
+ )
+
+ val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_ACCESS_MEDIA_LOCATION)
+ val expectedNewFlags = sourceRuntimeFlags or PermissionFlags.IMPLICIT_GRANTED
+ assertWithMessage(
+ "After onPackageAdded() is called for a package that requests a media permission that" +
+ " is implicit, new and runtime, it should inherit the runtime flags from" +
+ " the source permission and have the IMPLICIT flag removed. Hence the actual" +
+ " permission flags $actualFlags should match the expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ private fun testInheritImplicitPermissionStates(
+ implicitPermissionName: String = PERMISSION_NAME_0,
+ implicitPermissionFlags: Int = 0,
+ implicitPermissionProtectionLevel: Int = PermissionInfo.PROTECTION_DANGEROUS,
+ sourceRuntimeFlags: Int = PermissionFlags.RUNTIME_GRANTED or PermissionFlags.USER_SET,
+ isAnySourcePermissionNonRuntime: Boolean = true,
+ isNewInstallAndNewPermission: Boolean = true
+ ) {
+ val implicitPermission = mockParsedPermission(
+ implicitPermissionName,
+ PACKAGE_NAME_0,
+ protectionLevel = implicitPermissionProtectionLevel,
+ )
+ // For source from non-runtime in order to grant by implicit
+ val sourcePermission1 = mockParsedPermission(
+ PERMISSION_NAME_1,
+ PACKAGE_NAME_0,
+ protectionLevel = if (isAnySourcePermissionNonRuntime) {
+ PermissionInfo.PROTECTION_NORMAL
+ } else {
+ PermissionInfo.PROTECTION_DANGEROUS
+ }
+ )
+ // For inheriting runtime flags
+ val sourcePermission2 = mockParsedPermission(
+ PERMISSION_NAME_2,
+ PACKAGE_NAME_0,
+ protectionLevel = PermissionInfo.PROTECTION_DANGEROUS,
+ )
+ val permissionOwnerPackageState = mockPackageState(
+ APP_ID_0,
+ mockAndroidPackage(
+ PACKAGE_NAME_0,
+ permissions = listOf(implicitPermission, sourcePermission1, sourcePermission2)
+ )
+ )
+ val installedPackageState = mockPackageState(
+ APP_ID_1,
+ mockAndroidPackage(
+ PACKAGE_NAME_1,
+ requestedPermissions = setOf(
+ implicitPermissionName,
+ PERMISSION_NAME_1,
+ PERMISSION_NAME_2
+ ),
+ implicitPermissions = setOf(implicitPermissionName)
+ )
+ )
+ oldState.mutateExternalState().setImplicitToSourcePermissions(
+ MutableIndexedMap<String, IndexedListSet<String>>().apply {
+ put(implicitPermissionName, MutableIndexedListSet<String>().apply {
+ add(PERMISSION_NAME_1)
+ add(PERMISSION_NAME_2)
+ })
+ }
+ )
+ addPackageState(permissionOwnerPackageState)
+ addPermission(implicitPermission)
+ addPermission(sourcePermission1)
+ addPermission(sourcePermission2)
+ if (!isNewInstallAndNewPermission) {
+ addPackageState(installedPackageState)
+ setPermissionFlags(APP_ID_1, USER_ID_0, implicitPermissionName, implicitPermissionFlags)
+ }
+ setPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_2, sourceRuntimeFlags)
+
+ mutateState {
+ if (isNewInstallAndNewPermission) {
+ addPackageState(installedPackageState)
+ setPermissionFlags(
+ APP_ID_1,
+ USER_ID_0,
+ implicitPermissionName,
+ implicitPermissionFlags,
+ newState
+ )
+ }
+ with(appIdPermissionPolicy) {
+ onPackageAdded(installedPackageState)
+ }
+ }
+ }
+
/**
* Setup simple package states for testing evaluatePermissionState().
* permissionOwnerPackageState is definer of permissionName with APP_ID_0.
@@ -1734,6 +1920,7 @@
private const val PERMISSION_NAME_0 = "permissionName0"
private const val PERMISSION_NAME_1 = "permissionName1"
+ private const val PERMISSION_NAME_2 = "permissionName2"
private const val PERMISSION_READ_EXTERNAL_STORAGE =
Manifest.permission.READ_EXTERNAL_STORAGE
private const val PERMISSION_POST_NOTIFICATIONS =
@@ -1742,6 +1929,8 @@
Manifest.permission.BLUETOOTH_CONNECT
private const val PERMISSION_ACCESS_BACKGROUND_LOCATION =
Manifest.permission.ACCESS_BACKGROUND_LOCATION
+ private const val PERMISSION_ACCESS_MEDIA_LOCATION =
+ Manifest.permission.ACCESS_MEDIA_LOCATION
private const val USER_ID_0 = 0
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
index 82d00a6..7374901 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
@@ -44,6 +44,7 @@
import androidx.test.filters.SmallTest;
import com.android.internal.R;
+import com.android.server.display.config.HdrBrightnessData;
import com.android.server.display.config.ThermalStatus;
import org.junit.Before;
@@ -477,6 +478,23 @@
mDisplayDeviceConfig.getHighAmbientBrightnessThresholds(), ZERO_DELTA);
}
+ @Test
+ public void testHdrBrightnessDataFromDisplayConfig() throws IOException {
+ setupDisplayDeviceConfigFromDisplayConfigFile();
+
+ HdrBrightnessData data = mDisplayDeviceConfig.getHdrBrightnessData();
+
+ assertNotNull(data);
+ assertEquals(2, data.mMaxBrightnessLimits.size());
+ assertEquals(13000, data.mBrightnessDecreaseDebounceMillis);
+ assertEquals(10000, data.mBrightnessDecreaseDurationMillis);
+ assertEquals(1000, data.mBrightnessIncreaseDebounceMillis);
+ assertEquals(11000, data.mBrightnessIncreaseDurationMillis);
+
+ assertEquals(0.3f, data.mMaxBrightnessLimits.get(500f), SMALL_DELTA);
+ assertEquals(0.6f, data.mMaxBrightnessLimits.get(1200f), SMALL_DELTA);
+ }
+
private void verifyConfigValuesFromConfigResource() {
assertNull(mDisplayDeviceConfig.getName());
assertArrayEquals(mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsNits(), new
@@ -694,6 +712,25 @@
+ "</proxSensor>\n";
}
+ private String getHdrBrightnessConfig() {
+ return "<hdrBrightnessConfig>\n"
+ + " <brightnessMap>\n"
+ + " <point>\n"
+ + " <first>500</first>\n"
+ + " <second>0.3</second>\n"
+ + " </point>\n"
+ + " <point>\n"
+ + " <first>1200</first>\n"
+ + " <second>0.6</second>\n"
+ + " </point>\n"
+ + " </brightnessMap>\n"
+ + " <brightnessIncreaseDebounceMillis>1000</brightnessIncreaseDebounceMillis>\n"
+ + " <brightnessIncreaseDurationMillis>11000</brightnessIncreaseDurationMillis>\n"
+ + " <brightnessDecreaseDebounceMillis>13000</brightnessDecreaseDebounceMillis>\n"
+ + " <brightnessDecreaseDurationMillis>10000</brightnessDecreaseDurationMillis>\n"
+ + "</hdrBrightnessConfig>";
+ }
+
private String getContent() {
return getContent(getValidLuxThrottling(), getValidProxSensor());
}
@@ -784,6 +821,7 @@
+ "</point>\n"
+ "</sdrHdrRatioMap>\n"
+ "</highBrightnessMode>\n"
+ + getHdrBrightnessConfig()
+ brightnessCapConfig
+ "<lightSensor>\n"
+ "<type>test_light_sensor</type>\n"
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java
index 11ff42b..89e28cb8 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java
@@ -70,7 +70,7 @@
import com.android.server.display.brightness.BrightnessEvent;
import com.android.server.display.brightness.clamper.HdrClamper;
import com.android.server.display.color.ColorDisplayService;
-import com.android.server.display.feature.DeviceConfigParameterProvider;
+import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.display.layout.Layout;
import com.android.server.display.whitebalance.DisplayWhiteBalanceController;
import com.android.server.policy.WindowManagerPolicy;
@@ -100,6 +100,7 @@
private static final int SECOND_FOLLOWER_DISPLAY_ID = FOLLOWER_DISPLAY_ID + 1;
private static final String SECOND_FOLLOWER_UNIQUE_DISPLAY_ID = "unique_id_789";
private static final float PROX_SENSOR_MAX_RANGE = 5;
+ private static final float BRIGHTNESS_RAMP_RATE_MINIMUM = 0.0f;
private static final float BRIGHTNESS_RAMP_RATE_FAST_DECREASE = 0.3f;
private static final float BRIGHTNESS_RAMP_RATE_FAST_INCREASE = 0.4f;
private static final float BRIGHTNESS_RAMP_RATE_SLOW_DECREASE = 0.1f;
@@ -698,6 +699,80 @@
}
@Test
+ public void testDisplayBrightnessHdr_SkipAnimationOnHdrAppearance() {
+ Settings.System.putInt(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_MODE,
+ Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
+ final float sdrBrightness = 0.1f;
+ final float hdrBrightness = 0.3f;
+ when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
+ when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
+ when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness(
+ any(BrightnessEvent.class))).thenReturn(sdrBrightness);
+ when(mHolder.hdrClamper.getMaxBrightness()).thenReturn(1.0f);
+
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1); // Run updatePowerState
+
+ verify(mHolder.animator).animateTo(eq(sdrBrightness), eq(sdrBrightness),
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+
+ when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(sdrBrightness);
+ when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(sdrBrightness);
+
+ when(mHolder.hbmController.getHighBrightnessMode()).thenReturn(
+ BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR);
+ when(mHolder.hbmController.getHdrBrightnessValue()).thenReturn(hdrBrightness);
+ clearInvocations(mHolder.animator);
+
+ mHolder.dpc.updateBrightness();
+ advanceTime(1); // Run updatePowerState
+
+ verify(mHolder.animator).animateTo(eq(hdrBrightness), eq(sdrBrightness),
+ eq(BRIGHTNESS_RAMP_RATE_MINIMUM));
+ }
+
+ @Test
+ public void testDisplayBrightnessHdr_SkipAnimationOnHdrRemoval() {
+ Settings.System.putInt(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_MODE,
+ Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
+ final float sdrBrightness = 0.1f;
+ final float hdrBrightness = 0.3f;
+ when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
+ when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
+ when(mHolder.automaticBrightnessController.isInIdleMode()).thenReturn(true);
+ when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness(
+ any(BrightnessEvent.class))).thenReturn(sdrBrightness);
+ when(mHolder.hdrClamper.getMaxBrightness()).thenReturn(1.0f);
+
+ when(mHolder.hbmController.getHighBrightnessMode()).thenReturn(
+ BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR);
+ when(mHolder.hbmController.getHdrBrightnessValue()).thenReturn(hdrBrightness);
+
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1); // Run updatePowerState
+
+ verify(mHolder.animator).animateTo(eq(hdrBrightness), eq(sdrBrightness),
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+
+ when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(hdrBrightness);
+ when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(sdrBrightness);
+ when(mHolder.hbmController.getHighBrightnessMode()).thenReturn(
+ BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF);
+
+ clearInvocations(mHolder.animator);
+
+ mHolder.dpc.updateBrightness();
+ advanceTime(1); // Run updatePowerState
+
+ verify(mHolder.animator).animateTo(eq(sdrBrightness), eq(sdrBrightness),
+ eq(BRIGHTNESS_RAMP_RATE_MINIMUM));
+ }
+
+ @Test
public void testDoesNotSetScreenStateForNonDefaultDisplayUntilBootCompleted() {
// We should still set screen state for the default display
DisplayPowerRequest dpr = new DisplayPowerRequest();
@@ -1185,6 +1260,8 @@
DisplayPowerRequest dpr = new DisplayPowerRequest();
when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
+ when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(.2f);
+ when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(.1f);
when(mHolder.hbmController.getHighBrightnessMode()).thenReturn(
BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR);
when(mHolder.hbmController.getHdrBrightnessValue()).thenReturn(PowerManager.BRIGHTNESS_MAX);
@@ -1294,15 +1371,14 @@
mock(ScreenOffBrightnessSensorController.class);
final HighBrightnessModeController hbmController = mock(HighBrightnessModeController.class);
final HdrClamper hdrClamper = mock(HdrClamper.class);
- final DeviceConfigParameterProvider deviceConfigParameterProvider =
- mock(DeviceConfigParameterProvider.class);
+ final DisplayManagerFlags flags = mock(DisplayManagerFlags.class);
when(hbmController.getCurrentBrightnessMax()).thenReturn(PowerManager.BRIGHTNESS_MAX);
TestInjector injector = spy(new TestInjector(displayPowerState, animator,
automaticBrightnessController, wakelockController, brightnessMappingStrategy,
hysteresisLevels, screenOffBrightnessSensorController, hbmController, hdrClamper,
- deviceConfigParameterProvider));
+ flags));
final LogicalDisplay display = mock(LogicalDisplay.class);
final DisplayDevice device = mock(DisplayDevice.class);
@@ -1316,7 +1392,7 @@
mContext, injector, mDisplayPowerCallbacksMock, mHandler,
mSensorManagerMock, mDisplayBlankerMock, display,
mBrightnessTrackerMock, brightnessSetting, () -> {},
- hbmMetadata, /* bootCompleted= */ false);
+ hbmMetadata, /* bootCompleted= */ false, flags);
return new DisplayPowerControllerHolder(dpc, display, displayPowerState, brightnessSetting,
animator, automaticBrightnessController, wakelockController,
@@ -1383,7 +1459,7 @@
private final HdrClamper mHdrClamper;
- private final DeviceConfigParameterProvider mDeviceConfigParameterProvider;
+ private final DisplayManagerFlags mFlags;
TestInjector(DisplayPowerState dps, DualRampAnimator<DisplayPowerState> animator,
AutomaticBrightnessController automaticBrightnessController,
@@ -1393,7 +1469,7 @@
ScreenOffBrightnessSensorController screenOffBrightnessSensorController,
HighBrightnessModeController highBrightnessModeController,
HdrClamper hdrClamper,
- DeviceConfigParameterProvider deviceConfigParameterProvider) {
+ DisplayManagerFlags flags) {
mDisplayPowerState = dps;
mAnimator = animator;
mAutomaticBrightnessController = automaticBrightnessController;
@@ -1403,7 +1479,7 @@
mScreenOffBrightnessSensorController = screenOffBrightnessSensorController;
mHighBrightnessModeController = highBrightnessModeController;
mHdrClamper = hdrClamper;
- mDeviceConfigParameterProvider = deviceConfigParameterProvider;
+ mFlags = flags;
}
@Override
@@ -1512,9 +1588,10 @@
@Override
BrightnessRangeController getBrightnessRangeController(
HighBrightnessModeController hbmController, Runnable modeChangeCallback,
- DisplayDeviceConfig displayDeviceConfig, Handler handler) {
+ DisplayDeviceConfig displayDeviceConfig, Handler handler,
+ DisplayManagerFlags flags) {
return new BrightnessRangeController(hbmController, modeChangeCallback,
- displayDeviceConfig, mHdrClamper, mDeviceConfigParameterProvider);
+ displayDeviceConfig, mHdrClamper, mFlags);
}
@Override
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
index e0c0ae2..971ece3 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -43,6 +43,7 @@
import android.hardware.Sensor;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
+import android.hardware.display.BrightnessInfo;
import android.hardware.display.DisplayManagerInternal.DisplayPowerCallbacks;
import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
import android.os.Handler;
@@ -69,6 +70,7 @@
import com.android.server.display.RampAnimator.DualRampAnimator;
import com.android.server.display.brightness.BrightnessEvent;
import com.android.server.display.color.ColorDisplayService;
+import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.display.layout.Layout;
import com.android.server.display.whitebalance.DisplayWhiteBalanceController;
import com.android.server.policy.WindowManagerPolicy;
@@ -97,6 +99,7 @@
private static final int SECOND_FOLLOWER_DISPLAY_ID = FOLLOWER_DISPLAY_ID + 1;
private static final String SECOND_FOLLOWER_UNIQUE_DISPLAY_ID = "unique_id_789";
private static final float PROX_SENSOR_MAX_RANGE = 5;
+ private static final float BRIGHTNESS_RAMP_RATE_MINIMUM = 0.0f;
private static final float BRIGHTNESS_RAMP_RATE_FAST_DECREASE = 0.3f;
private static final float BRIGHTNESS_RAMP_RATE_FAST_INCREASE = 0.4f;
private static final float BRIGHTNESS_RAMP_RATE_SLOW_DECREASE = 0.1f;
@@ -1184,6 +1187,77 @@
verify(mHolder.displayPowerState, times(1)).stop();
}
+ @Test
+ public void testDisplayBrightnessHdr_SkipAnimationOnHdrAppearance() {
+ Settings.System.putInt(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_MODE,
+ Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
+ final float sdrBrightness = 0.1f;
+ final float hdrBrightness = 0.3f;
+ when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
+ when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
+ when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness(
+ any(BrightnessEvent.class))).thenReturn(sdrBrightness);
+
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1); // Run updatePowerState
+
+ verify(mHolder.animator).animateTo(eq(sdrBrightness), eq(sdrBrightness),
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+
+ when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(sdrBrightness);
+ when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(sdrBrightness);
+
+ when(mHolder.hbmController.getHighBrightnessMode()).thenReturn(
+ BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR);
+ when(mHolder.hbmController.getHdrBrightnessValue()).thenReturn(hdrBrightness);
+ clearInvocations(mHolder.animator);
+
+ mHolder.dpc.updateBrightness();
+ advanceTime(1); // Run updatePowerState
+
+ verify(mHolder.animator).animateTo(eq(hdrBrightness), eq(sdrBrightness),
+ eq(BRIGHTNESS_RAMP_RATE_MINIMUM));
+ }
+
+ @Test
+ public void testDisplayBrightnessHdr_SkipAnimationOnHdrRemoval() {
+ Settings.System.putInt(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_MODE,
+ Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
+ final float sdrBrightness = 0.1f;
+ final float hdrBrightness = 0.3f;
+ when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
+ when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
+ when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness(
+ any(BrightnessEvent.class))).thenReturn(sdrBrightness);
+
+ when(mHolder.hbmController.getHighBrightnessMode()).thenReturn(
+ BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR);
+ when(mHolder.hbmController.getHdrBrightnessValue()).thenReturn(hdrBrightness);
+
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1); // Run updatePowerState
+
+ verify(mHolder.animator).animateTo(eq(hdrBrightness), eq(sdrBrightness),
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+
+ when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(hdrBrightness);
+ when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(sdrBrightness);
+ when(mHolder.hbmController.getHighBrightnessMode()).thenReturn(
+ BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF);
+
+ clearInvocations(mHolder.animator);
+
+ mHolder.dpc.updateBrightness();
+ advanceTime(1); // Run updatePowerState
+
+ verify(mHolder.animator).animateTo(eq(sdrBrightness), eq(sdrBrightness),
+ eq(BRIGHTNESS_RAMP_RATE_MINIMUM));
+ }
+
private void advanceTime(long timeMs) {
mClock.fastForward(timeMs);
mTestLooper.dispatchAll();
@@ -1282,6 +1356,7 @@
final HighBrightnessModeMetadata hbmMetadata = mock(HighBrightnessModeMetadata.class);
final BrightnessSetting brightnessSetting = mock(BrightnessSetting.class);
final DisplayDeviceConfig config = mock(DisplayDeviceConfig.class);
+ final DisplayManagerFlags flags = mock(DisplayManagerFlags.class);
setUpDisplay(displayId, uniqueId, display, device, config, isEnabled);
@@ -1289,7 +1364,7 @@
mContext, injector, mDisplayPowerCallbacksMock, mHandler,
mSensorManagerMock, mDisplayBlankerMock, display,
mBrightnessTrackerMock, brightnessSetting, () -> {},
- hbmMetadata, /* bootCompleted= */ false);
+ hbmMetadata, /* bootCompleted= */ false, flags);
return new DisplayPowerControllerHolder(dpc, display, displayPowerState, brightnessSetting,
animator, automaticBrightnessController, screenOffBrightnessSensorController,
diff --git a/services/tests/displayservicetests/src/com/android/server/display/RampAnimatorTest.java b/services/tests/displayservicetests/src/com/android/server/display/RampAnimatorTest.java
new file mode 100644
index 0000000..2820da7
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/RampAnimatorTest.java
@@ -0,0 +1,62 @@
+/*
+ * 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.display;
+
+import static org.junit.Assert.assertEquals;
+
+import android.util.FloatProperty;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+
+@SmallTest
+public class RampAnimatorTest {
+
+ private RampAnimator<TestObject> mRampAnimator;
+
+ private final TestObject mTestObject = new TestObject();
+
+ private final FloatProperty<TestObject> mTestProperty = new FloatProperty<>("mValue") {
+ @Override
+ public void setValue(TestObject object, float value) {
+ object.mValue = value;
+ }
+
+ @Override
+ public Float get(TestObject object) {
+ return object.mValue;
+ }
+ };
+
+ @Before
+ public void setUp() {
+ mRampAnimator = new RampAnimator<>(mTestObject, mTestProperty);
+ }
+
+ @Test
+ public void testInitialValueUsedInLastAnimationStep() {
+ mRampAnimator.setAnimationTarget(0.67f, 0.1f);
+
+ assertEquals(0.67f, mTestObject.mValue, 0);
+ }
+
+ private static class TestObject {
+ private float mValue;
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
index 525bfd7..5b1508b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
@@ -16,7 +16,6 @@
package com.android.server;
import static androidx.test.InstrumentationRegistry.getContext;
-
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -42,7 +41,6 @@
import static com.android.server.DeviceIdleController.STATE_SENSING;
import static com.android.server.DeviceIdleController.lightStateToString;
import static com.android.server.DeviceIdleController.stateToString;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -2418,7 +2416,7 @@
}
@Test
- public void testModeManager_NoModeManagerLocalService_AddListenerNotCalled() {
+ public void testModeManager_NoModeManagerLocalService_AddQuickDozeListenerNotCalled() {
mConstants.USE_MODE_MANAGER = true;
doReturn(null)
.when(() -> LocalServices.getService(WearModeManagerInternal.class));
@@ -2430,6 +2428,33 @@
}
@Test
+ public void testModeManager_NoModeManagerLocalService_AddOffBodyListenerNotCalled() {
+ mConstants.USE_MODE_MANAGER = true;
+ doReturn(null)
+ .when(() -> LocalServices.getService(WearModeManagerInternal.class));
+ cleanupDeviceIdleController();
+ setupDeviceIdleController();
+ verify(mWearModeManagerInternal, never()).addActiveStateChangeListener(
+ eq(WearModeManagerInternal.OFFBODY_STATE_ID), any(),
+ eq(mDeviceIdleController.mModeManagerOffBodyStateConsumer));
+ }
+
+ @Test
+ public void testModeManager_USEMODEMANAGERIsFalse_AddListenerNotCalled() {
+ mConstants.USE_MODE_MANAGER = false;
+ doReturn(new Object())
+ .when(() -> LocalServices.getService(WearModeManagerInternal.class));
+ cleanupDeviceIdleController();
+ setupDeviceIdleController();
+ verify(mWearModeManagerInternal, never()).addActiveStateChangeListener(
+ eq(WearModeManagerInternal.OFFBODY_STATE_ID), any(),
+ eq(mDeviceIdleController.mModeManagerOffBodyStateConsumer));
+ verify(mWearModeManagerInternal, never()).addActiveStateChangeListener(
+ eq(WearModeManagerInternal.QUICK_DOZE_REQUEST_IDENTIFIER), any(),
+ eq(mDeviceIdleController.mModeManagerQuickDozeRequestConsumer));
+ }
+
+ @Test
public void testModeManager_NoBatterySaver_QuickDoze() {
mConstants.USE_MODE_MANAGER = true;
PowerSaveState powerSaveState = new PowerSaveState.Builder().setBatterySaverEnabled(
@@ -2469,6 +2494,102 @@
assertTrue(mDeviceIdleController.isQuickDozeEnabled());
}
+ @Test
+ public void testModeManager_QuickDozeRequestedBatterySaverEnabledOnBody_QuickDozeEnabled() {
+ mConstants.USE_MODE_MANAGER = true;
+ PowerSaveState powerSaveState = new PowerSaveState.Builder().setBatterySaverEnabled(
+ true).build();
+ when(mPowerManagerInternal.getLowPowerState(anyInt()))
+ .thenReturn(powerSaveState);
+ cleanupDeviceIdleController();
+ setupDeviceIdleController();
+
+ mDeviceIdleController.mModeManagerOffBodyStateConsumer.accept(false);
+ mDeviceIdleController.mModeManagerQuickDozeRequestConsumer.accept(true);
+
+ assertTrue(mDeviceIdleController.isQuickDozeEnabled());
+ }
+
+ @Test
+ public void testModeManager_QuickDozeRequestedBatterySaverEnabledOffBody_QuickDozeEnabled() {
+ mConstants.USE_MODE_MANAGER = true;
+ PowerSaveState powerSaveState = new PowerSaveState.Builder().setBatterySaverEnabled(
+ true).build();
+ when(mPowerManagerInternal.getLowPowerState(anyInt()))
+ .thenReturn(powerSaveState);
+ cleanupDeviceIdleController();
+ setupDeviceIdleController();
+
+ mDeviceIdleController.mModeManagerOffBodyStateConsumer.accept(true);
+ mDeviceIdleController.mModeManagerQuickDozeRequestConsumer.accept(true);
+
+ assertTrue(mDeviceIdleController.isQuickDozeEnabled());
+ }
+
+ @Test
+ public void testModeManager_QuickDozeRequestedBatterySaverDisabledOnBody_QuickDozeEnabled() {
+ mConstants.USE_MODE_MANAGER = true;
+ PowerSaveState powerSaveState = new PowerSaveState.Builder().setBatterySaverEnabled(
+ false).build();
+ when(mPowerManagerInternal.getLowPowerState(anyInt()))
+ .thenReturn(powerSaveState);
+ cleanupDeviceIdleController();
+ setupDeviceIdleController();
+
+ mDeviceIdleController.mModeManagerOffBodyStateConsumer.accept(false);
+ mDeviceIdleController.mModeManagerQuickDozeRequestConsumer.accept(true);
+
+ assertTrue(mDeviceIdleController.isQuickDozeEnabled());
+ }
+
+ @Test
+ public void testModeManager_QuickDozeRequestedBatterySaverDisabledOffBody_QuickDozeEnabled() {
+ mConstants.USE_MODE_MANAGER = true;
+ PowerSaveState powerSaveState = new PowerSaveState.Builder().setBatterySaverEnabled(
+ false).build();
+ when(mPowerManagerInternal.getLowPowerState(anyInt()))
+ .thenReturn(powerSaveState);
+ cleanupDeviceIdleController();
+ setupDeviceIdleController();
+
+ mDeviceIdleController.mModeManagerOffBodyStateConsumer.accept(true);
+ mDeviceIdleController.mModeManagerQuickDozeRequestConsumer.accept(true);
+
+ assertTrue(mDeviceIdleController.isQuickDozeEnabled());
+ }
+
+ @Test
+ public void testModeManager_QuickDozeNotRequestedBatterySaverEnabledOnBody_QuickDozeEnabled() {
+ mConstants.USE_MODE_MANAGER = true;
+ PowerSaveState powerSaveState = new PowerSaveState.Builder().setBatterySaverEnabled(
+ true).build();
+ when(mPowerManagerInternal.getLowPowerState(anyInt()))
+ .thenReturn(powerSaveState);
+ cleanupDeviceIdleController();
+ setupDeviceIdleController();
+
+ mDeviceIdleController.mModeManagerOffBodyStateConsumer.accept(false);
+ mDeviceIdleController.mModeManagerQuickDozeRequestConsumer.accept(false);
+
+ assertTrue(mDeviceIdleController.isQuickDozeEnabled());
+ }
+
+ @Test
+ public void testModeManager_QuickDozeNotRequestedBatterySaverEnabledOffBody_QuickDozeEnabled() {
+ mConstants.USE_MODE_MANAGER = true;
+ PowerSaveState powerSaveState = new PowerSaveState.Builder().setBatterySaverEnabled(
+ true).build();
+ when(mPowerManagerInternal.getLowPowerState(anyInt()))
+ .thenReturn(powerSaveState);
+ cleanupDeviceIdleController();
+ setupDeviceIdleController();
+
+ mDeviceIdleController.mModeManagerOffBodyStateConsumer.accept(true);
+ mDeviceIdleController.mModeManagerQuickDozeRequestConsumer.accept(false);
+
+ assertTrue(mDeviceIdleController.isQuickDozeEnabled());
+ }
+
private void enterDeepState(int state) {
switch (state) {
case STATE_ACTIVE:
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java
index 5555c19..b97fd4b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java
@@ -17,6 +17,7 @@
package com.android.server.pm;
import static android.content.Intent.FLAG_RECEIVER_FOREGROUND;
+import static android.content.pm.PackageManager.DELETE_ARCHIVE;
import static android.content.pm.PackageManager.DELETE_KEEP_DATA;
import static com.google.common.truth.Truth.assertThat;
@@ -159,11 +160,12 @@
when(mContext.getPackageManager()).thenReturn(mPackageManager);
when(mPackageManager.getResourcesForApplication(eq(PACKAGE))).thenReturn(
mock(Resources.class));
- when(mIcon.compress(eq(Bitmap.CompressFormat.PNG), eq(100), any())).thenReturn(true);
mArchiveManager = spy(new PackageArchiver(mContext, pm));
doReturn(ICON_PATH).when(mArchiveManager).storeIcon(eq(PACKAGE),
any(LauncherActivityInfo.class), eq(mUserId), anyInt());
+ doReturn(mIcon).when(mArchiveManager).decodeIcon(
+ any(ArchiveState.ArchiveActivityInfo.class));
}
@Test
@@ -274,7 +276,7 @@
verify(mInstallerService).uninstall(
eq(new VersionedPackage(PACKAGE, PackageManager.VERSION_CODE_HIGHEST)),
- eq(CALLER_PACKAGE), eq(DELETE_KEEP_DATA), eq(mIntentSender),
+ eq(CALLER_PACKAGE), eq(DELETE_ARCHIVE | DELETE_KEEP_DATA), eq(mIntentSender),
eq(UserHandle.CURRENT.getIdentifier()), anyInt());
assertThat(mPackageSetting.readUserState(
UserHandle.CURRENT.getIdentifier()).getArchiveState()).isEqualTo(
@@ -374,6 +376,38 @@
assertThat(intent.getPackage()).isEqualTo(INSTALLER_PACKAGE);
}
+ @Test
+ public void getArchivedAppIcon_packageNotInstalled() {
+ when(mComputer.getPackageStateFiltered(eq(PACKAGE), anyInt(), anyInt())).thenReturn(
+ null);
+
+ Exception e = assertThrows(
+ ParcelableException.class,
+ () -> mArchiveManager.getArchivedAppIcon(PACKAGE, UserHandle.CURRENT));
+ assertThat(e.getCause()).isInstanceOf(PackageManager.NameNotFoundException.class);
+ assertThat(e.getCause()).hasMessageThat().isEqualTo(
+ String.format("Package %s not found.", PACKAGE));
+ }
+
+ @Test
+ public void getArchivedAppIcon_notArchived() {
+ Exception e = assertThrows(
+ ParcelableException.class,
+ () -> mArchiveManager.getArchivedAppIcon(PACKAGE, UserHandle.CURRENT));
+ assertThat(e.getCause()).isInstanceOf(PackageManager.NameNotFoundException.class);
+ assertThat(e.getCause()).hasMessageThat().isEqualTo(
+ String.format("Package %s is not currently archived.", PACKAGE));
+ }
+
+ @Test
+ public void getArchivedAppIcon_success() {
+ mUserState.setArchiveState(createArchiveState()).setInstalled(false);
+
+ assertThat(mArchiveManager.getArchivedAppIcon(PACKAGE, UserHandle.CURRENT)).isEqualTo(
+ mIcon);
+ }
+
+
private static ArchiveState createArchiveState() {
List<ArchiveState.ArchiveActivityInfo> activityInfos = new ArrayList<>();
for (LauncherActivityInfo mainActivity : createLauncherActivities()) {
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsCollectorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsCollectorTest.java
index a11a8f5..d2e83e9 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsCollectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsCollectorTest.java
@@ -87,6 +87,8 @@
public void setUp() {
when(mContext.getResources()).thenReturn(mResources);
+ when(mResources.getBoolean(eq(R.bool.config_biometricFrrNotificationEnabled)))
+ .thenReturn(true);
when(mResources.getFraction(eq(R.fraction.config_biometricNotificationFrrThreshold),
anyInt(), anyInt())).thenReturn(FRR_THRESHOLD);
@@ -110,7 +112,6 @@
0 /* modality */, mBiometricNotification);
}
-
@Test
public void authenticate_authenticationSucceeded_mapShouldBeUpdated() {
// Assert that the user doesn't exist in the map initially.
@@ -342,4 +343,32 @@
// Assert that notification count has been updated.
assertThat(authenticationStats.getEnrollmentNotifications()).isEqualTo(1);
}
+
+ @Test
+ public void authenticate_featureDisabled_mapMustNotBeUpdated() {
+ // Disable the feature.
+ when(mResources.getBoolean(eq(R.bool.config_biometricFrrNotificationEnabled)))
+ .thenReturn(false);
+ AuthenticationStatsCollector authenticationStatsCollector =
+ new AuthenticationStatsCollector(mContext, 0 /* modality */,
+ mBiometricNotification);
+
+ authenticationStatsCollector.setAuthenticationStatsForUser(USER_ID_1,
+ new AuthenticationStats(USER_ID_1, 500 /* totalAttempts */,
+ 400 /* rejectedAttempts */, 0 /* enrollmentNotifications */,
+ 0 /* modality */));
+
+ authenticationStatsCollector.authenticate(USER_ID_1, false /* authenticated */);
+
+ // Assert that no notification should be sent.
+ verify(mBiometricNotification, never()).sendFaceEnrollNotification(any());
+ verify(mBiometricNotification, never()).sendFpEnrollNotification(any());
+ // Assert that data hasn't been updated.
+ AuthenticationStats authenticationStats = authenticationStatsCollector
+ .getAuthenticationStatsForUser(USER_ID_1);
+ assertThat(authenticationStats.getTotalAttempts()).isEqualTo(500);
+ assertThat(authenticationStats.getRejectedAttempts()).isEqualTo(400);
+ assertThat(authenticationStats.getEnrollmentNotifications()).isEqualTo(0);
+ assertThat(authenticationStats.getFrr()).isWithin(0f).of(0.8f);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
index ddd1221..d85768d 100644
--- a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
@@ -79,6 +79,9 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
/**
* Tests for the {@link MediaProjectionManagerService} class.
*
@@ -202,6 +205,29 @@
}
@Test
+ public void testCreateProjection_priorProjectionGrant() throws
+ NameNotFoundException, InterruptedException {
+ // Create a first projection.
+ MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions();
+ FakeIMediaProjectionCallback callback1 = new FakeIMediaProjectionCallback();
+ projection.start(callback1);
+
+ // Create a second projection.
+ MediaProjectionManagerService.MediaProjection secondProjection =
+ startProjectionPreconditions();
+ FakeIMediaProjectionCallback callback2 = new FakeIMediaProjectionCallback();
+ secondProjection.start(callback2);
+
+ // Check that the first projection get stopped, but not the second projection.
+ final int timeout = 5;
+ boolean stoppedCallback1 = callback1.mLatch.await(timeout, TimeUnit.SECONDS);
+ boolean stoppedCallback2 = callback2.mLatch.await(timeout, TimeUnit.SECONDS);
+
+ assertThat(stoppedCallback1).isTrue();
+ assertThat(stoppedCallback2).isFalse();
+ }
+
+ @Test
public void testCreateProjection_attemptReuse_noPriorProjectionGrant()
throws NameNotFoundException {
// Create a first projection.
@@ -785,8 +811,10 @@
}
private static class FakeIMediaProjectionCallback extends IMediaProjectionCallback.Stub {
+ CountDownLatch mLatch = new CountDownLatch(1);
@Override
public void onStop() throws RemoteException {
+ mLatch.countDown();
}
@Override
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java
index b522cab..5147a08 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java
@@ -28,6 +28,7 @@
import static com.android.server.notification.NotificationRecordLogger.NotificationCancelledEvent.NOTIFICATION_CANCEL_USER_OTHER;
import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED;
import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_UPDATED;
+import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -48,6 +49,8 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.time.Duration;
+
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -230,4 +233,12 @@
NotificationRecordLogger.NotificationCancelledEvent.fromCancelReason(
REASON_CANCEL, DISMISSAL_OTHER));
}
+
+ @Test
+ public void testGetAgeInMinutes() {
+ long postTimeMs = Duration.ofMinutes(5).toMillis();
+ long whenMs = Duration.ofMinutes(2).toMillis();
+ int age = NotificationRecordLogger.getAgeInMinutes(postTimeMs, whenMs);
+ assertThat(age).isEqualTo(3);
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java
index 318f932..3034942 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java
@@ -248,8 +248,7 @@
verify(mPermManager).grantRuntimePermission(
"pkg", Manifest.permission.POST_NOTIFICATIONS, Context.DEVICE_ID_DEFAULT, 10);
verify(mPermManager).updatePermissionFlags("pkg", Manifest.permission.POST_NOTIFICATIONS,
- USER_FLAG_MASK | FLAG_PERMISSION_GRANTED_BY_DEFAULT,
- FLAG_PERMISSION_USER_SET, true, Context.DEVICE_ID_DEFAULT, 10);
+ USER_FLAG_MASK, FLAG_PERMISSION_USER_SET, true, Context.DEVICE_ID_DEFAULT, 10);
}
@Test
@@ -267,8 +266,7 @@
verify(mPermManager).grantRuntimePermission(
"pkg", Manifest.permission.POST_NOTIFICATIONS, Context.DEVICE_ID_DEFAULT, 10);
verify(mPermManager).updatePermissionFlags("pkg", Manifest.permission.POST_NOTIFICATIONS,
- USER_FLAG_MASK | FLAG_PERMISSION_GRANTED_BY_DEFAULT,
- FLAG_PERMISSION_USER_SET, true, Context.DEVICE_ID_DEFAULT, 10);
+ USER_FLAG_MASK, FLAG_PERMISSION_USER_SET, true, Context.DEVICE_ID_DEFAULT, 10);
}
@Test
@@ -282,8 +280,7 @@
eq("pkg"), eq(Manifest.permission.POST_NOTIFICATIONS),
eq(Context.DEVICE_ID_DEFAULT), eq(10), anyString());
verify(mPermManager).updatePermissionFlags("pkg", Manifest.permission.POST_NOTIFICATIONS,
- USER_FLAG_MASK | FLAG_PERMISSION_GRANTED_BY_DEFAULT,
- FLAG_PERMISSION_USER_SET, true, Context.DEVICE_ID_DEFAULT, 10);
+ USER_FLAG_MASK, FLAG_PERMISSION_USER_SET, true, Context.DEVICE_ID_DEFAULT, 10);
}
@Test
@@ -310,8 +307,7 @@
eq("pkg"), eq(Manifest.permission.POST_NOTIFICATIONS),
eq(Context.DEVICE_ID_DEFAULT), eq(10), anyString());
verify(mPermManager).updatePermissionFlags("pkg", Manifest.permission.POST_NOTIFICATIONS,
- USER_FLAG_MASK | FLAG_PERMISSION_GRANTED_BY_DEFAULT, 0,
- true, Context.DEVICE_ID_DEFAULT, 10);
+ USER_FLAG_MASK, 0, true, Context.DEVICE_ID_DEFAULT, 10);
}
@Test
diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml
index d6a3877..42e3383 100644
--- a/services/tests/wmtests/AndroidManifest.xml
+++ b/services/tests/wmtests/AndroidManifest.xml
@@ -47,8 +47,6 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MANAGE_MEDIA_PROJECTION"/>
- <uses-permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW"/>
-
<!-- TODO: Remove largeHeap hack when memory leak is fixed (b/123984854) -->
<application android:debuggable="true"
@@ -106,11 +104,6 @@
android:showWhenLocked="true"
android:turnScreenOn="true" />
- <activity android:name="android.app.Activity"
- android:exported="true"
- android:showWhenLocked="true"
- android:turnScreenOn="true" />
-
<activity
android:name="androidx.test.core.app.InstrumentationActivityInvoker$EmptyActivity"
android:exported="true">
diff --git a/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java b/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java
index 4791946..0a7bb00 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java
@@ -297,6 +297,7 @@
mPhoneWindowManager.overrideUserSetupComplete();
mPhoneWindowManager.setupAssistForLaunch();
mPhoneWindowManager.overrideTogglePanel();
+ mPhoneWindowManager.overrideInjectKeyEvent();
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
index e301da7..6d46d9c 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -464,6 +464,10 @@
doReturn(device).when(mInputManager).getInputDevice(anyInt());
}
+ void overrideInjectKeyEvent() {
+ doReturn(true).when(mInputManager).injectInputEvent(any(KeyEvent.class), anyInt());
+ }
+
void overrideSearchKeyBehavior(int behavior) {
mPhoneWindowManager.mSearchKeyBehavior = behavior;
}
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 ae58700..8ec3ac6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -2871,14 +2871,14 @@
.setTask(sourceRecord.getTask()).build();
secondRecord.showStartingWindow(null /* prev */, true /* newTask */, false,
true /* startActivity */, sourceRecord);
- assertFalse(secondRecord.mSplashScreenStyleSolidColor);
+ assertTrue(secondRecord.mAllowIconSplashScreen);
secondRecord.onStartingWindowDrawn();
final ActivityRecord finalRecord = new ActivityBuilder(mAtm)
.setTask(sourceRecord.getTask()).build();
finalRecord.showStartingWindow(null /* prev */, true /* newTask */, false,
true /* startActivity */, secondRecord);
- assertTrue(finalRecord.mSplashScreenStyleSolidColor);
+ assertFalse(finalRecord.mAllowIconSplashScreen);
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java b/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
index ecd84e1..3d3531e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
@@ -304,6 +304,30 @@
anyFloat(), anyFloat());
}
+ /**
+ * Test that resizing the output surface results in resizing the mirrored content to fit.
+ */
+ @Test
+ public void testOnConfigurationChanged_resizeSurface() {
+ mContentRecorder.setContentRecordingSession(mDisplaySession);
+ mContentRecorder.updateRecording();
+
+ // Resize the output surface.
+ final Point newSurfaceSize = new Point(Math.round(sSurfaceSize.x / 2f),
+ Math.round(sSurfaceSize.y * 2));
+ doReturn(newSurfaceSize).when(mWm.mDisplayManagerInternal).getDisplaySurfaceDefaultSize(
+ anyInt());
+ mContentRecorder.onConfigurationChanged(
+ mVirtualDisplayContent.getConfiguration().orientation);
+
+ // No resize is issued, only the initial transformations when we started recording.
+ verify(mTransaction, atLeast(2)).setPosition(eq(mRecordedSurface), anyFloat(),
+ anyFloat());
+ verify(mTransaction, atLeast(2)).setMatrix(eq(mRecordedSurface), anyFloat(), anyFloat(),
+ anyFloat(), anyFloat());
+
+ }
+
@Test
public void testOnTaskOrientationConfigurationChanged_resizesSurface() {
mContentRecorder.setContentRecordingSession(mTaskSession);
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
index 2ad9fa0..381b27b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
@@ -811,6 +811,28 @@
/* candidate */ SCREEN_ORIENTATION_PORTRAIT), SCREEN_ORIENTATION_PORTRAIT);
}
+ @Test
+ public void testOverrideOrientationIfNeeded_userAspectRatioApplied_unspecifiedOverridden() {
+ spyOn(mController);
+ doReturn(true).when(mController).shouldApplyUserMinAspectRatioOverride();
+
+ assertEquals(mController.overrideOrientationIfNeeded(
+ /* candidate */ SCREEN_ORIENTATION_UNSPECIFIED), SCREEN_ORIENTATION_PORTRAIT);
+
+ // unchanged if orientation is specified
+ assertEquals(mController.overrideOrientationIfNeeded(
+ /* candidate */ SCREEN_ORIENTATION_LANDSCAPE), SCREEN_ORIENTATION_LANDSCAPE);
+ }
+
+ @Test
+ public void testOverrideOrientationIfNeeded_userAspectRatioNotApplied_returnsUnchanged() {
+ spyOn(mController);
+ doReturn(false).when(mController).shouldApplyUserMinAspectRatioOverride();
+
+ assertEquals(mController.overrideOrientationIfNeeded(
+ /* candidate */ SCREEN_ORIENTATION_UNSPECIFIED), SCREEN_ORIENTATION_UNSPECIFIED);
+ }
+
// shouldApplyUser...Override
@Test
public void testShouldApplyUserFullscreenOverride_trueProperty_returnsFalse() throws Exception {
@@ -862,6 +884,39 @@
}
@Test
+ public void testShouldEnableUserAspectRatioSettings_falseProperty_returnsFalse()
+ throws Exception {
+ prepareActivityThatShouldApplyUserMinAspectRatioOverride();
+ mockThatProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE, /* value */ false);
+
+ mController = new LetterboxUiController(mWm, mActivity);
+
+ assertFalse(mController.shouldEnableUserAspectRatioSettings());
+ }
+
+ @Test
+ public void testShouldEnableUserAspectRatioSettings_trueProperty_returnsTrue()
+ throws Exception {
+ prepareActivityThatShouldApplyUserMinAspectRatioOverride();
+ mockThatProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE, /* value */ true);
+
+ mController = new LetterboxUiController(mWm, mActivity);
+
+ assertTrue(mController.shouldEnableUserAspectRatioSettings());
+ }
+
+ @Test
+ public void testShouldEnableUserAspectRatioSettings_noIgnoreOrientaion_returnsFalse()
+ throws Exception {
+ prepareActivityForShouldApplyUserMinAspectRatioOverride(/* orientationRequest */ false);
+ mockThatProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE, /* value */ true);
+
+ mController = new LetterboxUiController(mWm, mActivity);
+
+ assertFalse(mController.shouldEnableUserAspectRatioSettings());
+ }
+
+ @Test
public void testShouldApplyUserMinAspectRatioOverride_falseProperty_returnsFalse()
throws Exception {
prepareActivityThatShouldApplyUserMinAspectRatioOverride();
@@ -898,13 +953,26 @@
assertTrue(mController.shouldApplyUserMinAspectRatioOverride());
}
- private void prepareActivityThatShouldApplyUserMinAspectRatioOverride() {
+ @Test
+ public void testShouldApplyUserMinAspectRatioOverride_noIgnoreOrientationreturnsFalse() {
+ prepareActivityForShouldApplyUserMinAspectRatioOverride(/* orientationRequest */ false);
+
+ assertFalse(mController.shouldApplyUserMinAspectRatioOverride());
+ }
+
+ private void prepareActivityForShouldApplyUserMinAspectRatioOverride(
+ boolean orientationRequest) {
spyOn(mController);
- doReturn(true).when(mLetterboxConfiguration).isUserAppAspectRatioSettingsEnabled();
+ doReturn(orientationRequest).when(
+ mLetterboxConfiguration).isUserAppAspectRatioSettingsEnabled();
mDisplayContent.setIgnoreOrientationRequest(true);
doReturn(USER_MIN_ASPECT_RATIO_3_2).when(mController).getUserMinAspectRatioOverrideCode();
}
+ private void prepareActivityThatShouldApplyUserMinAspectRatioOverride() {
+ prepareActivityForShouldApplyUserMinAspectRatioOverride(/* orientationRequest */ true);
+ }
+
private void prepareActivityThatShouldApplyUserFullscreenOverride() {
spyOn(mController);
doReturn(true).when(mLetterboxConfiguration).isUserAppAspectRatioFullscreenEnabled();
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
index de3a526..491d5b5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.app.ActivityManager.PROCESS_STATE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
@@ -148,8 +149,9 @@
anyInt() /* startFlags */, any() /* profilerInfo */);
// Assume its process is alive because the caller should be the recents service.
- mSystemServicesTestRule.addProcess(aInfo.packageName, aInfo.processName, 12345 /* pid */,
- aInfo.applicationInfo.uid);
+ final WindowProcessController proc = mSystemServicesTestRule.addProcess(aInfo.packageName,
+ aInfo.processName, 12345 /* pid */, aInfo.applicationInfo.uid);
+ proc.setCurrentProcState(PROCESS_STATE_HOME);
Intent recentsIntent = new Intent().setComponent(mRecentsComponent);
// Null animation indicates to preload.
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index 1dd71e0..fb27d63 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -571,26 +571,28 @@
final Task task = rootTask.getBottomMostTask();
final ActivityRecord root = task.getTopNonFinishingActivity();
spyOn(mWm.mLetterboxConfiguration);
-
- // When device config flag is disabled the button is not enabled
- doReturn(false).when(mWm.mLetterboxConfiguration)
- .isUserAppAspectRatioSettingsEnabled();
- doReturn(false).when(mWm.mLetterboxConfiguration)
- .isTranslucentLetterboxingEnabled();
- assertFalse(task.getTaskInfo().topActivityEligibleForUserAspectRatioButton);
-
- // The flag is enabled
- doReturn(true).when(mWm.mLetterboxConfiguration)
- .isUserAppAspectRatioSettingsEnabled();
spyOn(root);
- doReturn(task).when(root).getOrganizedTask();
- // When the flag is enabled and the top activity is not in size compat mode.
+ spyOn(root.mLetterboxUiController);
+
+ doReturn(true).when(root.mLetterboxUiController)
+ .shouldEnableUserAspectRatioSettings();
doReturn(false).when(root).inSizeCompatMode();
+ doReturn(task).when(root).getOrganizedTask();
+
+ // The button should be eligible to be displayed
assertTrue(task.getTaskInfo().topActivityEligibleForUserAspectRatioButton);
+ // When shouldApplyUserMinAspectRatioOverride is disable the button is not enabled
+ doReturn(false).when(root.mLetterboxUiController)
+ .shouldEnableUserAspectRatioSettings();
+ assertFalse(task.getTaskInfo().topActivityEligibleForUserAspectRatioButton);
+ doReturn(true).when(root.mLetterboxUiController)
+ .shouldEnableUserAspectRatioSettings();
+
// When in size compat mode the button is not enabled
doReturn(true).when(root).inSizeCompatMode();
assertFalse(task.getTaskInfo().topActivityEligibleForUserAspectRatioButton);
+ doReturn(false).when(root).inSizeCompatMode();
}
/**
diff --git a/services/tests/wmtests/src/com/android/server/wm/TrustedOverlayTests.java b/services/tests/wmtests/src/com/android/server/wm/TrustedOverlayTests.java
deleted file mode 100644
index e8a847c..0000000
--- a/services/tests/wmtests/src/com/android/server/wm/TrustedOverlayTests.java
+++ /dev/null
@@ -1,149 +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 com.android.server.wm;
-
-import static android.view.InputWindowHandle.USE_SURFACE_TRUSTED_OVERLAY;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assume.assumeFalse;
-import static org.junit.Assume.assumeTrue;
-
-import android.app.Activity;
-import android.app.Instrumentation;
-import android.os.IBinder;
-import android.platform.test.annotations.Presubmit;
-import android.server.wm.BuildUtils;
-import android.server.wm.CtsWindowInfoUtils;
-import android.view.View;
-import android.view.ViewTreeObserver;
-import android.view.WindowManager;
-
-import androidx.test.platform.app.InstrumentationRegistry;
-import androidx.test.rule.ActivityTestRule;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TestName;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-@Presubmit
-public class TrustedOverlayTests {
- private static final String TAG = "TrustedOverlayTests";
- private static final long TIMEOUT_S = 5L * BuildUtils.HW_TIMEOUT_MULTIPLIER;
-
- @Rule
- public TestName mName = new TestName();
-
- private final ActivityTestRule<Activity> mActivityRule = new ActivityTestRule<>(
- Activity.class);
-
- private Instrumentation mInstrumentation;
- private Activity mActivity;
-
- @Before
- public void setup() {
- mInstrumentation = InstrumentationRegistry.getInstrumentation();
- mActivity = mActivityRule.launchActivity(null);
- }
-
- @Test
- public void setTrustedOverlayInputWindow() throws InterruptedException {
- assumeFalse(USE_SURFACE_TRUSTED_OVERLAY);
- testTrustedOverlayChildHelper(false);
- }
-
- @Test
- public void setTrustedOverlayChildLayer() throws InterruptedException {
- assumeTrue(USE_SURFACE_TRUSTED_OVERLAY);
- testTrustedOverlayChildHelper(true);
- }
-
- private void testTrustedOverlayChildHelper(boolean expectTrusted) throws InterruptedException {
- IBinder[] tokens = new IBinder[2];
- CountDownLatch hostTokenReady = new CountDownLatch(1);
- mInstrumentation.runOnMainSync(() -> {
- mActivity.getWindow().addPrivateFlags(PRIVATE_FLAG_TRUSTED_OVERLAY);
- View rootView = mActivity.getWindow().getDecorView();
- if (rootView.isAttachedToWindow()) {
- tokens[0] = rootView.getWindowToken();
- hostTokenReady.countDown();
- } else {
- rootView.getViewTreeObserver().addOnWindowAttachListener(
- new ViewTreeObserver.OnWindowAttachListener() {
- @Override
- public void onWindowAttached() {
- tokens[0] = rootView.getWindowToken();
- hostTokenReady.countDown();
- }
-
- @Override
- public void onWindowDetached() {
- }
- });
- }
- });
-
- assertTrue("Failed to wait for host to get added",
- hostTokenReady.await(TIMEOUT_S, TimeUnit.SECONDS));
-
- mInstrumentation.runOnMainSync(() -> {
- WindowManager wm = mActivity.getSystemService(WindowManager.class);
-
- View childView = new View(mActivity) {
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- tokens[1] = getWindowToken();
- }
- };
- WindowManager.LayoutParams params = new WindowManager.LayoutParams();
- params.token = tokens[0];
- params.type = TYPE_APPLICATION_PANEL;
- wm.addView(childView, params);
- });
-
- boolean[] foundTrusted = new boolean[2];
-
- CtsWindowInfoUtils.waitForWindowInfos(
- windowInfos -> {
- for (var windowInfo : windowInfos) {
- if (windowInfo.windowToken == tokens[0]
- && windowInfo.isTrustedOverlay) {
- foundTrusted[0] = true;
- } else if (windowInfo.windowToken == tokens[1]
- && windowInfo.isTrustedOverlay) {
- foundTrusted[1] = true;
- }
- }
- return foundTrusted[0] && foundTrusted[1];
- }, TIMEOUT_S, TimeUnit.SECONDS);
-
- if (!foundTrusted[0] || !foundTrusted[1]) {
- CtsWindowInfoUtils.dumpWindowsOnScreen(TAG, mName.getMethodName());
- }
-
- assertEquals("Failed to find parent window or was not marked trusted", expectTrusted,
- foundTrusted[0]);
- assertEquals("Failed to find child window or was not marked trusted", expectTrusted,
- foundTrusted[1]);
- }
-}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TrustedPresentationCallbackTest.java b/services/tests/wmtests/src/com/android/server/wm/TrustedPresentationCallbackTest.java
index f173d66..c5dd447 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TrustedPresentationCallbackTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TrustedPresentationCallbackTest.java
@@ -18,16 +18,16 @@
import static android.server.wm.ActivityManagerTestBase.createFullscreenActivityScenarioRule;
import static android.server.wm.BuildUtils.HW_TIMEOUT_MULTIPLIER;
-
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import android.app.Activity;
import android.platform.test.annotations.Presubmit;
-import android.util.Log;
+import android.server.wm.CtsWindowInfoUtils;
import android.view.SurfaceControl;
import android.view.SurfaceControl.TrustedPresentationThresholds;
+import androidx.annotation.GuardedBy;
import androidx.test.ext.junit.rules.ActivityScenarioRule;
import com.android.server.wm.utils.CommonUtils;
@@ -36,9 +36,8 @@
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TestName;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
/**
@@ -53,6 +52,15 @@
private static final float FRACTION_VISIBLE = 0.1f;
+ private final Object mResultsLock = new Object();
+ @GuardedBy("mResultsLock")
+ private boolean mResult;
+ @GuardedBy("mResultsLock")
+ private boolean mReceivedResults;
+
+ @Rule
+ public TestName mName = new TestName();
+
@Rule
public ActivityScenarioRule<TestActivity> mActivityRule = createFullscreenActivityScenarioRule(
TestActivity.class);
@@ -71,36 +79,32 @@
@Test
public void testAddTrustedPresentationListenerOnWindow() throws InterruptedException {
- boolean[] results = new boolean[1];
- CountDownLatch receivedResults = new CountDownLatch(1);
TrustedPresentationThresholds thresholds = new TrustedPresentationThresholds(
1 /* minAlpha */, FRACTION_VISIBLE, STABILITY_REQUIREMENT_MS);
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
mActivity.getWindow().getRootSurfaceControl().addTrustedPresentationCallback(t, thresholds,
Runnable::run, inTrustedPresentationState -> {
- Log.d(TAG, "onTrustedPresentationChanged " + inTrustedPresentationState);
- results[0] = inTrustedPresentationState;
- receivedResults.countDown();
+ synchronized (mResultsLock) {
+ mResult = inTrustedPresentationState;
+ mReceivedResults = true;
+ mResultsLock.notify();
+ }
});
t.apply();
-
- assertTrue("Timed out waiting for results",
- receivedResults.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
- assertTrue(results[0]);
+ synchronized (mResultsLock) {
+ assertResults();
+ }
}
@Test
public void testRemoveTrustedPresentationListenerOnWindow() throws InterruptedException {
- final Object resultsLock = new Object();
- boolean[] results = new boolean[1];
- boolean[] receivedResults = new boolean[1];
TrustedPresentationThresholds thresholds = new TrustedPresentationThresholds(
1 /* minAlpha */, FRACTION_VISIBLE, STABILITY_REQUIREMENT_MS);
Consumer<Boolean> trustedPresentationCallback = inTrustedPresentationState -> {
- synchronized (resultsLock) {
- results[0] = inTrustedPresentationState;
- receivedResults[0] = true;
- resultsLock.notify();
+ synchronized (mResultsLock) {
+ mResult = inTrustedPresentationState;
+ mReceivedResults = true;
+ mResultsLock.notify();
}
};
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
@@ -108,34 +112,43 @@
Runnable::run, trustedPresentationCallback);
t.apply();
- synchronized (resultsLock) {
- if (!receivedResults[0]) {
- resultsLock.wait(WAIT_TIME_MS);
+ synchronized (mResultsLock) {
+ if (!mReceivedResults) {
+ mResultsLock.wait(WAIT_TIME_MS);
}
- // Make sure we received the results and not just timed out
- assertTrue("Timed out waiting for results", receivedResults[0]);
- assertTrue(results[0]);
-
+ assertResults();
// reset the state
- receivedResults[0] = false;
+ mReceivedResults = false;
}
mActivity.getWindow().getRootSurfaceControl().removeTrustedPresentationCallback(t,
trustedPresentationCallback);
t.apply();
- synchronized (resultsLock) {
- if (!receivedResults[0]) {
- resultsLock.wait(WAIT_TIME_MS);
+ synchronized (mResultsLock) {
+ if (!mReceivedResults) {
+ mResultsLock.wait(WAIT_TIME_MS);
}
// Ensure we waited the full time and never received a notify on the result from the
// callback.
- assertFalse("Should never have received a callback", receivedResults[0]);
+ assertFalse("Should never have received a callback", mReceivedResults);
// results shouldn't have changed.
- assertTrue(results[0]);
+ assertTrue(mResult);
}
}
+ @GuardedBy("mResultsLock")
+ private void assertResults() throws InterruptedException {
+ mResultsLock.wait(WAIT_TIME_MS);
+
+ if (!mReceivedResults) {
+ CtsWindowInfoUtils.dumpWindowsOnScreen(TAG, "test " + mName.getMethodName());
+ }
+ // Make sure we received the results and not just timed out
+ assertTrue("Timed out waiting for results", mReceivedResults);
+ assertTrue(mResult);
+ }
+
public static class TestActivity extends Activity {
}
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 66f3bed..36a8fc1 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -4607,12 +4607,12 @@
*
* <p>Example:
*
- * <pre><code>
+ * <pre>{@code
* <string-array name="carrier_service_name_array" num="2">
* <item value="Police"/>
* <item value="Ambulance"/>
* </string-array>
- * </code></pre>
+ * }</pre>
*/
public static final String KEY_CARRIER_SERVICE_NAME_STRING_ARRAY = "carrier_service_name_array";
@@ -4626,18 +4626,18 @@
*
* <ul>
* <li>The number of items in both the arrays are equal
- * <li>The item added in this key follows a specific format. Either it should be all numbers,
- * or "+" followed by all numbers.
+ * <li>The item should contain dialable characters only which includes 0-9, -, *, #, (, ),
+ * SPACE.
* </ul>
*
* <p>Example:
*
- * <pre><code>
+ * <pre>{@code
* <string-array name="carrier_service_number_array" num="2">
- * <item value="123"/>
- * <item value="+343"/>
+ * <item value="*123"/>
+ * <item value="+ (111) 111-111"/>
* </string-array>
- * </code></pre>
+ * }</pre>
*/
public static final String KEY_CARRIER_SERVICE_NUMBER_STRING_ARRAY =
"carrier_service_number_array";
@@ -10229,7 +10229,7 @@
sDefaults.putInt(KEY_LTE_PLUS_THRESHOLD_BANDWIDTH_KHZ_INT, 20000);
sDefaults.putInt(KEY_NR_ADVANCED_THRESHOLD_BANDWIDTH_KHZ_INT, 0);
sDefaults.putBoolean(KEY_INCLUDE_LTE_FOR_NR_ADVANCED_THRESHOLD_BANDWIDTH_BOOL, false);
- sDefaults.putBoolean(KEY_RATCHET_NR_ADVANCED_BANDWIDTH_IF_RRC_IDLE_BOOL, true);
+ sDefaults.putBoolean(KEY_RATCHET_NR_ADVANCED_BANDWIDTH_IF_RRC_IDLE_BOOL, false);
sDefaults.putIntArray(KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY,
new int[]{CARRIER_NR_AVAILABILITY_NSA, CARRIER_NR_AVAILABILITY_SA});
sDefaults.putBoolean(KEY_LTE_ENABLED_BOOL, true);
diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index f1af68f..53f347e 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -71,13 +71,6 @@
*/
public static final int INVALID = Integer.MAX_VALUE;
- private static final int LTE_RSRP_THRESHOLDS_NUM = 4;
-
- private static final int WCDMA_RSCP_THRESHOLDS_NUM = 4;
-
- /* The type of signal measurement */
- private static final String MEASUREMENT_TYPE_RSCP = "rscp";
-
// Timestamp of SignalStrength since boot
// Effectively final. Timestamp is set during construction of SignalStrength
private long mTimestampMillis;
@@ -92,28 +85,9 @@
CellSignalStrengthNr mNr;
/**
- * Create a new SignalStrength from a intent notifier Bundle
- *
- * This method may be used by external applications.
- *
- * @param m Bundle from intent notifier
- * @return newly created SignalStrength
- *
- * @hide
- */
- @UnsupportedAppUsage
- public static SignalStrength newFromBundle(Bundle m) {
- SignalStrength ret;
- ret = new SignalStrength();
- ret.setFromNotifierBundle(m);
- return ret;
- }
-
- /**
* This constructor is used to create SignalStrength with default
* values.
*
- * @return newly created SignalStrength
* @hide
*/
@UnsupportedAppUsage
@@ -164,20 +138,20 @@
* Returns a List of CellSignalStrength Components of this SignalStrength Report.
*
* Use this API to access underlying
- * {@link android.telephony#CellSignalStrength CellSignalStrength} objects that provide more
+ * {@link android.telephony.CellSignalStrength CellSignalStrength} objects that provide more
* granular information about the SignalStrength report. Only valid (non-empty)
* CellSignalStrengths will be returned. The order of any returned elements is not guaranteed,
* and the list may contain more than one instance of a CellSignalStrength type.
*
* @return a List of CellSignalStrength or an empty List if there are no valid measurements.
*
- * @see android.telephony#CellSignalStrength
- * @see android.telephony#CellSignalStrengthNr
- * @see android.telephony#CellSignalStrengthLte
- * @see android.telephony#CellSignalStrengthTdscdma
- * @see android.telephony#CellSignalStrengthWcdma
- * @see android.telephony#CellSignalStrengthCdma
- * @see android.telephony#CellSignalStrengthGsm
+ * @see android.telephony.CellSignalStrength
+ * @see android.telephony.CellSignalStrengthNr
+ * @see android.telephony.CellSignalStrengthLte
+ * @see android.telephony.CellSignalStrengthTdscdma
+ * @see android.telephony.CellSignalStrengthWcdma
+ * @see android.telephony.CellSignalStrengthCdma
+ * @see android.telephony.CellSignalStrengthGsm
*/
@NonNull public List<CellSignalStrength> getCellSignalStrengths() {
return getCellSignalStrengths(CellSignalStrength.class);
@@ -187,7 +161,7 @@
* Returns a List of CellSignalStrength Components of this SignalStrength Report.
*
* Use this API to access underlying
- * {@link android.telephony#CellSignalStrength CellSignalStrength} objects that provide more
+ * {@link android.telephony.CellSignalStrength CellSignalStrength} objects that provide more
* granular information about the SignalStrength report. Only valid (non-empty)
* CellSignalStrengths will be returned. The order of any returned elements is not guaranteed,
* and the list may contain more than one instance of a CellSignalStrength type.
@@ -197,13 +171,13 @@
* return values.
* @return a List of CellSignalStrength or an empty List if there are no valid measurements.
*
- * @see android.telephony#CellSignalStrength
- * @see android.telephony#CellSignalStrengthNr
- * @see android.telephony#CellSignalStrengthLte
- * @see android.telephony#CellSignalStrengthTdscdma
- * @see android.telephony#CellSignalStrengthWcdma
- * @see android.telephony#CellSignalStrengthCdma
- * @see android.telephony#CellSignalStrengthGsm
+ * @see android.telephony.CellSignalStrength
+ * @see android.telephony.CellSignalStrengthNr
+ * @see android.telephony.CellSignalStrengthLte
+ * @see android.telephony.CellSignalStrengthTdscdma
+ * @see android.telephony.CellSignalStrengthWcdma
+ * @see android.telephony.CellSignalStrengthCdma
+ * @see android.telephony.CellSignalStrengthGsm
*/
@NonNull public <T extends CellSignalStrength> List<T> getCellSignalStrengths(
@NonNull Class<T> clazz) {
@@ -319,7 +293,7 @@
*
*/
public static final @android.annotation.NonNull Parcelable.Creator<SignalStrength> CREATOR =
- new Parcelable.Creator<SignalStrength>() {
+ new Parcelable.Creator<>() {
public SignalStrength createFromParcel(Parcel in) {
return new SignalStrength(in);
}
@@ -327,7 +301,7 @@
public SignalStrength[] newArray(int size) {
return new SignalStrength[size];
}
- };
+ };
/**
* Get the GSM RSSI in ASU.
@@ -338,7 +312,7 @@
*
* @deprecated this information should be retrieved from
* {@link CellSignalStrengthGsm#getAsuLevel}.
- * @see android.telephony#CellSignalStrengthGsm
+ * @see android.telephony.CellSignalStrengthGsm
* @see android.telephony.SignalStrength#getCellSignalStrengths
*/
@Deprecated
@@ -352,7 +326,7 @@
* @deprecated this information should be retrieved from
* {@link CellSignalStrengthGsm#getBitErrorRate}.
*
- * @see android.telephony#CellSignalStrengthGsm
+ * @see android.telephony.CellSignalStrengthGsm
* @see android.telephony.SignalStrength#getCellSignalStrengths()
*/
@Deprecated
@@ -368,7 +342,7 @@
* @deprecated this information should be retrieved from
* {@link CellSignalStrengthCdma#getCdmaDbm}.
*
- * @see android.telephony#CellSignalStrengthCdma
+ * @see android.telephony.CellSignalStrengthCdma
* @see android.telephony.SignalStrength#getCellSignalStrengths()
*/
@Deprecated
@@ -382,7 +356,7 @@
* @deprecated this information should be retrieved from
* {@link CellSignalStrengthCdma#getCdmaEcio}.
*
- * @see android.telephony#CellSignalStrengthCdma
+ * @see android.telephony.CellSignalStrengthCdma
* @see android.telephony.SignalStrength#getCellSignalStrengths()
*/
@Deprecated
@@ -398,7 +372,7 @@
* @deprecated this information should be retrieved from
* {@link CellSignalStrengthCdma#getEvdoDbm}.
*
- * @see android.telephony#CellSignalStrengthCdma
+ * @see android.telephony.CellSignalStrengthCdma
* @see android.telephony.SignalStrength#getCellSignalStrengths()
*/
@Deprecated
@@ -412,7 +386,7 @@
* @deprecated this information should be retrieved from
* {@link CellSignalStrengthCdma#getEvdoEcio}.
*
- * @see android.telephony#CellSignalStrengthCdma
+ * @see android.telephony.CellSignalStrengthCdma
* @see android.telephony.SignalStrength#getCellSignalStrengths()
*/
@Deprecated
@@ -426,7 +400,7 @@
* @deprecated this information should be retrieved from
* {@link CellSignalStrengthCdma#getEvdoSnr}.
*
- * @see android.telephony#CellSignalStrengthCdma
+ * @see android.telephony.CellSignalStrengthCdma
* @see android.telephony.SignalStrength#getCellSignalStrengths()
*/
@Deprecated
@@ -438,7 +412,7 @@
* @deprecated this information should be retrieved from
* {@link CellSignalStrengthLte#getRssi}.
*
- * @see android.telephony#CellSignalStrengthLte
+ * @see android.telephony.CellSignalStrengthLte
* @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
@@ -452,7 +426,7 @@
* @deprecated this information should be retrieved from
* {@link CellSignalStrengthLte#getRsrp}.
*
- * @see android.telephony#CellSignalStrengthLte
+ * @see android.telephony.CellSignalStrengthLte
* @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
@@ -466,7 +440,7 @@
* @deprecated this information should be retrieved from
* {@link CellSignalStrengthLte#getRsrq}.
*
- * @see android.telephony#CellSignalStrengthLte
+ * @see android.telephony.CellSignalStrengthLte
* @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
@@ -480,7 +454,7 @@
* @deprecated this information should be retrieved from
* {@link CellSignalStrengthLte#getRssnr}.
*
- * @see android.telephony#CellSignalStrengthLte
+ * @see android.telephony.CellSignalStrengthLte
* @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
@@ -494,7 +468,7 @@
* @deprecated this information should be retrieved from
* {@link CellSignalStrengthLte#getCqi}.
*
- * @see android.telephony#CellSignalStrengthLte
+ * @see android.telephony.CellSignalStrengthLte
* @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
@@ -527,7 +501,7 @@
* @deprecated this information should be retrieved from
* {@link CellSignalStrength#getAsuLevel}. Because the levels vary by technology,
* this method is misleading and should not be used.
- * @see android.telephony#CellSignalStrength
+ * @see android.telephony.CellSignalStrength
* @see android.telephony.SignalStrength#getCellSignalStrengths
* @hide
*/
@@ -543,7 +517,7 @@
* @deprecated this information should be retrieved from
* {@link CellSignalStrength#getDbm()}. Because the levels vary by technology,
* this method is misleading and should not be used.
- * @see android.telephony#CellSignalStrength
+ * @see android.telephony.CellSignalStrength
* @see android.telephony.SignalStrength#getCellSignalStrengths
* @hide
*/
@@ -559,7 +533,7 @@
* @deprecated this information should be retrieved from
* {@link CellSignalStrengthGsm#getDbm}.
*
- * @see android.telephony#CellSignalStrengthGsm
+ * @see android.telephony.CellSignalStrengthGsm
* @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
@@ -575,7 +549,7 @@
* @deprecated this information should be retrieved from
* {@link CellSignalStrengthGsm#getLevel}.
*
- * @see android.telephony#CellSignalStrengthGsm
+ * @see android.telephony.CellSignalStrengthGsm
* @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
@@ -591,7 +565,7 @@
* @deprecated this information should be retrieved from
* {@link CellSignalStrengthGsm#getAsuLevel}.
*
- * @see android.telephony#CellSignalStrengthGsm
+ * @see android.telephony.CellSignalStrengthGsm
* @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
@@ -607,7 +581,7 @@
* @deprecated this information should be retrieved from
* {@link CellSignalStrengthCdma#getLevel}.
*
- * @see android.telephony#CellSignalStrengthCdma
+ * @see android.telephony.CellSignalStrengthCdma
* @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
@@ -625,7 +599,7 @@
* ASU for CDMA, the resultant value is Android-specific and is not recommended
* for use.
*
- * @see android.telephony#CellSignalStrengthCdma
+ * @see android.telephony.CellSignalStrengthCdma
* @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
@@ -641,7 +615,7 @@
* @deprecated this information should be retrieved from
* {@link CellSignalStrengthCdma#getEvdoLevel}.
*
- * @see android.telephony#CellSignalStrengthCdma
+ * @see android.telephony.CellSignalStrengthCdma
* @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
@@ -659,7 +633,7 @@
* ASU for EvDO, the resultant value is Android-specific and is not recommended
* for use.
*
- * @see android.telephony#CellSignalStrengthCdma
+ * @see android.telephony.CellSignalStrengthCdma
* @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
@@ -675,7 +649,7 @@
* @deprecated this information should be retrieved from
* {@link CellSignalStrengthLte#getDbm}.
*
- * @see android.telephony#CellSignalStrengthLte
+ * @see android.telephony.CellSignalStrengthLte
* @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
@@ -691,7 +665,7 @@
* @deprecated this information should be retrieved from
* {@link CellSignalStrengthLte#getLevel}.
*
- * @see android.telephony#CellSignalStrengthLte
+ * @see android.telephony.CellSignalStrengthLte
* @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
@@ -708,7 +682,7 @@
* @deprecated this information should be retrieved from
* {@link CellSignalStrengthLte#getAsuLevel}.
*
- * @see android.telephony#CellSignalStrengthLte
+ * @see android.telephony.CellSignalStrengthLte
* @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
@@ -739,7 +713,7 @@
* @deprecated this information should be retrieved from
* {@link CellSignalStrengthTdscdma#getDbm}.
*
- * @see android.telephony#CellSignalStrengthTdscdma
+ * @see android.telephony.CellSignalStrengthTdscdma
* @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
@@ -758,7 +732,7 @@
* @deprecated this information should be retrieved from
* {@link CellSignalStrengthTdscdma#getLevel}.
*
- * @see android.telephony#CellSignalStrengthTdscdma
+ * @see android.telephony.CellSignalStrengthTdscdma
* @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
@@ -774,7 +748,7 @@
* @deprecated this information should be retrieved from
* {@link CellSignalStrengthTdscdma#getAsuLevel}.
*
- * @see android.telephony#CellSignalStrengthTdscdma
+ * @see android.telephony.CellSignalStrengthTdscdma
* @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
@@ -790,7 +764,7 @@
* @deprecated this information should be retrieved from
* {@link CellSignalStrengthWcdma#getRscp}.
*
- * @see android.telephony#CellSignalStrengthWcdma
+ * @see android.telephony.CellSignalStrengthWcdma
* @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
@@ -805,7 +779,7 @@
* @deprecated this information should be retrieved from
* {@link CellSignalStrengthWcdma#getAsuLevel}.
*
- * @see android.telephony#CellSignalStrengthWcdma
+ * @see android.telephony.CellSignalStrengthWcdma
* @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
@@ -828,7 +802,7 @@
* @deprecated this information should be retrieved from
* {@link CellSignalStrengthWcdma#getDbm}.
*
- * @see android.telephony#CellSignalStrengthWcdma
+ * @see android.telephony.CellSignalStrengthWcdma
* @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
@@ -843,7 +817,7 @@
* @deprecated this information should be retrieved from
* {@link CellSignalStrengthWcdma#getDbm}.
*
- * @see android.telephony#CellSignalStrengthWcdma
+ * @see android.telephony.CellSignalStrengthWcdma
* @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
@@ -895,32 +869,12 @@
}
/**
- * Set SignalStrength based on intent notifier map
- *
- * @param m intent notifier map
- *
- * @deprecated this method relies on non-stable implementation details, and full access to
- * internal storage is available via {@link getCellSignalStrengths()}.
- * @hide
- */
- @Deprecated
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
- private void setFromNotifierBundle(Bundle m) {
- mCdma = m.getParcelable("Cdma", android.telephony.CellSignalStrengthCdma.class);
- mGsm = m.getParcelable("Gsm", android.telephony.CellSignalStrengthGsm.class);
- mWcdma = m.getParcelable("Wcdma", android.telephony.CellSignalStrengthWcdma.class);
- mTdscdma = m.getParcelable("Tdscdma", android.telephony.CellSignalStrengthTdscdma.class);
- mLte = m.getParcelable("Lte", android.telephony.CellSignalStrengthLte.class);
- mNr = m.getParcelable("Nr", android.telephony.CellSignalStrengthNr.class);
- }
-
- /**
* Set intent notifier Bundle based on SignalStrength
*
* @param m intent notifier Bundle
*
* @deprecated this method relies on non-stable implementation details, and full access to
- * internal storage is available via {@link getCellSignalStrengths()}.
+ * internal storage is available via {@link #getCellSignalStrengths()}.
* @hide
*/
@Deprecated
diff --git a/tests/CtsSurfaceControlTestsStaging/TEST_MAPPING b/tests/CtsSurfaceControlTestsStaging/TEST_MAPPING
index de0ad59..0b75eb3 100644
--- a/tests/CtsSurfaceControlTestsStaging/TEST_MAPPING
+++ b/tests/CtsSurfaceControlTestsStaging/TEST_MAPPING
@@ -1,5 +1,5 @@
{
- "postsubmit": [
+ "presubmit": [
{
"name": "CtsSurfaceControlTestsStaging"
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt
index 87231c8..93a5bf5 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt
@@ -27,7 +27,11 @@
import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper
import com.android.server.wm.flicker.activityembedding.ActivityEmbeddingTestBase
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.wm.shell.flicker.utils.*
+import com.android.wm.shell.flicker.utils.SPLIT_SCREEN_DIVIDER_COMPONENT
+import com.android.wm.shell.flicker.utils.SplitScreenUtils
+import com.android.wm.shell.flicker.utils.appWindowIsVisibleAtEnd
+import com.android.wm.shell.flicker.utils.splitAppLayerBoundsIsVisibleAtEnd
+import com.android.wm.shell.flicker.utils.splitScreenDividerBecomesVisible
import org.junit.FixMethodOrder
import org.junit.Ignore
import org.junit.Test
@@ -63,11 +67,12 @@
.withHomeActivityVisible()
.waitForAndVerify()
startDisplayBounds =
- wmHelper.currentState.layerState.physicalDisplayBounds ?:
- error("Display not found")
+ wmHelper.currentState.layerState.physicalDisplayBounds
+ ?: error("Display not found")
}
transitions {
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, testApp, secondaryApp)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, testApp, secondaryApp,
+ flicker.scenario.startRotation)
SplitScreenUtils.waitForSplitComplete(wmHelper, testApp, secondaryApp)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchBetweenTwoAppsForward.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchBetweenTwoAppsForward.kt
index 4941eea..3cae1c4 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchBetweenTwoAppsForward.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchBetweenTwoAppsForward.kt
@@ -30,6 +30,7 @@
import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
+import android.tools.device.flicker.rules.ChangeDisplayOrientationRule
@Ignore("Base Test Class")
abstract class QuickSwitchBetweenTwoAppsForward(val rotation: Rotation = Rotation.ROTATION_0) {
@@ -46,7 +47,9 @@
tapl.setExpectedRotation(rotation.value)
testApp1.launchViaIntent(wmHelper)
+ ChangeDisplayOrientationRule.setRotation(rotation)
testApp2.launchViaIntent(wmHelper)
+ ChangeDisplayOrientationRule.setRotation(rotation)
tapl.launchedAppState.quickSwitchToPreviousApp()
wmHelper
.StateSyncBuilder()
diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp
index 96b685d..365e00e 100644
--- a/tests/Input/Android.bp
+++ b/tests/Input/Android.bp
@@ -26,6 +26,7 @@
"androidx.test.runner",
"androidx.test.uiautomator_uiautomator",
"servicestests-utils",
+ "flag-junit",
"frameworks-base-testutils",
"hamcrest-library",
"kotlin-test",
diff --git a/tests/Input/src/android/hardware/input/KeyboardLayoutPreviewTests.kt b/tests/Input/src/android/hardware/input/KeyboardLayoutPreviewTests.kt
new file mode 100644
index 0000000..3a2a3be
--- /dev/null
+++ b/tests/Input/src/android/hardware/input/KeyboardLayoutPreviewTests.kt
@@ -0,0 +1,69 @@
+/*
+ * 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.hardware.input
+
+import android.content.ContextWrapper
+import android.graphics.drawable.Drawable
+import android.platform.test.annotations.Presubmit
+import android.platform.test.flag.junit.SetFlagsRule
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.hardware.input.Flags
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNull
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.junit.MockitoJUnitRunner
+
+/**
+ * Tests for Keyboard layout preview
+ *
+ * Build/Install/Run:
+ * atest InputTests:KeyboardLayoutPreviewTests
+ */
+@Presubmit
+@RunWith(MockitoJUnitRunner::class)
+class KeyboardLayoutPreviewTests {
+
+ companion object {
+ const val WIDTH = 100
+ const val HEIGHT = 100
+ }
+
+ @get:Rule
+ val setFlagsRule = SetFlagsRule()
+
+ private fun createDrawable(): Drawable? {
+ val context = ContextWrapper(InstrumentationRegistry.getInstrumentation().getContext())
+ val inputManager = context.getSystemService(InputManager::class.java)!!
+ return inputManager.getKeyboardLayoutPreview(null, WIDTH, HEIGHT)
+ }
+
+ @Test
+ fun testKeyboardLayoutDrawable_hasCorrectDimensions() {
+ setFlagsRule.enableFlags(Flags.FLAG_KEYBOARD_LAYOUT_PREVIEW_FLAG)
+ val drawable = createDrawable()!!
+ assertEquals(WIDTH, drawable.intrinsicWidth)
+ assertEquals(HEIGHT, drawable.intrinsicHeight)
+ }
+
+ @Test
+ fun testKeyboardLayoutDrawable_isNull_ifFlagOff() {
+ setFlagsRule.disableFlags(Flags.FLAG_KEYBOARD_LAYOUT_PREVIEW_FLAG)
+ assertNull(createDrawable())
+ }
+}
\ No newline at end of file
diff --git a/tests/InputScreenshotTest/Android.bp b/tests/InputScreenshotTest/Android.bp
new file mode 100644
index 0000000..eee486f
--- /dev/null
+++ b/tests/InputScreenshotTest/Android.bp
@@ -0,0 +1,60 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+ name: "InputScreenshotTests",
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.kt",
+ ],
+ platform_apis: true,
+ certificate: "platform",
+ static_libs: [
+ "androidx.arch.core_core-testing",
+ "androidx.compose.ui_ui-test-junit4",
+ "androidx.compose.ui_ui-test-manifest",
+ "androidx.lifecycle_lifecycle-runtime-testing",
+ "androidx.compose.animation_animation",
+ "androidx.compose.material3_material3",
+ "androidx.compose.material_material-icons-extended",
+ "androidx.compose.runtime_runtime",
+ "androidx.compose.runtime_runtime-livedata",
+ "androidx.compose.ui_ui-tooling-preview",
+ "androidx.lifecycle_lifecycle-livedata-ktx",
+ "androidx.lifecycle_lifecycle-runtime-compose",
+ "androidx.navigation_navigation-compose",
+ "truth-prebuilt",
+ "androidx.compose.runtime_runtime",
+ "androidx.test.core",
+ "androidx.test.ext.junit",
+ "androidx.test.ext.truth",
+ "androidx.test.rules",
+ "androidx.test.runner",
+ "androidx.test.uiautomator_uiautomator",
+ "servicestests-utils",
+ "frameworks-base-testutils",
+ "platform-screenshot-diff-core",
+ "hamcrest-library",
+ "kotlin-test",
+ "flag-junit",
+ "platform-test-annotations",
+ "services.core.unboosted",
+ "testables",
+ "testng",
+ "truth-prebuilt",
+ ],
+ libs: [
+ "android.test.mock",
+ "android.test.base",
+ ],
+ test_suites: ["device-tests"],
+ compile_multilib: "both",
+ use_embedded_native_libs: false,
+ asset_dirs: ["assets"],
+}
diff --git a/tests/InputScreenshotTest/AndroidManifest.xml b/tests/InputScreenshotTest/AndroidManifest.xml
new file mode 100644
index 0000000..9ffbb3a
--- /dev/null
+++ b/tests/InputScreenshotTest/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.input.screenshot">
+
+ <uses-sdk android:minSdkVersion="21"/>
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:label="Screenshot tests for Input"
+ android:targetPackage="com.android.input.screenshot">
+ </instrumentation>
+</manifest>
diff --git a/tests/InputScreenshotTest/AndroidTest.xml b/tests/InputScreenshotTest/AndroidTest.xml
new file mode 100644
index 0000000..cc25fa4
--- /dev/null
+++ b/tests/InputScreenshotTest/AndroidTest.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+ -->
+<configuration description="Runs Input screendiff tests.">
+ <option name="test-suite-tag" value="apct-instrumentation" />
+ <option name="test-suite-tag" value="apct" />
+ <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+ <option name="optimized-property-setting" value="true" />
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="InputScreenshotTests.apk" />
+ </target_preparer>
+ <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+ <option name="directory-keys"
+ value="/data/user/0/com.android.input.screenshot/files/input_screenshots" />
+ <option name="collect-on-run-ended-only" value="true" />
+ </metrics_collector>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="com.android.input.screenshot" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ </test>
+</configuration>
diff --git a/tests/InputScreenshotTest/OWNERS b/tests/InputScreenshotTest/OWNERS
new file mode 100644
index 0000000..3cffce9
--- /dev/null
+++ b/tests/InputScreenshotTest/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 136048
+include /core/java/android/hardware/input/OWNERS
diff --git a/tests/InputScreenshotTest/TEST_MAPPING b/tests/InputScreenshotTest/TEST_MAPPING
new file mode 100644
index 0000000..727e609
--- /dev/null
+++ b/tests/InputScreenshotTest/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "postsubmit": [
+ {
+ "name": "InputScreenshotTests"
+ }
+ ]
+}
diff --git a/tests/InputScreenshotTest/assets/phone/light_landscape_layout-preview.png b/tests/InputScreenshotTest/assets/phone/light_landscape_layout-preview.png
new file mode 100644
index 0000000..70e4a71
--- /dev/null
+++ b/tests/InputScreenshotTest/assets/phone/light_landscape_layout-preview.png
Binary files differ
diff --git a/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-ansi.png b/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-ansi.png
new file mode 100644
index 0000000..502c1b4
--- /dev/null
+++ b/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-ansi.png
Binary files differ
diff --git a/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-jis.png b/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-jis.png
new file mode 100644
index 0000000..591b2fa
--- /dev/null
+++ b/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-jis.png
Binary files differ
diff --git a/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview.png b/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview.png
new file mode 100644
index 0000000..0137a85
--- /dev/null
+++ b/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview.png
Binary files differ
diff --git a/tests/InputScreenshotTest/assets/tablet/dark_portrait_layout-preview.png b/tests/InputScreenshotTest/assets/tablet/dark_portrait_layout-preview.png
new file mode 100644
index 0000000..37a91e1
--- /dev/null
+++ b/tests/InputScreenshotTest/assets/tablet/dark_portrait_layout-preview.png
Binary files differ
diff --git a/tests/InputScreenshotTest/src/android/input/screenshot/Bitmap.kt b/tests/InputScreenshotTest/src/android/input/screenshot/Bitmap.kt
new file mode 100644
index 0000000..84c971c
--- /dev/null
+++ b/tests/InputScreenshotTest/src/android/input/screenshot/Bitmap.kt
@@ -0,0 +1,66 @@
+/*
+ * 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 com.android.input.screenshot
+
+import android.graphics.Bitmap
+import android.graphics.Canvas
+import android.os.Build
+import android.view.View
+import platform.test.screenshot.matchers.MSSIMMatcher
+import platform.test.screenshot.matchers.PixelPerfectMatcher
+
+/** Draw this [View] into a [Bitmap]. */
+// TODO(b/195673633): Remove this once Compose screenshot tests use hardware rendering for their
+// tests.
+fun View.drawIntoBitmap(): Bitmap {
+ val bitmap =
+ Bitmap.createBitmap(
+ measuredWidth,
+ measuredHeight,
+ Bitmap.Config.ARGB_8888,
+ )
+ val canvas = Canvas(bitmap)
+ draw(canvas)
+ return bitmap
+}
+
+/**
+ * The [BitmapMatcher][platform.test.screenshot.matchers.BitmapMatcher] that should be used for
+ * screenshot *unit* tests.
+ */
+val UnitTestBitmapMatcher =
+ if (Build.CPU_ABI == "x86_64") {
+ // Different CPU architectures can sometimes end up rendering differently, so we can't do
+ // pixel-perfect matching on different architectures using the same golden. Given that our
+ // presubmits are run on cf_x86_64_phone, our goldens should be perfectly matched on the
+ // x86_64 architecture and use the Structural Similarity Index on others.
+ // TODO(b/237511747): Run our screenshot presubmit tests on arm64 instead so that we can
+ // do pixel perfect matching both at presubmit time and at development time with actual
+ // devices.
+ PixelPerfectMatcher()
+ } else {
+ MSSIMMatcher()
+ }
+
+/**
+ * The [BitmapMatcher][platform.test.screenshot.matchers.BitmapMatcher] that should be used for
+ * screenshot *unit* tests.
+ *
+ * We use the Structural Similarity Index for integration tests because they usually contain
+ * additional information and noise that shouldn't break the test.
+ */
+val IntegrationTestBitmapMatcher = MSSIMMatcher()
\ No newline at end of file
diff --git a/tests/InputScreenshotTest/src/android/input/screenshot/DefaultDeviceEmulationSpec.kt b/tests/InputScreenshotTest/src/android/input/screenshot/DefaultDeviceEmulationSpec.kt
new file mode 100644
index 0000000..edddc6b4
--- /dev/null
+++ b/tests/InputScreenshotTest/src/android/input/screenshot/DefaultDeviceEmulationSpec.kt
@@ -0,0 +1,75 @@
+/*
+ * 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 com.android.input.screenshot
+
+import platform.test.screenshot.DeviceEmulationSpec
+import platform.test.screenshot.DisplaySpec
+
+/**
+ * The emulations specs for all 8 permutations of:
+ * - phone or tablet.
+ * - dark of light mode.
+ * - portrait or landscape.
+ */
+val DeviceEmulationSpec.Companion.PhoneAndTabletFull
+ get() = PhoneAndTabletFullSpec
+
+private val PhoneAndTabletFullSpec =
+ DeviceEmulationSpec.forDisplays(Displays.Phone, Displays.Tablet)
+
+/**
+ * The emulations specs of:
+ * - phone + light mode + portrait.
+ * - phone + light mode + landscape.
+ * - tablet + dark mode + portrait.
+ *
+ * This allows to test the most important permutations of a screen/layout with only 3
+ * configurations.
+ */
+val DeviceEmulationSpec.Companion.PhoneAndTabletMinimal
+ get() = PhoneAndTabletMinimalSpec
+
+private val PhoneAndTabletMinimalSpec =
+ DeviceEmulationSpec.forDisplays(Displays.Phone, isDarkTheme = false) +
+ DeviceEmulationSpec.forDisplays(Displays.Tablet, isDarkTheme = true, isLandscape = false)
+
+/**
+ * This allows to test only single most important configuration.
+ */
+val DeviceEmulationSpec.Companion.PhoneMinimal
+ get() = PhoneMinimalSpec
+
+private val PhoneMinimalSpec =
+ DeviceEmulationSpec.forDisplays(Displays.Phone, isDarkTheme = false, isLandscape = false)
+
+object Displays {
+ val Phone =
+ DisplaySpec(
+ "phone",
+ width = 1440,
+ height = 3120,
+ densityDpi = 560,
+ )
+
+ val Tablet =
+ DisplaySpec(
+ "tablet",
+ width = 2560,
+ height = 1600,
+ densityDpi = 320,
+ )
+}
\ No newline at end of file
diff --git a/tests/InputScreenshotTest/src/android/input/screenshot/InputGoldenImagePathManager.kt b/tests/InputScreenshotTest/src/android/input/screenshot/InputGoldenImagePathManager.kt
new file mode 100644
index 0000000..8faf224
--- /dev/null
+++ b/tests/InputScreenshotTest/src/android/input/screenshot/InputGoldenImagePathManager.kt
@@ -0,0 +1,44 @@
+/*
+ * 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 com.android.input.screenshot
+
+import androidx.test.platform.app.InstrumentationRegistry
+import platform.test.screenshot.GoldenImagePathManager
+import platform.test.screenshot.PathConfig
+
+/** A [GoldenImagePathManager] that should be used for all Input screenshot tests. */
+class InputGoldenImagePathManager(
+ pathConfig: PathConfig,
+ assetsPathRelativeToBuildRoot: String
+) :
+ GoldenImagePathManager(
+ appContext = InstrumentationRegistry.getInstrumentation().context,
+ assetsPathRelativeToBuildRoot = assetsPathRelativeToBuildRoot,
+ deviceLocalPath =
+ InstrumentationRegistry.getInstrumentation()
+ .targetContext
+ .filesDir
+ .absolutePath
+ .toString() + "/input_screenshots",
+ pathConfig = pathConfig,
+ ) {
+ override fun toString(): String {
+ // This string is appended to all actual/expected screenshots on the device, so make sure
+ // it is a static value.
+ return "InputGoldenImagePathManager"
+ }
+}
\ No newline at end of file
diff --git a/tests/InputScreenshotTest/src/android/input/screenshot/InputScreenshotTestRule.kt b/tests/InputScreenshotTest/src/android/input/screenshot/InputScreenshotTestRule.kt
new file mode 100644
index 0000000..c2c3d55
--- /dev/null
+++ b/tests/InputScreenshotTest/src/android/input/screenshot/InputScreenshotTestRule.kt
@@ -0,0 +1,87 @@
+/*
+ * 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 com.android.input.screenshot
+
+import android.content.Context
+import android.graphics.Bitmap
+import androidx.activity.ComponentActivity
+import androidx.compose.foundation.Image
+import androidx.compose.ui.platform.ViewRootForTest
+import androidx.compose.ui.test.junit4.createAndroidComposeRule
+import androidx.compose.ui.test.onRoot
+import androidx.compose.ui.graphics.asImageBitmap
+import org.junit.rules.RuleChain
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+import platform.test.screenshot.DeviceEmulationRule
+import platform.test.screenshot.DeviceEmulationSpec
+import platform.test.screenshot.MaterialYouColorsRule
+import platform.test.screenshot.ScreenshotTestRule
+import platform.test.screenshot.getEmulatedDevicePathConfig
+
+/** A rule for Input screenshot diff tests. */
+class InputScreenshotTestRule(
+ emulationSpec: DeviceEmulationSpec,
+ assetsPathRelativeToBuildRoot: String
+) : TestRule {
+ private val colorsRule = MaterialYouColorsRule()
+ private val deviceEmulationRule = DeviceEmulationRule(emulationSpec)
+ private val screenshotRule =
+ ScreenshotTestRule(
+ InputGoldenImagePathManager(
+ getEmulatedDevicePathConfig(emulationSpec),
+ assetsPathRelativeToBuildRoot
+ )
+ )
+ private val composeRule = createAndroidComposeRule<ComponentActivity>()
+ private val delegateRule =
+ RuleChain.outerRule(colorsRule)
+ .around(deviceEmulationRule)
+ .around(screenshotRule)
+ .around(composeRule)
+ private val matcher = UnitTestBitmapMatcher
+
+ override fun apply(base: Statement, description: Description): Statement {
+ return delegateRule.apply(base, description)
+ }
+
+ /**
+ * Compare [content] with the golden image identified by [goldenIdentifier].
+ */
+ fun screenshotTest(
+ goldenIdentifier: String,
+ content: (Context) -> Bitmap,
+ ) {
+ // Make sure that the activity draws full screen and fits the whole display.
+ val activity = composeRule.activity
+ activity.mainExecutor.execute { activity.window.setDecorFitsSystemWindows(false) }
+
+ // Set the content using the AndroidComposeRule to make sure that the Activity is set up
+ // correctly.
+ composeRule.setContent {
+ Image(
+ bitmap = content(activity).asImageBitmap(),
+ contentDescription = null,
+ )
+ }
+ composeRule.waitForIdle()
+
+ val view = (composeRule.onRoot().fetchSemanticsNode().root as ViewRootForTest).view
+ screenshotRule.assertBitmapAgainstGolden(view.drawIntoBitmap(), goldenIdentifier, matcher)
+ }
+}
\ No newline at end of file
diff --git a/tests/InputScreenshotTest/src/android/input/screenshot/KeyboardLayoutPreviewAnsiScreenshotTest.kt b/tests/InputScreenshotTest/src/android/input/screenshot/KeyboardLayoutPreviewAnsiScreenshotTest.kt
new file mode 100644
index 0000000..e855786
--- /dev/null
+++ b/tests/InputScreenshotTest/src/android/input/screenshot/KeyboardLayoutPreviewAnsiScreenshotTest.kt
@@ -0,0 +1,70 @@
+/*
+ * 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 com.android.input.screenshot
+
+import android.content.Context
+import android.hardware.input.KeyboardLayout
+import android.os.LocaleList
+import android.platform.test.flag.junit.SetFlagsRule
+import com.android.hardware.input.Flags
+import java.util.Locale
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.RuleChain
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import platform.test.screenshot.DeviceEmulationSpec
+
+/** A screenshot test for Keyboard layout preview for Ansi physical layout. */
+@RunWith(Parameterized::class)
+class KeyboardLayoutPreviewAnsiScreenshotTest(emulationSpec: DeviceEmulationSpec) {
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getTestSpecs() = DeviceEmulationSpec.PhoneMinimal
+ }
+
+ val setFlagsRule = SetFlagsRule()
+ val screenshotRule = InputScreenshotTestRule(
+ emulationSpec,
+ "frameworks/base/tests/InputScreenshotTest/assets"
+ )
+
+ @get:Rule
+ val ruleChain = RuleChain.outerRule(screenshotRule).around(setFlagsRule)
+
+ @Test
+ fun test() {
+ setFlagsRule.enableFlags(Flags.FLAG_KEYBOARD_LAYOUT_PREVIEW_FLAG)
+ screenshotRule.screenshotTest("layout-preview-ansi") {
+ context: Context -> LayoutPreview.createLayoutPreview(
+ context,
+ KeyboardLayout(
+ "descriptor",
+ "layout",
+ /* collection= */null,
+ /* priority= */0,
+ LocaleList(Locale.US),
+ /* layoutType= */0,
+ /* vid= */0,
+ /* pid= */0
+ )
+ )
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/tests/InputScreenshotTest/src/android/input/screenshot/KeyboardLayoutPreviewIsoScreenshotTest.kt b/tests/InputScreenshotTest/src/android/input/screenshot/KeyboardLayoutPreviewIsoScreenshotTest.kt
new file mode 100644
index 0000000..8ae6dfd
--- /dev/null
+++ b/tests/InputScreenshotTest/src/android/input/screenshot/KeyboardLayoutPreviewIsoScreenshotTest.kt
@@ -0,0 +1,58 @@
+/*
+ * 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 com.android.input.screenshot
+
+import android.content.Context
+import android.hardware.input.KeyboardLayout
+import android.os.LocaleList
+import android.platform.test.flag.junit.SetFlagsRule
+import com.android.hardware.input.Flags
+import java.util.Locale
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.RuleChain
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import platform.test.screenshot.DeviceEmulationSpec
+
+/** A screenshot test for Keyboard layout preview for Iso physical layout. */
+@RunWith(Parameterized::class)
+class KeyboardLayoutPreviewIsoScreenshotTest(emulationSpec: DeviceEmulationSpec) {
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getTestSpecs() = DeviceEmulationSpec.PhoneAndTabletMinimal
+ }
+
+ val setFlagsRule = SetFlagsRule()
+ val screenshotRule = InputScreenshotTestRule(
+ emulationSpec,
+ "frameworks/base/tests/InputScreenshotTest/assets"
+ )
+
+ @get:Rule
+ val ruleChain = RuleChain.outerRule(screenshotRule).around(setFlagsRule)
+
+ @Test
+ fun test() {
+ setFlagsRule.enableFlags(Flags.FLAG_KEYBOARD_LAYOUT_PREVIEW_FLAG)
+ screenshotRule.screenshotTest("layout-preview") {
+ context: Context -> LayoutPreview.createLayoutPreview(context, null)
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/tests/InputScreenshotTest/src/android/input/screenshot/KeyboardLayoutPreviewJisScreenshotTest.kt b/tests/InputScreenshotTest/src/android/input/screenshot/KeyboardLayoutPreviewJisScreenshotTest.kt
new file mode 100644
index 0000000..5231c14
--- /dev/null
+++ b/tests/InputScreenshotTest/src/android/input/screenshot/KeyboardLayoutPreviewJisScreenshotTest.kt
@@ -0,0 +1,70 @@
+/*
+ * 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 com.android.input.screenshot
+
+import android.content.Context
+import android.hardware.input.KeyboardLayout
+import android.os.LocaleList
+import android.platform.test.flag.junit.SetFlagsRule
+import com.android.hardware.input.Flags
+import java.util.Locale
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.RuleChain
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import platform.test.screenshot.DeviceEmulationSpec
+
+/** A screenshot test for Keyboard layout preview for JIS physical layout. */
+@RunWith(Parameterized::class)
+class KeyboardLayoutPreviewJisScreenshotTest(emulationSpec: DeviceEmulationSpec) {
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getTestSpecs() = DeviceEmulationSpec.PhoneMinimal
+ }
+
+ val setFlagsRule = SetFlagsRule()
+ val screenshotRule = InputScreenshotTestRule(
+ emulationSpec,
+ "frameworks/base/tests/InputScreenshotTest/assets"
+ )
+
+ @get:Rule
+ val ruleChain = RuleChain.outerRule(screenshotRule).around(setFlagsRule)
+
+ @Test
+ fun test() {
+ setFlagsRule.enableFlags(Flags.FLAG_KEYBOARD_LAYOUT_PREVIEW_FLAG)
+ screenshotRule.screenshotTest("layout-preview-jis") {
+ context: Context -> LayoutPreview.createLayoutPreview(
+ context,
+ KeyboardLayout(
+ "descriptor",
+ "layout",
+ /* collection= */null,
+ /* priority= */0,
+ LocaleList(Locale.JAPAN),
+ /* layoutType= */0,
+ /* vid= */0,
+ /* pid= */0
+ )
+ )
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/tests/InputScreenshotTest/src/android/input/screenshot/LayoutPreview.kt b/tests/InputScreenshotTest/src/android/input/screenshot/LayoutPreview.kt
new file mode 100644
index 0000000..76ee379
--- /dev/null
+++ b/tests/InputScreenshotTest/src/android/input/screenshot/LayoutPreview.kt
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+import android.content.Context
+import android.graphics.Bitmap
+import android.graphics.Canvas
+import android.hardware.input.InputManager
+import android.hardware.input.KeyboardLayout
+import android.util.TypedValue
+import kotlin.math.roundToInt
+
+object LayoutPreview {
+ fun createLayoutPreview(context: Context, layout: KeyboardLayout?): Bitmap {
+ val im = context.getSystemService(InputManager::class.java)!!
+ val width = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+ 600.0F, context.getResources().getDisplayMetrics()).roundToInt()
+ val height = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+ 200.0F, context.getResources().getDisplayMetrics()).roundToInt()
+ val drawable = im.getKeyboardLayoutPreview(layout, width, height)!!
+ val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
+ val canvas = Canvas(bitmap)
+ drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight())
+ drawable.draw(canvas)
+ return bitmap
+ }
+}
\ No newline at end of file
diff --git a/tools/lint/fix/soong_lint_fix.py b/tools/lint/fix/soong_lint_fix.py
index acc0ad0..b42f9ee 100644
--- a/tools/lint/fix/soong_lint_fix.py
+++ b/tools/lint/fix/soong_lint_fix.py
@@ -64,27 +64,27 @@
class SoongLintFix:
"""
- This class creates a command line tool that will
- apply lint fixes to the platform via the necessary
- combination of soong and shell commands.
+ This class creates a command line tool that will apply lint fixes to the
+ platform via the necessary combination of soong and shell commands.
- It breaks up these operations into a few "private" methods
- that are intentionally exposed so experimental code can tweak behavior.
+ It breaks up these operations into a few "private" methods that are
+ intentionally exposed so experimental code can tweak behavior.
- The entry point, `run`, will apply lint fixes using the
- intermediate `suggested-fixes` directory that soong creates during its
- invocation of lint.
+ The entry point, `run`, will apply lint fixes using the intermediate
+ `suggested-fixes` directory that soong creates during its invocation of
+ lint.
Basic usage:
```
from soong_lint_fix import SoongLintFix
- SoongLintFix().run()
+ opts = SoongLintFixOptions()
+ opts.parse_args(sys.argv)
+ SoongLintFix(opts).run()
```
"""
- def __init__(self):
- self._parser = _setup_parser()
- self._args = None
+ def __init__(self, opts):
+ self._opts = opts
self._kwargs = None
self._modules = []
@@ -96,19 +96,18 @@
self._find_modules()
self._lint()
- if not self._args.no_fix:
+ if not self._opts.no_fix:
self._fix()
- if self._args.print:
+ if self._opts.print:
self._print()
def _setup(self):
- self._args = self._parser.parse_args()
env = os.environ.copy()
- if self._args.check:
- env["ANDROID_LINT_CHECK"] = self._args.check
- if self._args.lint_module:
- env["ANDROID_LINT_CHECK_EXTRA_MODULES"] = self._args.lint_module
+ if self._opts.check:
+ env["ANDROID_LINT_CHECK"] = self._opts.check
+ if self._opts.lint_module:
+ env["ANDROID_LINT_CHECK_EXTRA_MODULES"] = self._opts.lint_module
self._kwargs = {
"env": env,
@@ -131,7 +130,7 @@
with open(f"{ANDROID_PRODUCT_OUT}/module-info.json") as f:
module_info = json.load(f)
- for module_name in self._args.modules:
+ for module_name in self._opts.modules:
module = SoongModule(module_name)
module.find(module_info)
self._modules.append(module)
@@ -169,6 +168,20 @@
print(f.read())
+class SoongLintFixOptions:
+ """Options for SoongLintFix"""
+
+ def __init__(self):
+ self.modules = []
+ self.check = None
+ self.lint_module = None
+ self.no_fix = False
+ self.print = False
+
+ def parse_args(self, args=None):
+ _setup_parser().parse_args(args, self)
+
+
def _setup_parser():
parser = argparse.ArgumentParser(description="""
This is a python script that applies lint fixes to the platform:
@@ -199,4 +212,6 @@
return parser
if __name__ == "__main__":
- SoongLintFix().run()
+ opts = SoongLintFixOptions()
+ opts.parse_args(sys.argv)
+ SoongLintFix(opts).run()
diff --git a/tools/lint/utils/Android.bp b/tools/lint/utils/Android.bp
index 75e8d68..439c86d 100644
--- a/tools/lint/utils/Android.bp
+++ b/tools/lint/utils/Android.bp
@@ -43,3 +43,9 @@
"AndroidUtilsLintChecker",
],
}
+
+python_binary_host {
+ name: "enforce_permission_counter",
+ srcs: ["enforce_permission_counter.py"],
+ libs: ["soong_lint_fix"],
+}
diff --git a/tools/lint/utils/enforce_permission_counter.py b/tools/lint/utils/enforce_permission_counter.py
new file mode 100644
index 0000000..b5c2ffe
--- /dev/null
+++ b/tools/lint/utils/enforce_permission_counter.py
@@ -0,0 +1,82 @@
+# 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.
+
+import re
+
+import soong_lint_fix
+
+# Libraries that constitute system_server.
+# It is non-trivial to keep in sync with services/Android.bp as some
+# module are post-processed (e.g, services.core).
+TARGETS = [
+ "services.core.unboosted",
+ "services.accessibility",
+ "services.appprediction",
+ "services.appwidget",
+ "services.autofill",
+ "services.backup",
+ "services.companion",
+ "services.contentcapture",
+ "services.contentsuggestions",
+ "services.coverage",
+ "services.devicepolicy",
+ "services.midi",
+ "services.musicsearch",
+ "services.net",
+ "services.people",
+ "services.print",
+ "services.profcollect",
+ "services.restrictions",
+ "services.searchui",
+ "services.smartspace",
+ "services.systemcaptions",
+ "services.translation",
+ "services.texttospeech",
+ "services.usage",
+ "services.usb",
+ "services.voiceinteraction",
+ "services.wallpapereffectsgeneration",
+ "services.wifi",
+]
+
+
+class EnforcePermissionMigratedCounter:
+ """Wrapper around lint_fix to count the number of AIDL methods annotated."""
+ def run(self):
+ opts = soong_lint_fix.SoongLintFixOptions()
+ opts.check = "AnnotatedAidlCounter"
+ opts.lint_module = "AndroidUtilsLintChecker"
+ opts.no_fix = True
+ opts.modules = TARGETS
+
+ self.linter = soong_lint_fix.SoongLintFix(opts)
+ self.linter.run()
+ self.parse_lint_reports()
+
+ def parse_lint_reports(self):
+ counts = { "unannotated": 0, "enforced": 0, "notRequired": 0 }
+ for module in self.linter._modules:
+ with open(module.lint_report, "r") as f:
+ content = f.read()
+ keys = dict(re.findall(r'(\w+)=(\d+)', content))
+ for key in keys:
+ counts[key] += int(keys[key])
+ print(counts)
+ total = sum(counts.values())
+ annotated_percent = (1 - (counts["unannotated"] / total)) * 100
+ print("Annotated methods = %.2f%%" % (annotated_percent))
+
+
+if __name__ == "__main__":
+ EnforcePermissionMigratedCounter().run()