Merge "Log debug and trace tags to statsd." into main
diff --git a/apex/jobscheduler/framework/aconfig/job.aconfig b/apex/jobscheduler/framework/aconfig/job.aconfig
index e73b434..788e824 100644
--- a/apex/jobscheduler/framework/aconfig/job.aconfig
+++ b/apex/jobscheduler/framework/aconfig/job.aconfig
@@ -13,3 +13,10 @@
description: "Add APIs to let apps attach debug information to jobs"
bug: "293491637"
}
+
+flag {
+ name: "backup_jobs_exemption"
+ namespace: "backstage_power"
+ description: "Introduce a new RUN_BACKUP_JOBS permission and exemption logic allowing for longer running jobs for apps whose primary purpose is to backup or sync content."
+ bug: "318731461"
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index d53f480..fc193d8 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -5452,6 +5452,9 @@
pw.print(Flags.FLAG_THROW_ON_UNSUPPORTED_BIAS_USAGE,
Flags.throwOnUnsupportedBiasUsage());
pw.println();
+ pw.print(android.app.job.Flags.FLAG_BACKUP_JOBS_EXEMPTION,
+ android.app.job.Flags.backupJobsExemption());
+ pw.println();
pw.decreaseIndent();
pw.println();
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java
index 6f2393a..0cf6a7a 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java
@@ -356,6 +356,9 @@
case com.android.server.job.Flags.FLAG_THROW_ON_UNSUPPORTED_BIAS_USAGE:
pw.println(com.android.server.job.Flags.throwOnUnsupportedBiasUsage());
break;
+ case android.app.job.Flags.FLAG_BACKUP_JOBS_EXEMPTION:
+ pw.println(android.app.job.Flags.backupJobsExemption());
+ break;
default:
pw.println("Unknown flag: " + flagName);
break;
diff --git a/core/api/Android.bp b/core/api/Android.bp
index 907916a..8d8a82b 100644
--- a/core/api/Android.bp
+++ b/core/api/Android.bp
@@ -96,21 +96,3 @@
name: "non-updatable-test-lint-baseline.txt",
srcs: ["test-lint-baseline.txt"],
}
-
-java_api_contribution {
- name: "api-stubs-docs-non-updatable-public-stubs",
- api_surface: "public",
- api_file: "current.txt",
- visibility: [
- "//build/orchestrator/apis",
- ],
-}
-
-java_api_contribution {
- name: "frameworks-base-core-api-module-lib-stubs",
- api_surface: "module-lib",
- api_file: "module-lib-current.txt",
- visibility: [
- "//build/orchestrator/apis",
- ],
-}
diff --git a/core/api/current.txt b/core/api/current.txt
index e5a9506..9e3919d 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -277,6 +277,7 @@
field @FlaggedApi("android.companion.flags.device_presence") public static final String REQUEST_OBSERVE_DEVICE_UUID_PRESENCE = "android.permission.REQUEST_OBSERVE_DEVICE_UUID_PRESENCE";
field public static final String REQUEST_PASSWORD_COMPLEXITY = "android.permission.REQUEST_PASSWORD_COMPLEXITY";
field @Deprecated public static final String RESTART_PACKAGES = "android.permission.RESTART_PACKAGES";
+ field @FlaggedApi("android.app.job.backup_jobs_exemption") public static final String RUN_BACKUP_JOBS = "android.permission.RUN_BACKUP_JOBS";
field public static final String RUN_USER_INITIATED_JOBS = "android.permission.RUN_USER_INITIATED_JOBS";
field public static final String SCHEDULE_EXACT_ALARM = "android.permission.SCHEDULE_EXACT_ALARM";
field public static final String SEND_RESPOND_VIA_MESSAGE = "android.permission.SEND_RESPOND_VIA_MESSAGE";
@@ -284,6 +285,7 @@
field public static final String SET_ALARM = "com.android.alarm.permission.SET_ALARM";
field public static final String SET_ALWAYS_FINISH = "android.permission.SET_ALWAYS_FINISH";
field public static final String SET_ANIMATION_SCALE = "android.permission.SET_ANIMATION_SCALE";
+ field @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") public static final String SET_BIOMETRIC_DIALOG_LOGO = "android.permission.SET_BIOMETRIC_DIALOG_LOGO";
field public static final String SET_DEBUG_APP = "android.permission.SET_DEBUG_APP";
field @Deprecated public static final String SET_PREFERRED_APPLICATIONS = "android.permission.SET_PREFERRED_APPLICATIONS";
field public static final String SET_PROCESS_LIMIT = "android.permission.SET_PROCESS_LIMIT";
@@ -18722,8 +18724,8 @@
method @Nullable public int getAllowedAuthenticators();
method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @Nullable public android.hardware.biometrics.PromptContentView getContentView();
method @Nullable public CharSequence getDescription();
- method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @Nullable @RequiresPermission("android.permission.MANAGE_BIOMETRIC_DIALOG") public android.graphics.Bitmap getLogoBitmap();
- method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @DrawableRes @RequiresPermission("android.permission.MANAGE_BIOMETRIC_DIALOG") public int getLogoRes();
+ method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @Nullable @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_LOGO) public android.graphics.Bitmap getLogoBitmap();
+ method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @DrawableRes @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_LOGO) public int getLogoRes();
method @Nullable public CharSequence getNegativeButtonText();
method @Nullable public CharSequence getSubtitle();
method @NonNull public CharSequence getTitle();
@@ -18773,8 +18775,8 @@
method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setContentView(@NonNull android.hardware.biometrics.PromptContentView);
method @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setDescription(@NonNull CharSequence);
method @Deprecated @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setDeviceCredentialAllowed(boolean);
- method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @NonNull @RequiresPermission("android.permission.MANAGE_BIOMETRIC_DIALOG") public android.hardware.biometrics.BiometricPrompt.Builder setLogo(@DrawableRes int);
- method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @NonNull @RequiresPermission("android.permission.MANAGE_BIOMETRIC_DIALOG") public android.hardware.biometrics.BiometricPrompt.Builder setLogo(@NonNull android.graphics.Bitmap);
+ method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @NonNull @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_LOGO) public android.hardware.biometrics.BiometricPrompt.Builder setLogoBitmap(@NonNull android.graphics.Bitmap);
+ method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @NonNull @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_LOGO) public android.hardware.biometrics.BiometricPrompt.Builder setLogoRes(@DrawableRes int);
method @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setNegativeButton(@NonNull CharSequence, @NonNull java.util.concurrent.Executor, @NonNull android.content.DialogInterface.OnClickListener);
method @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setSubtitle(@NonNull CharSequence);
method @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setTitle(@NonNull CharSequence);
@@ -24299,7 +24301,7 @@
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("com.android.media.flags.enable_cross_user_routing_in_media_router2") @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MEDIA_CONTENT_CONTROL, android.Manifest.permission.MEDIA_ROUTING_CONTROL}) public static android.media.MediaRouter2 getInstance(@NonNull android.content.Context, @NonNull String, @NonNull android.os.UserHandle);
+ method @FlaggedApi("com.android.media.flags.enable_cross_user_routing_in_media_router2") @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MEDIA_CONTENT_CONTROL, android.Manifest.permission.MEDIA_ROUTING_CONTROL}) public static android.media.MediaRouter2 getInstance(@NonNull android.content.Context, @NonNull String);
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();
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 5920c6b..ea008ac 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -871,6 +871,10 @@
field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.PackageOps> CREATOR;
}
+ @FlaggedApi("android.app.bic_client") public final class BackgroundInstallControlManager {
+ method @FlaggedApi("android.app.bic_client") @NonNull @RequiresPermission(android.Manifest.permission.GET_BACKGROUND_INSTALLED_PACKAGES) public java.util.List<android.content.pm.PackageInfo> getBackgroundInstalledPackages(long);
+ }
+
public class BroadcastOptions {
method public void clearRequireCompatChange();
method public int getPendingIntentBackgroundActivityStartMode();
@@ -3388,11 +3392,10 @@
}
@FlaggedApi("android.companion.virtual.flags.virtual_camera") public static final class VirtualCameraConfig.Builder {
- ctor public VirtualCameraConfig.Builder();
+ ctor public VirtualCameraConfig.Builder(@NonNull String);
method @NonNull public android.companion.virtual.camera.VirtualCameraConfig.Builder addStreamConfig(@IntRange(from=1) int, @IntRange(from=1) int, int, @IntRange(from=1) int);
method @NonNull public android.companion.virtual.camera.VirtualCameraConfig build();
method @NonNull public android.companion.virtual.camera.VirtualCameraConfig.Builder setLensFacing(int);
- method @NonNull public android.companion.virtual.camera.VirtualCameraConfig.Builder setName(@NonNull String);
method @NonNull public android.companion.virtual.camera.VirtualCameraConfig.Builder setSensorOrientation(int);
method @NonNull public android.companion.virtual.camera.VirtualCameraConfig.Builder setVirtualCameraCallback(@NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.camera.VirtualCameraCallback);
}
@@ -6886,7 +6889,6 @@
public final class MediaRouter2 {
method @NonNull public java.util.List<android.media.MediaRoute2Info> getAllRoutes();
method @Nullable public String getClientPackageName();
- method @Nullable @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public static android.media.MediaRouter2 getInstance(@NonNull android.content.Context, @NonNull String);
method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void setRouteVolume(@NonNull android.media.MediaRoute2Info, int);
method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void startScan();
method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void stopScan();
@@ -14774,6 +14776,7 @@
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean matchesCurrentSimOperator(@NonNull String, int, @Nullable String);
method public boolean needsOtaServiceProvisioning();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyOtaEmergencyNumberDbInstalled();
+ method @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") @RequiresPermission(android.Manifest.permission.DUMP) public void persistEmergencyCallDiagnosticData(@NonNull String, @NonNull android.telephony.TelephonyManager.EmergencyCallDiagnosticParams);
method @RequiresPermission(android.Manifest.permission.REBOOT) public int prepareForUnattendedReboot();
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean rebootRadio();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerCarrierPrivilegesCallback(int, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CarrierPrivilegesCallback);
@@ -14952,6 +14955,21 @@
method public default void onCarrierServiceChanged(@Nullable String, int);
}
+ @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") public static final class TelephonyManager.EmergencyCallDiagnosticParams {
+ method public long getLogcatCollectionStartTimeMillis();
+ method public boolean isLogcatCollectionEnabled();
+ method public boolean isTelecomDumpSysCollectionEnabled();
+ method public boolean isTelephonyDumpSysCollectionEnabled();
+ }
+
+ public static final class TelephonyManager.EmergencyCallDiagnosticParams.Builder {
+ ctor public TelephonyManager.EmergencyCallDiagnosticParams.Builder();
+ method @NonNull public android.telephony.TelephonyManager.EmergencyCallDiagnosticParams build();
+ method @NonNull public android.telephony.TelephonyManager.EmergencyCallDiagnosticParams.Builder setLogcatCollectionStartTimeMillis(long);
+ method @NonNull public android.telephony.TelephonyManager.EmergencyCallDiagnosticParams.Builder setTelecomDumpSysCollectionEnabled(boolean);
+ method @NonNull public android.telephony.TelephonyManager.EmergencyCallDiagnosticParams.Builder setTelephonyDumpSysCollectionEnabled(boolean);
+ }
+
public static class TelephonyManager.ModemActivityInfoException extends java.lang.Exception {
ctor public TelephonyManager.ModemActivityInfoException(int);
method public int getErrorCode();
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index ccd8456..00c4b0f 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1548,9 +1548,16 @@
public static final int OP_READ_SYSTEM_GRAMMATICAL_GENDER =
AppProtoEnums.APP_OP_READ_SYSTEM_GRAMMATICAL_GENDER;
+ /**
+ * Allows an app whose primary use case is to backup or sync content to run longer jobs.
+ *
+ * @hide
+ */
+ public static final int OP_RUN_BACKUP_JOBS = AppProtoEnums.APP_OP_RUN_BACKUP_JOBS;
+
/** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public static final int _NUM_OP = 144;
+ public static final int _NUM_OP = 145;
/**
* All app ops represented as strings.
@@ -1700,6 +1707,7 @@
OPSTR_ENABLE_MOBILE_DATA_BY_USER,
OPSTR_RESERVED_FOR_TESTING,
OPSTR_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER,
+ OPSTR_RUN_BACKUP_JOBS,
})
public @interface AppOpString {}
@@ -2392,6 +2400,13 @@
public static final String OPSTR_READ_SYSTEM_GRAMMATICAL_GENDER =
"android:read_system_grammatical_gender";
+ /**
+ * Allows an app whose primary use case is to backup or sync content to run longer jobs.
+ *
+ * @hide
+ */
+ public static final String OPSTR_RUN_BACKUP_JOBS = "android:run_backup_jobs";
+
/** {@link #sAppOpsToNote} not initialized yet for this op */
private static final byte SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED = 0;
/** Should not collect noting of this app-op in {@link #sAppOpsToNote} */
@@ -2504,6 +2519,7 @@
OP_RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA,
OP_MEDIA_ROUTING_CONTROL,
OP_READ_SYSTEM_GRAMMATICAL_GENDER,
+ OP_RUN_BACKUP_JOBS,
};
static final AppOpInfo[] sAppOpInfos = new AppOpInfo[]{
@@ -2961,6 +2977,8 @@
// will make it an app-op permission in the future.
// .setPermission(Manifest.permission.READ_SYSTEM_GRAMMATICAL_GENDER)
.build(),
+ new AppOpInfo.Builder(OP_RUN_BACKUP_JOBS, OPSTR_RUN_BACKUP_JOBS, "RUN_BACKUP_JOBS")
+ .setPermission(Manifest.permission.RUN_BACKUP_JOBS).build(),
};
// The number of longs needed to form a full bitmask of app ops
diff --git a/core/java/android/app/BackgroundInstallControlManager.java b/core/java/android/app/BackgroundInstallControlManager.java
new file mode 100644
index 0000000..664fceb
--- /dev/null
+++ b/core/java/android/app/BackgroundInstallControlManager.java
@@ -0,0 +1,102 @@
+/*
+ * 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;
+
+import static android.Manifest.permission.GET_BACKGROUND_INSTALLED_PACKAGES;
+import static android.annotation.SystemApi.Client.PRIVILEGED_APPS;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.content.pm.IBackgroundInstallControlService;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
+import java.util.List;
+
+/**
+ * BackgroundInstallControlManager client allows apps to query apps installed in background.
+ *
+ * <p>Any applications that was installed without an accompanying installer UI activity paired
+ * with recorded user interaction event is considered background installed. This is determined by
+ * analysis of user-activity logs.
+ *
+ * <p>Warning: BackgroundInstallControl should not be considered a definitive
+ * authority of identifying background installed applications. Consumers can use this as a
+ * supplementary signal, but must perform additional due diligence to confirm the install nature
+ * of the package.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_BIC_CLIENT)
+@SystemApi(client = PRIVILEGED_APPS)
+@SystemService(Context.BACKGROUND_INSTALL_CONTROL_SERVICE)
+public final class BackgroundInstallControlManager {
+
+ private static final String TAG = "BackgroundInstallControlManager";
+ private static IBackgroundInstallControlService sService;
+ private final Context mContext;
+
+ BackgroundInstallControlManager(Context context) {
+ mContext = context;
+ }
+
+ private static IBackgroundInstallControlService getService() {
+ if (sService == null) {
+ sService =
+ IBackgroundInstallControlService.Stub.asInterface(
+ ServiceManager.getService(Context.BACKGROUND_INSTALL_CONTROL_SERVICE));
+ }
+ return sService;
+ }
+
+ /**
+ * Returns a full list of {@link PackageInfo} of apps currently installed for the current user
+ * that are considered installed in the background.
+ *
+ * <p>Refer to top level doc {@link BackgroundInstallControlManager} for more details on
+ * background-installed applications.
+ * <p>
+ *
+ * @param flags - Flags will be used to call
+ * {@link PackageManager#getInstalledPackages(PackageInfoFlags)} to retrieve installed packages.
+ * @return A list of packages retrieved from {@link PackageManager} with non-background
+ * installed app filter applied.
+ *
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_BIC_CLIENT)
+ @SystemApi
+ @RequiresPermission(GET_BACKGROUND_INSTALLED_PACKAGES)
+ public @NonNull List<PackageInfo> getBackgroundInstalledPackages(
+ @PackageManager.PackageInfoFlagsBits long flags) {
+ List<PackageInfo> backgroundInstalledPackages;
+ try {
+ return getService()
+ .getBackgroundInstalledPackages(flags, mContext.getUserId())
+ .getList();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+}
diff --git a/core/java/android/app/HomeVisibilityListener.java b/core/java/android/app/HomeVisibilityListener.java
index 1f5f2e4..5dd7ab0 100644
--- a/core/java/android/app/HomeVisibilityListener.java
+++ b/core/java/android/app/HomeVisibilityListener.java
@@ -69,6 +69,11 @@
public HomeVisibilityListener() {
mObserver = new android.app.IProcessObserver.Stub() {
@Override
+ public void onProcessStarted(int pid, int processUid, int packageUid,
+ String packageName, String processName) {
+ }
+
+ @Override
public void onForegroundActivitiesChanged(int pid, int uid, boolean fg) {
refreshHomeVisibility();
}
diff --git a/core/java/android/app/IProcessObserver.aidl b/core/java/android/app/IProcessObserver.aidl
index 7be3620..5c5e72c 100644
--- a/core/java/android/app/IProcessObserver.aidl
+++ b/core/java/android/app/IProcessObserver.aidl
@@ -18,6 +18,17 @@
/** {@hide} */
oneway interface IProcessObserver {
+ /**
+ * Invoked when an app process starts up.
+ *
+ * @param pid The pid of the process.
+ * @param processUid The UID associated with the process.
+ * @param packageUid The UID associated with the package.
+ * @param packageName The name of the package.
+ * @param processName The name of the process.
+ */
+ void onProcessStarted(int pid, int processUid, int packageUid,
+ @utf8InCpp String packageName, @utf8InCpp String processName);
void onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities);
void onForegroundServicesChanged(int pid, int uid, int serviceTypes);
void onProcessDied(int pid, int uid);
diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS
index 0760d4d..3b5bba2 100644
--- a/core/java/android/app/OWNERS
+++ b/core/java/android/app/OWNERS
@@ -90,8 +90,8 @@
per-file pinner-client.aconfig = file:/core/java/android/app/pinner/OWNERS
# BackgroundInstallControlManager
-per-file BackgroundInstallControlManager.java = file:/services/core/java/com/android/server/pm/OWNERS
-per-file background_install_control_manager.aconfig = file:/services/core/java/com/android/server/pm/OWNERS
+per-file BackgroundInstallControlManager.java = file:/services/core/java/com/android/server/pm/BACKGROUND_INSTALL_OWNERS
+per-file background_install_control_manager.aconfig = file:/services/core/java/com/android/server/pm/BACKGROUND_INSTALL_OWNERS
# ResourcesManager
per-file ResourcesManager.java = file:RESOURCES_OWNERS
diff --git a/core/java/android/companion/virtual/camera/VirtualCameraConfig.java b/core/java/android/companion/virtual/camera/VirtualCameraConfig.java
index 350cf3d..06a0f5c 100644
--- a/core/java/android/companion/virtual/camera/VirtualCameraConfig.java
+++ b/core/java/android/companion/virtual/camera/VirtualCameraConfig.java
@@ -196,13 +196,12 @@
* <li>At least one stream must be added with {@link #addStreamConfig(int, int, int, int)}.
* <li>A callback must be set with {@link #setVirtualCameraCallback(Executor,
* VirtualCameraCallback)}
- * <li>A camera name must be set with {@link #setName(String)}
* <li>A lens facing must be set with {@link #setLensFacing(int)}
*/
@FlaggedApi(Flags.FLAG_VIRTUAL_CAMERA)
public static final class Builder {
- private String mName;
+ private final String mName;
private final ArraySet<VirtualCameraStreamConfig> mStreamConfigurations = new ArraySet<>();
private Executor mCallbackExecutor;
private VirtualCameraCallback mCallback;
@@ -210,12 +209,12 @@
private int mLensFacing = LENS_FACING_UNKNOWN;
/**
- * Sets the name of the virtual camera instance.
+ * Creates a new instance of {@link Builder}.
+ *
+ * @param name The name of the {@link VirtualCamera}.
*/
- @NonNull
- public Builder setName(@NonNull String name) {
- mName = requireNonNull(name, "Display name cannot be null");
- return this;
+ public Builder(@NonNull String name) {
+ mName = requireNonNull(name, "Name cannot be null");
}
/**
diff --git a/core/java/android/content/res/FontScaleConverterFactory.java b/core/java/android/content/res/FontScaleConverterFactory.java
index cbe4c62..625d7cb 100644
--- a/core/java/android/content/res/FontScaleConverterFactory.java
+++ b/core/java/android/content/res/FontScaleConverterFactory.java
@@ -58,6 +58,16 @@
synchronized (LOOKUP_TABLES_WRITE_LOCK) {
putInto(
sLookupTables,
+ /* scaleKey= */ 1.1f,
+ new FontScaleConverterImpl(
+ /* fromSp= */
+ new float[] { 8f, 10f, 12f, 14f, 18f, 20f, 24f, 30f, 100},
+ /* toDp= */
+ new float[] { 8.8f, 11f, 13.2f, 15.6f, 19.2f, 21.2f, 24.8f, 30f, 100})
+ );
+
+ putInto(
+ sLookupTables,
/* scaleKey= */ 1.15f,
new FontScaleConverterImpl(
/* fromSp= */
diff --git a/core/java/android/hardware/HardwareBuffer.java b/core/java/android/hardware/HardwareBuffer.java
index 0047b7d..ce0f9f59 100644
--- a/core/java/android/hardware/HardwareBuffer.java
+++ b/core/java/android/hardware/HardwareBuffer.java
@@ -115,14 +115,16 @@
@FlaggedApi(com.android.graphics.hwui.flags.Flags.FLAG_REQUESTED_FORMATS_V)
public static final int R_8 = 0x38;
/**
- * Format: 16 bits red. Bits should be represented in unsigned integer, instead of the
- * implicit unsigned normalized.
+ * Format: 16 bits red. When sampled on the GPU this is represented as an
+ * unsigned integer instead of implicit unsigned normalize.
+ * For more information see https://www.khronos.org/opengl/wiki/Normalized_Integer
*/
@FlaggedApi(com.android.graphics.hwui.flags.Flags.FLAG_REQUESTED_FORMATS_V)
public static final int R_16 = 0x39;
/**
- * Format: 16 bits each red, green. Bits should be represented in unsigned integer,
- * instead of the implicit unsigned normalized.
+ * Format: 16 bits each red, green. When sampled on the GPU this is represented
+ * as an unsigned integer instead of implicit unsigned normalize.
+ * For more information see https://www.khronos.org/opengl/wiki/Normalized_Integer
*/
@FlaggedApi(com.android.graphics.hwui.flags.Flags.FLAG_REQUESTED_FORMATS_V)
public static final int RG_1616 = 0x3a;
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index c0424db..bdaf9d7 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -16,7 +16,7 @@
package android.hardware.biometrics;
-import static android.Manifest.permission.MANAGE_BIOMETRIC_DIALOG;
+import static android.Manifest.permission.SET_BIOMETRIC_DIALOG_LOGO;
import static android.Manifest.permission.TEST_BIOMETRIC;
import static android.Manifest.permission.USE_BIOMETRIC;
import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
@@ -174,9 +174,9 @@
* @return This builder.
*/
@FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
- @RequiresPermission(MANAGE_BIOMETRIC_DIALOG)
+ @RequiresPermission(SET_BIOMETRIC_DIALOG_LOGO)
@NonNull
- public BiometricPrompt.Builder setLogo(@DrawableRes int logoRes) {
+ public BiometricPrompt.Builder setLogoRes(@DrawableRes int logoRes) {
mPromptInfo.setLogoRes(logoRes);
return this;
}
@@ -193,9 +193,9 @@
* @return This builder.
*/
@FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
- @RequiresPermission(MANAGE_BIOMETRIC_DIALOG)
+ @RequiresPermission(SET_BIOMETRIC_DIALOG_LOGO)
@NonNull
- public BiometricPrompt.Builder setLogo(@NonNull Bitmap logoBitmap) {
+ public BiometricPrompt.Builder setLogoBitmap(@NonNull Bitmap logoBitmap) {
mPromptInfo.setLogoBitmap(logoBitmap);
return this;
}
@@ -719,25 +719,25 @@
/**
* Gets the drawable resource of the logo for the prompt, as set by
- * {@link Builder#setLogo(int)}. Currently for system applications use only.
+ * {@link Builder#setLogoRes(int)}. Currently for system applications use only.
*
* @return The drawable resource of the logo, or -1 if the prompt has no logo resource set.
*/
@FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
- @RequiresPermission(MANAGE_BIOMETRIC_DIALOG)
+ @RequiresPermission(SET_BIOMETRIC_DIALOG_LOGO)
@DrawableRes
public int getLogoRes() {
return mPromptInfo.getLogoRes();
}
/**
- * Gets the logo bitmap for the prompt, as set by {@link Builder#setLogo(Bitmap)}. Currently for
- * system applications use only.
+ * Gets the logo bitmap for the prompt, as set by {@link Builder#setLogoBitmap(Bitmap)}.
+ * Currently for system applications use only.
*
* @return The logo bitmap of the prompt, or null if the prompt has no logo bitmap set.
*/
@FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
- @RequiresPermission(MANAGE_BIOMETRIC_DIALOG)
+ @RequiresPermission(SET_BIOMETRIC_DIALOG_LOGO)
@Nullable
public Bitmap getLogoBitmap() {
return mPromptInfo.getLogoBitmap();
diff --git a/core/java/android/hardware/biometrics/PromptInfo.java b/core/java/android/hardware/biometrics/PromptInfo.java
index d788b37..0f9cadc 100644
--- a/core/java/android/hardware/biometrics/PromptInfo.java
+++ b/core/java/android/hardware/biometrics/PromptInfo.java
@@ -166,9 +166,9 @@
}
/**
- * Returns whether MANAGE_BIOMETRIC_DIALOG is contained.
+ * Returns whether SET_BIOMETRIC_DIALOG_LOGO is contained.
*/
- public boolean containsManageBioApiConfigurations() {
+ public boolean containsSetLogoApiConfigurations() {
if (mLogoRes != -1) {
return true;
} else if (mLogoBitmap != null) {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 755f6d6..11edcaf 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -444,6 +444,18 @@
"android.settings.ACCESSIBILITY_DETAILS_SETTINGS";
/**
+ * Activity Action: Show settings to allow configuration of an accessibility
+ * shortcut belonging to an accessibility feature or features.
+ * <p>
+ * Input: ":settings:show_fragment_args" must contain "targets" denoting the services to edit.
+ * <p>
+ * Output: Nothing.
+ * @hide
+ **/
+ public static final String ACTION_ACCESSIBILITY_SHORTCUT_SETTINGS =
+ "android.settings.ACCESSIBILITY_SHORTCUT_SETTINGS";
+
+ /**
* Activity Action: Show settings to allow configuration of accessibility color and motion.
* <p>
* In some cases, a matching Activity may not exist, so ensure you
@@ -4513,10 +4525,11 @@
/** @hide */
public static void adjustConfigurationForUser(ContentResolver cr, Configuration outConfig,
int userHandle, boolean updateSettingsIfEmpty) {
+ final float defaultFontScale = getDefaultFontScale(cr, userHandle);
outConfig.fontScale = Settings.System.getFloatForUser(
- cr, FONT_SCALE, DEFAULT_FONT_SCALE, userHandle);
+ cr, FONT_SCALE, defaultFontScale, userHandle);
if (outConfig.fontScale < 0) {
- outConfig.fontScale = DEFAULT_FONT_SCALE;
+ outConfig.fontScale = defaultFontScale;
}
outConfig.fontWeightAdjustment = Settings.Secure.getIntForUser(
cr, Settings.Secure.FONT_WEIGHT_ADJUSTMENT, DEFAULT_FONT_WEIGHT, userHandle);
@@ -4541,6 +4554,12 @@
}
}
+ private static float getDefaultFontScale(ContentResolver cr, int userHandle) {
+ return com.android.window.flags.Flags.configurableFontScaleDefault()
+ ? Settings.System.getFloatForUser(cr, DEFAULT_DEVICE_FONT_SCALE,
+ DEFAULT_FONT_SCALE, userHandle) : DEFAULT_FONT_SCALE;
+ }
+
/**
* @hide Erase the fields in the Configuration that should be applied
* by the settings.
@@ -4907,6 +4926,15 @@
public static final String FONT_SCALE = "font_scale";
/**
+ * Default scaling factor for fonts for the specific device, float.
+ * The value is read from the {@link R.dimen.def_device_font_scale}
+ * configuration property.
+ *
+ * @hide
+ */
+ public static final String DEFAULT_DEVICE_FONT_SCALE = "device_font_scale";
+
+ /**
* The serialized system locale value.
*
* Do not use this value directory.
@@ -6245,6 +6273,7 @@
PRIVATE_SETTINGS.add(CAMERA_FLASH_NOTIFICATION);
PRIVATE_SETTINGS.add(SCREEN_FLASH_NOTIFICATION);
PRIVATE_SETTINGS.add(SCREEN_FLASH_NOTIFICATION_COLOR);
+ PRIVATE_SETTINGS.add(DEFAULT_DEVICE_FONT_SCALE);
}
/**
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index 5ad2502..298bdb8 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -622,6 +622,15 @@
new FillCallback(callback, request.getId())));
}
+ @Override
+ public void onConvertCredentialRequest(
+ @NonNull ConvertCredentialRequest convertCredentialRequest,
+ @NonNull IConvertCredentialCallback convertCredentialCallback) {
+ mHandler.sendMessage(obtainMessage(
+ AutofillService::onConvertCredentialRequest,
+ AutofillService.this, convertCredentialRequest,
+ new ConvertCredentialCallback(convertCredentialCallback)));
+ }
@Override
public void onFillCredentialRequest(FillRequest request, IFillCallback callback,
@@ -707,7 +716,19 @@
*/
public void onFillCredentialRequest(@NonNull FillRequest request,
@NonNull CancellationSignal cancellationSignal, @NonNull FillCallback callback,
- IAutoFillManagerClient autofillClientCallback) {}
+ @NonNull IAutoFillManagerClient autofillClientCallback) {}
+
+ /**
+ * Called by the Android system to convert a credential manager response to a dataset
+ *
+ * @param convertCredentialRequest the request that has the original credential manager response
+ * @param convertCredentialCallback callback used to notify the result of the request.
+ *
+ * @hide
+ */
+ public void onConvertCredentialRequest(
+ @NonNull ConvertCredentialRequest convertCredentialRequest,
+ @NonNull ConvertCredentialCallback convertCredentialCallback){}
/**
* Called when the user requests the service to save the contents of a screen.
diff --git a/core/java/android/service/autofill/ConvertCredentialCallback.java b/core/java/android/service/autofill/ConvertCredentialCallback.java
new file mode 100644
index 0000000..a39f011
--- /dev/null
+++ b/core/java/android/service/autofill/ConvertCredentialCallback.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.autofill;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.RemoteException;
+
+/**
+ * <p><code>ConvertCredentialCallback</code> handles convertCredentialResponse from Autofill
+ * Service.
+ *
+ * @hide
+ */
+public final class ConvertCredentialCallback {
+
+ private static final String TAG = "ConvertCredentialCallback";
+
+ private final IConvertCredentialCallback mCallback;
+
+ /** @hide */
+ public ConvertCredentialCallback(IConvertCredentialCallback callback) {
+ mCallback = callback;
+ }
+
+ /**
+ * Notifies the Android System that a convertCredentialRequest was fulfilled by the service.
+ *
+ * @param convertCredentialResponse the result
+ */
+ public void onSuccess(@NonNull ConvertCredentialResponse convertCredentialResponse) {
+ try {
+ mCallback.onSuccess(convertCredentialResponse);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Notifies the Android System that a convert credential request has failed
+ *
+ * @param message the error message
+ */
+ public void onFailure(@Nullable CharSequence message) {
+ try {
+ mCallback.onFailure(message);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+}
diff --git a/core/java/android/service/autofill/ConvertCredentialRequest.aidl b/core/java/android/service/autofill/ConvertCredentialRequest.aidl
new file mode 100644
index 0000000..79681e2
--- /dev/null
+++ b/core/java/android/service/autofill/ConvertCredentialRequest.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2024, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.autofill;
+
+parcelable ConvertCredentialRequest;
\ No newline at end of file
diff --git a/core/java/android/service/autofill/ConvertCredentialRequest.java b/core/java/android/service/autofill/ConvertCredentialRequest.java
new file mode 100644
index 0000000..d2d7556
--- /dev/null
+++ b/core/java/android/service/autofill/ConvertCredentialRequest.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.autofill;
+
+import android.annotation.NonNull;
+import android.credentials.GetCredentialResponse;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+
+
+/**
+ * This class represents a request to an autofill service to convert the credential manager response
+ * to a dataset.
+ *
+ * @hide
+ */
+@DataClass(
+ genToString = true,
+ genHiddenConstructor = true,
+ genHiddenConstDefs = true)
+public final class ConvertCredentialRequest implements Parcelable {
+ private final @NonNull GetCredentialResponse mGetCredentialResponse;
+ private final @NonNull Bundle mClientState;
+
+
+
+ // Code below generated by codegen v1.0.23.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/service/autofill/ConvertCredentialRequest.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ /**
+ * Creates a new ConvertCredentialRequest.
+ *
+ * @hide
+ */
+ @DataClass.Generated.Member
+ public ConvertCredentialRequest(
+ @NonNull GetCredentialResponse getCredentialResponse,
+ @NonNull Bundle clientState) {
+ this.mGetCredentialResponse = getCredentialResponse;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mGetCredentialResponse);
+ this.mClientState = clientState;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mClientState);
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull GetCredentialResponse getGetCredentialResponse() {
+ return mGetCredentialResponse;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull Bundle getClientState() {
+ return mClientState;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public String toString() {
+ // You can override field toString logic by defining methods like:
+ // String fieldNameToString() { ... }
+
+ return "ConvertCredentialRequest { " +
+ "getCredentialResponse = " + mGetCredentialResponse + ", " +
+ "clientState = " + mClientState +
+ " }";
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ dest.writeTypedObject(mGetCredentialResponse, flags);
+ dest.writeBundle(mClientState);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ ConvertCredentialRequest(@NonNull Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ GetCredentialResponse getCredentialResponse = (GetCredentialResponse) in.readTypedObject(GetCredentialResponse.CREATOR);
+ Bundle clientState = in.readBundle();
+
+ this.mGetCredentialResponse = getCredentialResponse;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mGetCredentialResponse);
+ this.mClientState = clientState;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mClientState);
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<ConvertCredentialRequest> CREATOR
+ = new Parcelable.Creator<ConvertCredentialRequest>() {
+ @Override
+ public ConvertCredentialRequest[] newArray(int size) {
+ return new ConvertCredentialRequest[size];
+ }
+
+ @Override
+ public ConvertCredentialRequest createFromParcel(@NonNull Parcel in) {
+ return new ConvertCredentialRequest(in);
+ }
+ };
+
+ @DataClass.Generated(
+ time = 1706132305002L,
+ codegenVersion = "1.0.23",
+ sourceFile = "frameworks/base/core/java/android/service/autofill/ConvertCredentialRequest.java",
+ inputSignatures = "private final @android.annotation.NonNull android.credentials.GetCredentialResponse mGetCredentialResponse\nprivate final @android.annotation.NonNull android.os.Bundle mClientState\nclass ConvertCredentialRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstructor=true, genHiddenConstDefs=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/service/autofill/ConvertCredentialResponse.aidl b/core/java/android/service/autofill/ConvertCredentialResponse.aidl
new file mode 100644
index 0000000..98ac6f6
--- /dev/null
+++ b/core/java/android/service/autofill/ConvertCredentialResponse.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2024, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.autofill;
+
+parcelable ConvertCredentialResponse;
\ No newline at end of file
diff --git a/core/java/android/service/autofill/ConvertCredentialResponse.java b/core/java/android/service/autofill/ConvertCredentialResponse.java
new file mode 100644
index 0000000..5da4f63
--- /dev/null
+++ b/core/java/android/service/autofill/ConvertCredentialResponse.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.autofill;
+
+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.DataClass;
+
+/**
+ * Response for a {@Link ConvertCredentialRequest}
+ *
+ * @hide
+ */
+@DataClass(
+ genToString = true,
+ genHiddenConstructor = true,
+ genHiddenConstDefs = true)
+public final class ConvertCredentialResponse implements Parcelable {
+ private final @NonNull Dataset mDataset;
+ private final @Nullable Bundle mClientState;
+
+
+
+
+ // Code below generated by codegen v1.0.23.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/service/autofill/ConvertCredentialResponse.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ /**
+ * Creates a new ConvertCredentialResponse.
+ *
+ * @hide
+ */
+ @DataClass.Generated.Member
+ public ConvertCredentialResponse(
+ @NonNull Dataset dataset,
+ @Nullable Bundle clientState) {
+ this.mDataset = dataset;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mDataset);
+ this.mClientState = clientState;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull Dataset getDataset() {
+ return mDataset;
+ }
+
+ @DataClass.Generated.Member
+ public @Nullable Bundle getClientState() {
+ return mClientState;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public String toString() {
+ // You can override field toString logic by defining methods like:
+ // String fieldNameToString() { ... }
+
+ return "ConvertCredentialResponse { " +
+ "dataset = " + mDataset + ", " +
+ "clientState = " + mClientState +
+ " }";
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ byte flg = 0;
+ if (mClientState != null) flg |= 0x2;
+ dest.writeByte(flg);
+ dest.writeTypedObject(mDataset, flags);
+ if (mClientState != null) dest.writeBundle(mClientState);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ ConvertCredentialResponse(@NonNull Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ byte flg = in.readByte();
+ Dataset dataset = (Dataset) in.readTypedObject(Dataset.CREATOR);
+ Bundle clientState = (flg & 0x2) == 0 ? null : in.readBundle();
+
+ this.mDataset = dataset;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mDataset);
+ this.mClientState = clientState;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<ConvertCredentialResponse> CREATOR
+ = new Parcelable.Creator<ConvertCredentialResponse>() {
+ @Override
+ public ConvertCredentialResponse[] newArray(int size) {
+ return new ConvertCredentialResponse[size];
+ }
+
+ @Override
+ public ConvertCredentialResponse createFromParcel(@NonNull Parcel in) {
+ return new ConvertCredentialResponse(in);
+ }
+ };
+
+ @DataClass.Generated(
+ time = 1706132669373L,
+ codegenVersion = "1.0.23",
+ sourceFile = "frameworks/base/core/java/android/service/autofill/ConvertCredentialResponse.java",
+ inputSignatures = "private final @android.annotation.NonNull android.service.autofill.Dataset mDataset\nprivate final @android.annotation.Nullable android.os.Bundle mClientState\nclass ConvertCredentialResponse extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstructor=true, genHiddenConstDefs=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/service/autofill/FillEventHistory.java b/core/java/android/service/autofill/FillEventHistory.java
index 5d58120..98dda10 100644
--- a/core/java/android/service/autofill/FillEventHistory.java
+++ b/core/java/android/service/autofill/FillEventHistory.java
@@ -309,12 +309,19 @@
/** The autofill suggestion is shown as a dialog presentation. */
public static final int UI_TYPE_DIALOG = 3;
+ /**
+ * The autofill suggestion is shown os a credman bottom sheet
+ * @hide
+ */
+ public static final int UI_TYPE_CREDMAN_BOTTOM_SHEET = 4;
+
/** @hide */
@IntDef(prefix = { "UI_TYPE_" }, value = {
UI_TYPE_UNKNOWN,
UI_TYPE_MENU,
UI_TYPE_INLINE,
- UI_TYPE_DIALOG
+ UI_TYPE_DIALOG,
+ UI_TYPE_CREDMAN_BOTTOM_SHEET
})
@Retention(RetentionPolicy.SOURCE)
public @interface UiType {}
@@ -755,6 +762,8 @@
return "UI_TYPE_INLINE";
case UI_TYPE_DIALOG:
return "UI_TYPE_FILL_DIALOG";
+ case UI_TYPE_CREDMAN_BOTTOM_SHEET:
+ return "UI_TYPE_CREDMAN_BOTTOM_SHEET";
default:
return "UI_TYPE_UNKNOWN";
}
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index 7ea74d3..09ec933 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -28,6 +28,7 @@
import android.annotation.SuppressLint;
import android.annotation.TestApi;
import android.app.Activity;
+import android.app.PendingIntent;
import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.ParceledListSlice;
@@ -116,6 +117,7 @@
private final boolean mShowFillDialogIcon;
private final boolean mShowSaveDialogIcon;
private final @Nullable FieldClassification[] mDetectedFieldTypes;
+ private final @Nullable PendingIntent mDialogPendingIntent;
/**
* Creates a shollow copy of the provided FillResponse.
@@ -150,7 +152,8 @@
r.mServiceDisplayNameResourceId,
r.mShowFillDialogIcon,
r.mShowSaveDialogIcon,
- r.mDetectedFieldTypes);
+ r.mDetectedFieldTypes,
+ r.mDialogPendingIntent);
}
private FillResponse(ParceledListSlice<Dataset> datasets, SaveInfo saveInfo, Bundle clientState,
@@ -163,7 +166,7 @@
int[] cancelIds, boolean supportsInlineSuggestions, int iconResourceId,
int serviceDisplayNameResourceId, boolean showFillDialogIcon,
boolean showSaveDialogIcon,
- FieldClassification[] detectedFieldTypes) {
+ FieldClassification[] detectedFieldTypes, PendingIntent dialogPendingIntent) {
mDatasets = datasets;
mSaveInfo = saveInfo;
mClientState = clientState;
@@ -190,6 +193,7 @@
mShowFillDialogIcon = showFillDialogIcon;
mShowSaveDialogIcon = showSaveDialogIcon;
mDetectedFieldTypes = detectedFieldTypes;
+ mDialogPendingIntent = dialogPendingIntent;
}
private FillResponse(@NonNull Builder builder) {
@@ -219,6 +223,7 @@
mShowFillDialogIcon = builder.mShowFillDialogIcon;
mShowSaveDialogIcon = builder.mShowSaveDialogIcon;
mDetectedFieldTypes = builder.mDetectedFieldTypes;
+ mDialogPendingIntent = builder.mDialogPendingIntent;
}
/** @hide */
@@ -399,6 +404,7 @@
private boolean mShowFillDialogIcon = true;
private boolean mShowSaveDialogIcon = true;
private FieldClassification[] mDetectedFieldTypes;
+ private PendingIntent mDialogPendingIntent;
/**
* Adds a new {@link FieldClassification} to this response, to
@@ -1079,6 +1085,24 @@
}
/**
+ * Sets credential dialog pending intent. Framework will use the intent to launch the
+ * selector UI. A replacement for previous fill bottom sheet.
+ *
+ * @throws IllegalStateException if {@link #build()} was already called.
+ * @throws NullPointerException if {@code pendingIntent} is {@code null}.
+ *
+ * @hide
+ */
+ @NonNull
+ public Builder setDialogPendingIntent(@NonNull PendingIntent pendingIntent) {
+ throwIfDestroyed();
+ Preconditions.checkNotNull(pendingIntent,
+ "can't pass a null object to setDialogPendingIntent");
+ mDialogPendingIntent = pendingIntent;
+ return this;
+ }
+
+ /**
* Builds a new {@link FillResponse} instance.
*
* @throws IllegalStateException if any of the following conditions occur:
@@ -1187,6 +1211,9 @@
if (mAuthentication != null) {
builder.append(", hasAuthentication");
}
+ if (mDialogPendingIntent != null) {
+ builder.append(", hasDialogPendingIntent");
+ }
if (mAuthenticationIds != null) {
builder.append(", authenticationIds=").append(Arrays.toString(mAuthenticationIds));
}
@@ -1232,6 +1259,7 @@
parcel.writeParcelable(mInlineTooltipPresentation, flags);
parcel.writeParcelable(mDialogPresentation, flags);
parcel.writeParcelable(mDialogHeader, flags);
+ parcel.writeParcelable(mDialogPendingIntent, flags);
parcel.writeParcelableArray(mFillDialogTriggerIds, flags);
parcel.writeParcelable(mHeader, flags);
parcel.writeParcelable(mFooter, flags);
@@ -1282,6 +1310,11 @@
if (dialogHeader != null) {
builder.setDialogHeader(dialogHeader);
}
+ final PendingIntent dialogPendingIntent = parcel.readParcelable(null,
+ PendingIntent.class);
+ if (dialogPendingIntent != null) {
+ builder.setDialogPendingIntent(dialogPendingIntent);
+ }
final AutofillId[] triggerIds = parcel.readParcelableArray(null, AutofillId.class);
if (triggerIds != null) {
builder.setFillDialogTriggerIds(triggerIds);
diff --git a/core/java/android/service/autofill/IAutoFillService.aidl b/core/java/android/service/autofill/IAutoFillService.aidl
index 03ead32..2c2feae 100644
--- a/core/java/android/service/autofill/IAutoFillService.aidl
+++ b/core/java/android/service/autofill/IAutoFillService.aidl
@@ -16,6 +16,8 @@
package android.service.autofill;
+import android.service.autofill.ConvertCredentialRequest;
+import android.service.autofill.IConvertCredentialCallback;
import android.service.autofill.FillRequest;
import android.service.autofill.IFillCallback;
import android.service.autofill.ISaveCallback;
@@ -32,7 +34,8 @@
void onConnectedStateChanged(boolean connected);
void onFillRequest(in FillRequest request, in IFillCallback callback);
void onFillCredentialRequest(in FillRequest request, in IFillCallback callback,
- in IAutoFillManagerClient client);
+ in IAutoFillManagerClient client);
void onSaveRequest(in SaveRequest request, in ISaveCallback callback);
void onSavedPasswordCountRequest(in IResultReceiver receiver);
+ void onConvertCredentialRequest(in ConvertCredentialRequest convertCredentialRequest, in IConvertCredentialCallback convertCredentialCallback);
}
diff --git a/core/java/android/service/autofill/IConvertCredentialCallback.aidl b/core/java/android/service/autofill/IConvertCredentialCallback.aidl
new file mode 100644
index 0000000..9dfc294
--- /dev/null
+++ b/core/java/android/service/autofill/IConvertCredentialCallback.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.autofill;
+
+import android.os.ICancellationSignal;
+
+import android.service.autofill.ConvertCredentialResponse;
+
+/**
+ * Interface to receive the result of a convert credential request
+ *
+ * @hide
+ */
+oneway interface IConvertCredentialCallback {
+ void onSuccess(in ConvertCredentialResponse convertCredentialResponse);
+ void onFailure(CharSequence message);
+}
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index ba7874e..a1f44e4 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -1256,13 +1256,13 @@
}
private static void registerNativeMemoryUsage() {
- if (Flags.enableSurfaceNativeAllocRegistration()) {
+ if (Flags.enableSurfaceNativeAllocRegistrationRo()) {
VMRuntime.getRuntime().registerNativeAllocation(SURFACE_NATIVE_ALLOCATION_SIZE_BYTES);
}
}
private static void freeNativeMemoryUsage() {
- if (Flags.enableSurfaceNativeAllocRegistration()) {
+ if (Flags.enableSurfaceNativeAllocRegistrationRo()) {
VMRuntime.getRuntime().registerNativeFree(SURFACE_NATIVE_ALLOCATION_SIZE_BYTES);
}
}
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index dbeffc8..559ccfea7 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -298,6 +298,15 @@
"android.view.autofill.extra.AUGMENTED_AUTOFILL_CLIENT";
/**
+ * Internal extra used to pass the fill request id in client state of
+ * {@link ConvertCredentialResponse}
+ *
+ * @hide
+ */
+ public static final String EXTRA_AUTOFILL_REQUEST_ID =
+ "android.view.autofill.extra.AUTOFILL_REQUEST_ID";
+
+ /**
* Autofill Hint to indicate that it can match any field.
*
* @hide
diff --git a/core/java/android/view/autofill/OWNERS b/core/java/android/view/autofill/OWNERS
index 37c6f5b..898947a 100644
--- a/core/java/android/view/autofill/OWNERS
+++ b/core/java/android/view/autofill/OWNERS
@@ -4,6 +4,7 @@
haoranzhang@google.com
skxu@google.com
yunicorn@google.com
+reemabajwa@google.com
# Bug component: 543785 = per-file *Augmented*
per-file *Augmented* = wangqi@google.com
diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java
index 86804c6..baefe7b 100644
--- a/core/java/android/window/WindowOnBackInvokedDispatcher.java
+++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java
@@ -442,8 +442,7 @@
return WindowOnBackInvokedDispatcher
.isOnBackInvokedCallbackEnabled(activityInfo, applicationInfo,
- () -> originalContext.obtainStyledAttributes(
- new int[] {android.R.attr.windowSwipeToDismiss}), true);
+ () -> originalContext);
}
@Override
@@ -501,7 +500,7 @@
*/
public static boolean isOnBackInvokedCallbackEnabled(@Nullable ActivityInfo activityInfo,
@NonNull ApplicationInfo applicationInfo,
- @NonNull Supplier<TypedArray> windowAttrSupplier, boolean recycleTypedArray) {
+ @NonNull Supplier<Context> contextSupplier) {
// new back is enabled if the feature flag is enabled AND the app does not explicitly
// request legacy back.
if (!ENABLE_PREDICTIVE_BACK) {
@@ -547,15 +546,15 @@
// setTrigger(true)
// Use the original context to resolve the styled attribute so that they stay
// true to the window.
- TypedArray windowAttr = windowAttrSupplier.get();
+ final Context context = contextSupplier.get();
boolean windowSwipeToDismiss = true;
- if (windowAttr != null) {
- if (windowAttr.getIndexCount() > 0) {
- windowSwipeToDismiss = windowAttr.getBoolean(0, true);
+ if (context != null) {
+ final TypedArray array = context.obtainStyledAttributes(
+ new int[]{android.R.attr.windowSwipeToDismiss});
+ if (array.getIndexCount() > 0) {
+ windowSwipeToDismiss = array.getBoolean(0, true);
}
- if (recycleTypedArray) {
- windowAttr.recycle();
- }
+ array.recycle();
}
if (DEBUG) {
diff --git a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
index edfbea4..1de77f6 100644
--- a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
+++ b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
@@ -58,3 +58,11 @@
bug: "319808237"
is_fixed_read_only: true
}
+
+flag {
+ name: "camera_compat_for_freeform"
+ namespace: "large_screen_experiences_app_compat"
+ description: "Whether to apply Camera Compat treatment to fixed-orientation apps in freeform windowing mode"
+ bug: "314952133"
+ is_fixed_read_only: true
+}
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index 2c5fbd7..f234637 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -38,14 +38,6 @@
}
flag {
- name: "draw_magnifier_border_outside_wmlock"
- namespace: "windowing_frontend"
- description: "Avoid holding WM locks for a long time when executing lockCanvas"
- bug: "316075123"
- is_fixed_read_only: true
-}
-
-flag {
name: "introduce_smoother_dimmer"
namespace: "windowing_frontend"
description: "Refactor dim to fix flickers"
diff --git a/core/java/com/android/internal/jank/Cuj.java b/core/java/com/android/internal/jank/Cuj.java
index 96740c5..7b3565b 100644
--- a/core/java/com/android/internal/jank/Cuj.java
+++ b/core/java/com/android/internal/jank/Cuj.java
@@ -121,10 +121,11 @@
public static final int CUJ_PREDICTIVE_BACK_CROSS_TASK = 85;
public static final int CUJ_PREDICTIVE_BACK_HOME = 86;
public static final int CUJ_LAUNCHER_SEARCH_QSB_OPEN = 87;
+ public static final int CUJ_BACK_PANEL_ARROW = 88;
// When adding a CUJ, update this and make sure to also update CUJ_TO_STATSD_INTERACTION_TYPE.
@VisibleForTesting
- static final int LAST_CUJ = CUJ_LAUNCHER_SEARCH_QSB_OPEN;
+ static final int LAST_CUJ = CUJ_BACK_PANEL_ARROW;
/** @hide */
@IntDef({
@@ -207,6 +208,7 @@
CUJ_PREDICTIVE_BACK_CROSS_TASK,
CUJ_PREDICTIVE_BACK_HOME,
CUJ_LAUNCHER_SEARCH_QSB_OPEN,
+ CUJ_BACK_PANEL_ARROW,
})
@Retention(RetentionPolicy.SOURCE)
public @interface CujType {
@@ -298,8 +300,8 @@
CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PREDICTIVE_BACK_CROSS_ACTIVITY;
CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_PREDICTIVE_BACK_CROSS_TASK] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PREDICTIVE_BACK_CROSS_TASK;
CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_PREDICTIVE_BACK_HOME] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PREDICTIVE_BACK_HOME;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_SEARCH_QSB_OPEN] =
- FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_SEARCH_QSB_OPEN;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_SEARCH_QSB_OPEN] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_SEARCH_QSB_OPEN;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_BACK_PANEL_ARROW] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__BACK_PANEL_ARROW;
}
private Cuj() {
@@ -474,6 +476,8 @@
return "PREDICTIVE_BACK_HOME";
case CUJ_LAUNCHER_SEARCH_QSB_OPEN:
return "LAUNCHER_SEARCH_QSB_OPEN";
+ case CUJ_BACK_PANEL_ARROW:
+ return "BACK_PANEL_ARROW";
}
return "UNKNOWN";
}
diff --git a/core/java/com/android/internal/jank/InteractionMonitorDebugOverlay.java b/core/java/com/android/internal/jank/InteractionMonitorDebugOverlay.java
index f3f16a0..d9cac12 100644
--- a/core/java/com/android/internal/jank/InteractionMonitorDebugOverlay.java
+++ b/core/java/com/android/internal/jank/InteractionMonitorDebugOverlay.java
@@ -28,6 +28,7 @@
import android.graphics.Rect;
import android.os.Handler;
import android.os.Trace;
+import android.util.Log;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.view.WindowCallbacks;
@@ -52,6 +53,7 @@
* @hide
*/
class InteractionMonitorDebugOverlay implements WindowCallbacks {
+ private static final String TAG = "InteractionMonitorDebug";
private static final int REASON_STILL_RUNNING = -1000;
private final Object mLock;
// Sparse array where the key in the CUJ and the value is the session status, or null if
@@ -77,7 +79,7 @@
mDebugPaint.setAntiAlias(false);
mDebugFontMetrics = new Paint.FontMetrics();
final Context context = ActivityThread.currentApplication();
- mPackageName = context.getPackageName();
+ mPackageName = context == null ? "null" : context.getPackageName();
}
@UiThread
@@ -153,8 +155,14 @@
SparseArray<InteractionJankMonitor.RunningTracker> runningTrackers) {
synchronized (mLock) {
mRunningCujs.put(removedCuj, reason);
+ boolean isLoggable = Log.isLoggable(TAG, Log.DEBUG);
+ if (isLoggable) {
+ String cujName = Cuj.getNameOfCuj(removedCuj);
+ Log.d(TAG, cujName + (reason == REASON_END_NORMAL ? " ended" : " cancelled"));
+ }
// If REASON_STILL_RUNNING is not in mRunningCujs, then all CUJs have ended
if (mRunningCujs.indexOfValue(REASON_STILL_RUNNING) < 0) {
+ if (isLoggable) Log.d(TAG, "All CUJs ended");
mRunningCujs.clear();
dispose();
} else {
@@ -186,6 +194,10 @@
@UiThread
void onTrackerAdded(@Cuj.CujType int addedCuj, InteractionJankMonitor.RunningTracker tracker) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ String cujName = Cuj.getNameOfCuj(addedCuj);
+ Log.d(TAG, cujName + " started");
+ }
synchronized (mLock) {
// Use REASON_STILL_RUNNING (not technically one of the '@Reasons') to indicate the CUJ
// is still running
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 723eb70..0e0af4d 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -6654,7 +6654,14 @@
<!-- Allows the system to control the BiometricDialog (SystemUI). Reserved for the system. @hide -->
<permission android:name="android.permission.MANAGE_BIOMETRIC_DIALOG"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
+
+ <!-- Allows an application to set the BiometricDialog (SystemUI) logo .
+ <p>Not for use by third-party applications.
+ @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt")
+ -->
+ <permission android:name="android.permission.SET_BIOMETRIC_DIALOG_LOGO"
+ android:protectionLevel="signature" />
<!-- Allows an application to control keyguard. Only allowed for system processes.
@hide -->
@@ -7801,6 +7808,16 @@
<permission android:name="android.permission.RUN_USER_INITIATED_JOBS"
android:protectionLevel="normal"/>
+ <!-- @FlaggedApi("android.app.job.backup_jobs_exemption")
+ Gives applications whose <b>primary use case</b> is to backup or sync content increased
+ job execution allowance in order to complete the related work. The jobs must have a valid
+ content URI trigger and network constraint set.
+ <p>This is a special access permission that can be revoked by the system or the user.
+ <p>Protection level: signature|privileged|appop
+ -->
+ <permission android:name="android.permission.RUN_BACKUP_JOBS"
+ android:protectionLevel="signature|privileged|appop"/>
+
<!-- Allows an app access to the installer provided app metadata.
@SystemApi
@hide
@@ -8374,6 +8391,16 @@
</intent-filter>
</receiver>
+ <!-- Broadcast Receiver listens to sufficient verifier broadcast from Package Manager
+ when installing new SDK. Verification of SDK code during installation time is run
+ to determine compatibility with privacy sandbox restrictions. -->
+ <receiver android:name="com.android.server.sdksandbox.SdkSandboxVerifierReceiver"
+ android:exported="false">
+ <intent-filter>
+ <action android:name="android.intent.action.PACKAGE_NEEDS_VERIFICATION"/>
+ </intent-filter>
+ </receiver>
+
<service android:name="android.hardware.location.GeofenceHardwareService"
android:permission="android.permission.LOCATION_HARDWARE"
android:exported="false" />
diff --git a/core/res/res/color-night/notification_expand_button_state_tint.xml b/core/res/res/color-night/notification_expand_button_state_tint.xml
deleted file mode 100644
index a794d53..0000000
--- a/core/res/res/color-night/notification_expand_button_state_tint.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<!--
- ~ 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">
- <item android:state_pressed="true" android:color="@android:color/system_on_surface_dark" android:alpha="0.06"/>
- <item android:state_hovered="true" android:color="@android:color/system_on_surface_dark" android:alpha="0.03"/>
- <item android:color="@android:color/system_on_surface_dark" android:alpha="0.00"/>
-</selector>
\ No newline at end of file
diff --git a/core/res/res/color/notification_expand_button_state_tint.xml b/core/res/res/color/notification_expand_button_state_tint.xml
index 67b2c25..5a8594f 100644
--- a/core/res/res/color/notification_expand_button_state_tint.xml
+++ b/core/res/res/color/notification_expand_button_state_tint.xml
@@ -14,8 +14,11 @@
~ limitations under the License.
-->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_pressed="true" android:color="@android:color/system_on_surface_light" android:alpha="0.12"/>
- <item android:state_hovered="true" android:color="@android:color/system_on_surface_light" android:alpha="0.08"/>
- <item android:color="@android:color/system_on_surface_light" android:alpha="0.00"/>
+<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/materialColorOnPrimaryFixed"
+ android:alpha="0.15"/>
+ <item android:state_hovered="true" android:color="?androidprv:attr/materialColorOnPrimaryFixed"
+ android:alpha="0.11"/>
+ <item android:color="@color/transparent" />
</selector>
\ No newline at end of file
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 0d1a987..23c78fd 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -6611,7 +6611,7 @@
</string-array>
<!-- Whether or not the monitoring on the apps' background battery drain is enabled -->
- <bool name="config_bg_current_drain_monitor_enabled">true</bool>
+ <bool name="config_bg_current_drain_monitor_enabled">false</bool>
<!-- The threshold of the background current drain (in percentage) to the restricted
standby bucket.
diff --git a/core/tests/InputMethodCoreTests/Android.bp b/core/tests/InputMethodCoreTests/Android.bp
new file mode 100644
index 0000000..ac64625
--- /dev/null
+++ b/core/tests/InputMethodCoreTests/Android.bp
@@ -0,0 +1,66 @@
+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: "InputMethodCoreTests",
+
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.kt",
+ "src/**/I*.aidl",
+ ],
+
+ dxflags: ["--core-library"],
+
+ static_libs: [
+ "collector-device-lib-platform",
+ "android-common",
+ "frameworks-core-util-lib",
+ "androidx.core_core",
+ "androidx.core_core-ktx",
+ "androidx.test.ext.junit",
+ "androidx.test.runner",
+ "androidx.test.rules",
+ "flag-junit",
+ "junit-params",
+ "kotlin-test",
+ "mockito-target-minus-junit4",
+ "platform-test-annotations",
+ "platform-compat-test-rules",
+ "truth",
+ "print-test-util-lib",
+ "testng",
+ "device-time-shell-utils",
+ "testables",
+ "flag-junit",
+ ],
+
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ "android.test.mock",
+ "framework",
+ "ext",
+ "framework-res",
+ ],
+
+ sdk_version: "core_platform",
+ test_suites: [
+ "device-tests",
+ "automotive-tests",
+ ],
+
+ certificate: "platform",
+
+ resource_dirs: ["res"],
+
+ data: [
+ ":com.android.cts.helpers.aosp",
+ ],
+}
diff --git a/core/tests/InputMethodCoreTests/AndroidManifest.xml b/core/tests/InputMethodCoreTests/AndroidManifest.xml
new file mode 100644
index 0000000..8d00d0f
--- /dev/null
+++ b/core/tests/InputMethodCoreTests/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ android:installLocation="internalOnly"
+ package="com.android.frameworks.inputmethodcoretests"
+ android:sharedUserId="com.android.uid.test">
+
+ <application
+ android:supportsRtl="true"
+ android:enableOnBackInvokedCallback="true">
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.frameworks.inputmethodcoretests"
+ android:label="InputMethod Core Tests" />
+</manifest>
diff --git a/core/tests/InputMethodCoreTests/AndroidTest.xml b/core/tests/InputMethodCoreTests/AndroidTest.xml
new file mode 100644
index 0000000..fa585d8
--- /dev/null
+++ b/core/tests/InputMethodCoreTests/AndroidTest.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Runs InputMethod Core Tests.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-instrumentation" />
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="InputMethodCoreTests.apk" />
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <!-- TODO(b/254155965): Design a mechanism to finally remove this command. -->
+ <option name="run-command" value="settings put global device_config_sync_disabled 0" />
+ </target_preparer>
+
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DeviceInteractionHelperInstaller" />
+
+ <option name="test-tag" value="InputMethodCoreTests" />
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.frameworks.inputmethodcoretests" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+</configuration>
diff --git a/core/tests/InputMethodCoreTests/OWNERS b/core/tests/InputMethodCoreTests/OWNERS
new file mode 100644
index 0000000..5deb2ce
--- /dev/null
+++ b/core/tests/InputMethodCoreTests/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/view/inputmethod/OWNERS
diff --git a/core/tests/coretests/res/xml/ime_meta.xml b/core/tests/InputMethodCoreTests/res/xml/ime_meta.xml
similarity index 100%
rename from core/tests/coretests/res/xml/ime_meta.xml
rename to core/tests/InputMethodCoreTests/res/xml/ime_meta.xml
diff --git a/core/tests/coretests/res/xml/ime_meta_inline_suggestions.xml b/core/tests/InputMethodCoreTests/res/xml/ime_meta_inline_suggestions.xml
similarity index 100%
rename from core/tests/coretests/res/xml/ime_meta_inline_suggestions.xml
rename to core/tests/InputMethodCoreTests/res/xml/ime_meta_inline_suggestions.xml
diff --git a/core/tests/coretests/res/xml/ime_meta_inline_suggestions_with_touch_exploration.xml b/core/tests/InputMethodCoreTests/res/xml/ime_meta_inline_suggestions_with_touch_exploration.xml
similarity index 100%
rename from core/tests/coretests/res/xml/ime_meta_inline_suggestions_with_touch_exploration.xml
rename to core/tests/InputMethodCoreTests/res/xml/ime_meta_inline_suggestions_with_touch_exploration.xml
diff --git a/core/tests/coretests/res/xml/ime_meta_sw_next.xml b/core/tests/InputMethodCoreTests/res/xml/ime_meta_sw_next.xml
similarity index 100%
rename from core/tests/coretests/res/xml/ime_meta_sw_next.xml
rename to core/tests/InputMethodCoreTests/res/xml/ime_meta_sw_next.xml
diff --git a/core/tests/coretests/res/xml/ime_meta_virtual_device_only.xml b/core/tests/InputMethodCoreTests/res/xml/ime_meta_virtual_device_only.xml
similarity index 100%
rename from core/tests/coretests/res/xml/ime_meta_virtual_device_only.xml
rename to core/tests/InputMethodCoreTests/res/xml/ime_meta_virtual_device_only.xml
diff --git a/core/tests/coretests/res/xml/ime_meta_vr_only.xml b/core/tests/InputMethodCoreTests/res/xml/ime_meta_vr_only.xml
similarity index 100%
rename from core/tests/coretests/res/xml/ime_meta_vr_only.xml
rename to core/tests/InputMethodCoreTests/res/xml/ime_meta_vr_only.xml
diff --git a/core/tests/coretests/src/android/view/inputmethod/BaseInputConnectionTest.java b/core/tests/InputMethodCoreTests/src/android/view/inputmethod/BaseInputConnectionTest.java
similarity index 100%
rename from core/tests/coretests/src/android/view/inputmethod/BaseInputConnectionTest.java
rename to core/tests/InputMethodCoreTests/src/android/view/inputmethod/BaseInputConnectionTest.java
diff --git a/core/tests/coretests/src/android/view/inputmethod/CursorAnchorInfoTest.java b/core/tests/InputMethodCoreTests/src/android/view/inputmethod/CursorAnchorInfoTest.java
similarity index 100%
rename from core/tests/coretests/src/android/view/inputmethod/CursorAnchorInfoTest.java
rename to core/tests/InputMethodCoreTests/src/android/view/inputmethod/CursorAnchorInfoTest.java
diff --git a/core/tests/coretests/src/android/view/inputmethod/DeleteRangeGestureTest.java b/core/tests/InputMethodCoreTests/src/android/view/inputmethod/DeleteRangeGestureTest.java
similarity index 100%
rename from core/tests/coretests/src/android/view/inputmethod/DeleteRangeGestureTest.java
rename to core/tests/InputMethodCoreTests/src/android/view/inputmethod/DeleteRangeGestureTest.java
diff --git a/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java b/core/tests/InputMethodCoreTests/src/android/view/inputmethod/EditorInfoTest.java
similarity index 100%
rename from core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java
rename to core/tests/InputMethodCoreTests/src/android/view/inputmethod/EditorInfoTest.java
diff --git a/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java b/core/tests/InputMethodCoreTests/src/android/view/inputmethod/InputMethodInfoTest.java
similarity index 98%
rename from core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java
rename to core/tests/InputMethodCoreTests/src/android/view/inputmethod/InputMethodInfoTest.java
index 909af7b..a3f537e 100644
--- a/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java
+++ b/core/tests/InputMethodCoreTests/src/android/view/inputmethod/InputMethodInfoTest.java
@@ -32,7 +32,7 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.frameworks.coretests.R;
+import com.android.frameworks.inputmethodcoretests.R;
import org.junit.Rule;
import org.junit.Test;
diff --git a/core/tests/coretests/src/android/view/inputmethod/InputMethodManagerTest.java b/core/tests/InputMethodCoreTests/src/android/view/inputmethod/InputMethodManagerTest.java
similarity index 100%
rename from core/tests/coretests/src/android/view/inputmethod/InputMethodManagerTest.java
rename to core/tests/InputMethodCoreTests/src/android/view/inputmethod/InputMethodManagerTest.java
diff --git a/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java b/core/tests/InputMethodCoreTests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java
similarity index 100%
rename from core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java
rename to core/tests/InputMethodCoreTests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java
diff --git a/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeTest.java b/core/tests/InputMethodCoreTests/src/android/view/inputmethod/InputMethodSubtypeTest.java
similarity index 100%
rename from core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeTest.java
rename to core/tests/InputMethodCoreTests/src/android/view/inputmethod/InputMethodSubtypeTest.java
diff --git a/core/tests/coretests/src/android/view/inputmethod/InsertGestureTest.java b/core/tests/InputMethodCoreTests/src/android/view/inputmethod/InsertGestureTest.java
similarity index 100%
rename from core/tests/coretests/src/android/view/inputmethod/InsertGestureTest.java
rename to core/tests/InputMethodCoreTests/src/android/view/inputmethod/InsertGestureTest.java
diff --git a/core/tests/coretests/src/android/view/inputmethod/InsertModeGestureTest.java b/core/tests/InputMethodCoreTests/src/android/view/inputmethod/InsertModeGestureTest.java
similarity index 100%
rename from core/tests/coretests/src/android/view/inputmethod/InsertModeGestureTest.java
rename to core/tests/InputMethodCoreTests/src/android/view/inputmethod/InsertModeGestureTest.java
diff --git a/core/tests/coretests/src/android/view/inputmethod/ParcelableHandwritingGestureTest.java b/core/tests/InputMethodCoreTests/src/android/view/inputmethod/ParcelableHandwritingGestureTest.java
similarity index 100%
rename from core/tests/coretests/src/android/view/inputmethod/ParcelableHandwritingGestureTest.java
rename to core/tests/InputMethodCoreTests/src/android/view/inputmethod/ParcelableHandwritingGestureTest.java
diff --git a/core/tests/coretests/src/android/view/inputmethod/SelectGestureTest.java b/core/tests/InputMethodCoreTests/src/android/view/inputmethod/SelectGestureTest.java
similarity index 100%
rename from core/tests/coretests/src/android/view/inputmethod/SelectGestureTest.java
rename to core/tests/InputMethodCoreTests/src/android/view/inputmethod/SelectGestureTest.java
diff --git a/core/tests/coretests/src/android/view/inputmethod/SelectRangeGestureTest.java b/core/tests/InputMethodCoreTests/src/android/view/inputmethod/SelectRangeGestureTest.java
similarity index 100%
rename from core/tests/coretests/src/android/view/inputmethod/SelectRangeGestureTest.java
rename to core/tests/InputMethodCoreTests/src/android/view/inputmethod/SelectRangeGestureTest.java
diff --git a/core/tests/coretests/src/android/view/inputmethod/SparseRectFArrayTest.java b/core/tests/InputMethodCoreTests/src/android/view/inputmethod/SparseRectFArrayTest.java
similarity index 100%
rename from core/tests/coretests/src/android/view/inputmethod/SparseRectFArrayTest.java
rename to core/tests/InputMethodCoreTests/src/android/view/inputmethod/SparseRectFArrayTest.java
diff --git a/core/tests/coretests/src/android/view/inputmethod/SurroundingTextTest.java b/core/tests/InputMethodCoreTests/src/android/view/inputmethod/SurroundingTextTest.java
similarity index 100%
rename from core/tests/coretests/src/android/view/inputmethod/SurroundingTextTest.java
rename to core/tests/InputMethodCoreTests/src/android/view/inputmethod/SurroundingTextTest.java
diff --git a/core/tests/coretests/src/android/view/inputmethod/TextAppearanceInfoTest.java b/core/tests/InputMethodCoreTests/src/android/view/inputmethod/TextAppearanceInfoTest.java
similarity index 100%
rename from core/tests/coretests/src/android/view/inputmethod/TextAppearanceInfoTest.java
rename to core/tests/InputMethodCoreTests/src/android/view/inputmethod/TextAppearanceInfoTest.java
diff --git a/core/tests/coretests/src/com/android/internal/inputmethod/CompletableFutureUtilTest.kt b/core/tests/InputMethodCoreTests/src/com/android/internal/inputmethod/CompletableFutureUtilTest.kt
similarity index 100%
rename from core/tests/coretests/src/com/android/internal/inputmethod/CompletableFutureUtilTest.kt
rename to core/tests/InputMethodCoreTests/src/com/android/internal/inputmethod/CompletableFutureUtilTest.kt
diff --git a/core/tests/coretests/src/com/android/internal/inputmethod/InputConnectionWrapperTest.java b/core/tests/InputMethodCoreTests/src/com/android/internal/inputmethod/InputConnectionWrapperTest.java
similarity index 100%
rename from core/tests/coretests/src/com/android/internal/inputmethod/InputConnectionWrapperTest.java
rename to core/tests/InputMethodCoreTests/src/com/android/internal/inputmethod/InputConnectionWrapperTest.java
diff --git a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodDebugTest.java b/core/tests/InputMethodCoreTests/src/com/android/internal/inputmethod/InputMethodDebugTest.java
similarity index 100%
rename from core/tests/coretests/src/com/android/internal/inputmethod/InputMethodDebugTest.java
rename to core/tests/InputMethodCoreTests/src/com/android/internal/inputmethod/InputMethodDebugTest.java
diff --git a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodSubtypeHandleTest.java b/core/tests/InputMethodCoreTests/src/com/android/internal/inputmethod/InputMethodSubtypeHandleTest.java
similarity index 100%
rename from core/tests/coretests/src/com/android/internal/inputmethod/InputMethodSubtypeHandleTest.java
rename to core/tests/InputMethodCoreTests/src/com/android/internal/inputmethod/InputMethodSubtypeHandleTest.java
diff --git a/core/tests/coretests/src/com/android/internal/inputmethod/SubtypeLocaleUtilsTest.java b/core/tests/InputMethodCoreTests/src/com/android/internal/inputmethod/SubtypeLocaleUtilsTest.java
similarity index 100%
rename from core/tests/coretests/src/com/android/internal/inputmethod/SubtypeLocaleUtilsTest.java
rename to core/tests/InputMethodCoreTests/src/com/android/internal/inputmethod/SubtypeLocaleUtilsTest.java
diff --git a/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt b/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt
index e32a57b..a2a5433 100644
--- a/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt
+++ b/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt
@@ -145,7 +145,6 @@
fun unnecessaryFontScalesReturnsNull() {
assertThat(FontScaleConverterFactory.forScale(0F)).isNull()
assertThat(FontScaleConverterFactory.forScale(1F)).isNull()
- assertThat(FontScaleConverterFactory.forScale(1.1F)).isNull()
assertThat(FontScaleConverterFactory.forScale(0.85F)).isNull()
}
@@ -176,7 +175,7 @@
assertThat(FontScaleConverterFactory.isNonLinearFontScalingActive(-1f)).isFalse()
assertThat(FontScaleConverterFactory.isNonLinearFontScalingActive(0.85f)).isFalse()
assertThat(FontScaleConverterFactory.isNonLinearFontScalingActive(1.02f)).isFalse()
- assertThat(FontScaleConverterFactory.isNonLinearFontScalingActive(1.10f)).isFalse()
+ assertThat(FontScaleConverterFactory.isNonLinearFontScalingActive(1.10f)).isTrue()
assertThat(FontScaleConverterFactory.isNonLinearFontScalingActive(1.15f)).isTrue()
assertThat(FontScaleConverterFactory.isNonLinearFontScalingActive(1.1499999f))
.isTrue()
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 2de305f..62c9e16 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -450,7 +450,7 @@
<!-- Permissions required for CTS test - android.server.biometrics -->
<permission name="android.permission.USE_BIOMETRIC" />
<permission name="android.permission.TEST_BIOMETRIC" />
- <permission name="android.permission.MANAGE_BIOMETRIC_DIALOG" />
+ <permission name="android.permission.SET_BIOMETRIC_DIALOG_LOGO" />
<permission name="android.permission.USE_BACKGROUND_FACE_AUTHENTICATION" />
<!-- Permissions required for CTS test - CtsContactsProviderTestCases -->
<permission name="android.contacts.permission.MANAGE_SIM_ACCOUNTS" />
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
index 83ddfc5..9c05a3a 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -109,29 +109,13 @@
}
}
- // For curve 25519, KeyMint uses the KM_ALGORITHM_EC constant, but in the Java layer we need
- // to distinguish between Curve 25519 and other EC algorithms, so we use a different constant
- // with a value that is outside the range of the enum used for KeyMint algorithms.
- private static final int ALGORITHM_XDH = KeymasterDefs.KM_ALGORITHM_EC + 1200;
- private static final int ALGORITHM_ED25519 = ALGORITHM_XDH + 1;
-
/**
- * XDH represents Curve 25519 agreement key provider.
+ * XDH represents Curve 25519 providers.
*/
public static class XDH extends AndroidKeyStoreKeyPairGeneratorSpi {
// XDH is treated as EC.
public XDH() {
- super(ALGORITHM_XDH);
- }
- }
-
- /**
- * ED25519 represents Curve 25519 signing key provider.
- */
- public static class ED25519 extends AndroidKeyStoreKeyPairGeneratorSpi {
- // ED25519 is treated as EC.
- public ED25519() {
- super(ALGORITHM_ED25519);
+ super(KeymasterDefs.KM_ALGORITHM_EC);
}
}
@@ -257,9 +241,7 @@
KeyGenParameterSpec spec;
boolean encryptionAtRestRequired = false;
- int keymasterAlgorithm = (mOriginalKeymasterAlgorithm == ALGORITHM_XDH
- || mOriginalKeymasterAlgorithm == ALGORITHM_ED25519)
- ? KeymasterDefs.KM_ALGORITHM_EC : mOriginalKeymasterAlgorithm;
+ int keymasterAlgorithm = mOriginalKeymasterAlgorithm;
if (params instanceof KeyGenParameterSpec) {
spec = (KeyGenParameterSpec) params;
} else if (params instanceof KeyPairGeneratorSpec) {
@@ -628,15 +610,6 @@
if (algSpecificSpec instanceof ECGenParameterSpec) {
ECGenParameterSpec ecSpec = (ECGenParameterSpec) algSpecificSpec;
mEcCurveName = ecSpec.getName();
- if (mOriginalKeymasterAlgorithm == ALGORITHM_XDH
- && !mEcCurveName.equalsIgnoreCase("x25519")) {
- throw new InvalidAlgorithmParameterException("XDH algorithm only supports"
- + " x25519 curve.");
- } else if (mOriginalKeymasterAlgorithm == ALGORITHM_ED25519
- && !mEcCurveName.equalsIgnoreCase("ed25519")) {
- throw new InvalidAlgorithmParameterException("Ed25519 algorithm only"
- + " supports ed25519 curve.");
- }
final Integer ecSpecKeySizeBits = SUPPORTED_EC_CURVE_NAME_TO_SIZE.get(
mEcCurveName.toLowerCase(Locale.US));
if (ecSpecKeySizeBits == null) {
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
index d204f13..11278e8 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
@@ -86,14 +86,11 @@
put("KeyPairGenerator.EC", PACKAGE_NAME + ".AndroidKeyStoreKeyPairGeneratorSpi$EC");
put("KeyPairGenerator.RSA", PACKAGE_NAME + ".AndroidKeyStoreKeyPairGeneratorSpi$RSA");
put("KeyPairGenerator.XDH", PACKAGE_NAME + ".AndroidKeyStoreKeyPairGeneratorSpi$XDH");
- put("KeyPairGenerator.ED25519", PACKAGE_NAME
- + ".AndroidKeyStoreKeyPairGeneratorSpi$ED25519");
// java.security.KeyFactory
putKeyFactoryImpl("EC");
putKeyFactoryImpl("RSA");
putKeyFactoryImpl("XDH");
- putKeyFactoryImpl("ED25519");
// javax.crypto.KeyGenerator
put("KeyGenerator.AES", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$AES");
diff --git a/libs/WindowManager/Shell/multivalentTests/OWNERS b/libs/WindowManager/Shell/multivalentTests/OWNERS
new file mode 100644
index 0000000..24c1a3a
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentTests/OWNERS
@@ -0,0 +1,4 @@
+atsjenk@google.com
+liranb@google.com
+madym@google.com
+
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDoubleTapHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDoubleTapHelper.java
similarity index 92%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDoubleTapHelper.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDoubleTapHelper.java
index 1b1ebc3..4cbb78f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDoubleTapHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDoubleTapHelper.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,14 +14,12 @@
* limitations under the License.
*/
-package com.android.wm.shell.pip.phone;
+package com.android.wm.shell.common.pip;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.graphics.Rect;
-import com.android.wm.shell.common.pip.PipBoundsState;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -50,9 +48,9 @@
@Retention(RetentionPolicy.SOURCE)
@interface PipSizeSpec {}
- static final int SIZE_SPEC_DEFAULT = 0;
- static final int SIZE_SPEC_MAX = 1;
- static final int SIZE_SPEC_CUSTOM = 2;
+ public static final int SIZE_SPEC_DEFAULT = 0;
+ public static final int SIZE_SPEC_MAX = 1;
+ public static final int SIZE_SPEC_CUSTOM = 2;
/**
* Returns MAX or DEFAULT {@link PipSizeSpec} to toggle to/from.
@@ -84,7 +82,7 @@
* @return pip screen size to switch to
*/
@PipSizeSpec
- static int nextSizeSpec(@NonNull PipBoundsState mPipBoundsState,
+ public static int nextSizeSpec(@NonNull PipBoundsState mPipBoundsState,
@NonNull Rect userResizeBounds) {
// is pip screen at its maximum
boolean isScreenMax = mPipBoundsState.getBounds().width()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
index 3b48c67..7b98fa6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
@@ -50,15 +50,16 @@
public abstract class Pip2Module {
@WMSingleton
@Provides
- static PipTransition providePipTransition(@NonNull ShellInit shellInit,
+ static PipTransition providePipTransition(Context context,
+ @NonNull ShellInit shellInit,
@NonNull ShellTaskOrganizer shellTaskOrganizer,
@NonNull Transitions transitions,
PipBoundsState pipBoundsState,
PipBoundsAlgorithm pipBoundsAlgorithm,
Optional<PipController> pipController,
@NonNull PipScheduler pipScheduler) {
- return new PipTransition(shellInit, shellTaskOrganizer, transitions, pipBoundsState, null,
- pipBoundsAlgorithm, pipScheduler);
+ return new PipTransition(context, shellInit, shellTaskOrganizer, transitions,
+ pipBoundsState, null, pipBoundsAlgorithm, pipScheduler);
}
@WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
index 04911c0..0e70736 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
@@ -47,6 +47,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
+import java.util.function.Consumer;
/**
* Responsible supplying PiP Transitions.
@@ -116,6 +117,17 @@
}
/**
+ * Called when the Shell wants to start resizing Pip transition/animation.
+ *
+ * @param onFinishResizeCallback callback guaranteed to execute when animation ends and
+ * client completes any potential draws upon WM state updates.
+ */
+ public void startResizeTransition(WindowContainerTransaction wct,
+ Consumer<Rect> onFinishResizeCallback) {
+ // Default implementation does nothing.
+ }
+
+ /**
* Called when the transition animation can't continue (eg. task is removed during
* animation)
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index 452a416..81705e2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -52,6 +52,7 @@
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipDoubleTapHelper;
import com.android.wm.shell.common.pip.PipUiEventLogger;
import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.common.pip.SizeSpecSource;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
index 0b8f60e..57b73b3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
@@ -24,10 +24,12 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.graphics.Rect;
import android.view.SurfaceControl;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
+import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
@@ -36,6 +38,10 @@
import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.pip.PipTransitionController;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.function.Consumer;
+
/**
* Scheduler for Shell initiated PiP transitions and animations.
*/
@@ -58,13 +64,37 @@
private SurfaceControl mPinnedTaskLeash;
/**
- * A temporary broadcast receiver to initiate exit PiP via expand.
- * This will later be modified to be triggered by the PiP menu.
+ * Temporary PiP CUJ codes to schedule PiP related transitions directly from Shell.
+ * This is used for a broadcast receiver to resolve intents. This should be removed once
+ * there is an equivalent of PipTouchHandler and PipResizeGestureHandler for PiP2.
+ */
+ private static final int PIP_EXIT_VIA_EXPAND_CODE = 0;
+ private static final int PIP_DOUBLE_TAP = 1;
+
+ @IntDef(value = {
+ PIP_EXIT_VIA_EXPAND_CODE,
+ PIP_DOUBLE_TAP
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface PipUserJourneyCode {}
+
+ /**
+ * A temporary broadcast receiver to initiate PiP CUJs.
*/
private class PipSchedulerReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
- scheduleExitPipViaExpand();
+ int userJourneyCode = intent.getIntExtra("cuj_code_extra", 0);
+ switch (userJourneyCode) {
+ case PIP_EXIT_VIA_EXPAND_CODE:
+ scheduleExitPipViaExpand();
+ break;
+ case PIP_DOUBLE_TAP:
+ scheduleDoubleTapToResize();
+ break;
+ default:
+ throw new IllegalStateException("unexpected CUJ code=" + userJourneyCode);
+ }
}
}
@@ -121,6 +151,23 @@
}
}
+ /**
+ * Schedules resize PiP via double tap.
+ */
+ public void scheduleDoubleTapToResize() {}
+
+ /**
+ * Animates resizing of the pinned stack given the duration.
+ */
+ public void scheduleAnimateResizePip(Rect toBounds, Consumer<Rect> onFinishResizeCallback) {
+ if (mPipTaskToken == null) {
+ return;
+ }
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.setBounds(mPipTaskToken, toBounds);
+ mPipTransitionController.startResizeTransition(wct, onFinishResizeCallback);
+ }
+
void onExitPip() {
mPipTaskToken = null;
mPinnedTaskLeash = null;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
index 3b0e7c1..f3d178a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
@@ -22,10 +22,12 @@
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_RESIZE_PIP;
import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.PictureInPictureParams;
+import android.content.Context;
import android.graphics.Rect;
import android.os.IBinder;
import android.view.SurfaceControl;
@@ -36,6 +38,7 @@
import androidx.annotation.Nullable;
+import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
import com.android.wm.shell.common.pip.PipBoundsState;
@@ -45,25 +48,29 @@
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
+import java.util.function.Consumer;
+
/**
* Implementation of transitions for PiP on phone.
*/
public class PipTransition extends PipTransitionController {
private static final String TAG = PipTransition.class.getSimpleName();
+ private final Context mContext;
private final PipScheduler mPipScheduler;
@Nullable
private WindowContainerToken mPipTaskToken;
@Nullable
private IBinder mEnterTransition;
@Nullable
- private IBinder mAutoEnterButtonNavTransition;
- @Nullable
private IBinder mExitViaExpandTransition;
@Nullable
- private IBinder mLegacyEnterTransition;
+ private IBinder mResizeTransition;
+
+ private Consumer<Rect> mFinishResizeCallback;
public PipTransition(
+ Context context,
@NonNull ShellInit shellInit,
@NonNull ShellTaskOrganizer shellTaskOrganizer,
@NonNull Transitions transitions,
@@ -74,6 +81,7 @@
super(shellInit, shellTaskOrganizer, transitions, pipBoundsState, pipMenuController,
pipBoundsAlgorithm);
+ mContext = context;
mPipScheduler = pipScheduler;
mPipScheduler.setPipTransitionController(this);
}
@@ -87,7 +95,7 @@
@Override
public void startExitTransition(int type, WindowContainerTransaction out,
- @android.annotation.Nullable Rect destinationBounds) {
+ @Nullable Rect destinationBounds) {
if (out == null) {
return;
}
@@ -97,6 +105,16 @@
}
}
+ @Override
+ public void startResizeTransition(WindowContainerTransaction wct,
+ Consumer<Rect> onFinishResizeCallback) {
+ if (wct == null) {
+ return;
+ }
+ mResizeTransition = mTransitions.startTransition(TRANSIT_RESIZE_PIP, wct, this);
+ mFinishResizeCallback = onFinishResizeCallback;
+ }
+
@Nullable
@Override
public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
@@ -126,43 +144,6 @@
public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
@Nullable SurfaceControl.Transaction finishT) {}
- private WindowContainerTransaction getEnterPipTransaction(@NonNull IBinder transition,
- @NonNull TransitionRequestInfo request) {
- // cache the original task token to check for multi-activity case later
- final ActivityManager.RunningTaskInfo pipTask = request.getPipTask();
- PictureInPictureParams pipParams = pipTask.pictureInPictureParams;
- mPipBoundsState.setBoundsStateForEntry(pipTask.topActivity, pipTask.topActivityInfo,
- pipParams, mPipBoundsAlgorithm);
-
- // calculate the entry bounds and notify core to move task to pinned with final bounds
- final Rect entryBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
- mPipBoundsState.setBounds(entryBounds);
-
- WindowContainerTransaction wct = new WindowContainerTransaction();
- wct.movePipActivityToPinnedRootTask(pipTask.token, entryBounds);
- return wct;
- }
-
- private boolean isAutoEnterInButtonNavigation(@NonNull TransitionRequestInfo requestInfo) {
- final ActivityManager.RunningTaskInfo pipTask = requestInfo.getPipTask();
- if (pipTask == null) {
- return false;
- }
- if (pipTask.pictureInPictureParams == null) {
- return false;
- }
-
- // Assuming auto-enter is enabled and pipTask is non-null, the TRANSIT_OPEN request type
- // implies that we are entering PiP in button navigation mode. This is guaranteed by
- // TaskFragment#startPausing()` in Core which wouldn't get called in gesture nav.
- return requestInfo.getType() == TRANSIT_OPEN
- && pipTask.pictureInPictureParams.isAutoEnterEnabled();
- }
-
- private boolean isEnterPictureInPictureModeRequest(@NonNull TransitionRequestInfo requestInfo) {
- return requestInfo.getType() == TRANSIT_PIP;
- }
-
@Override
public boolean startAnimation(@NonNull IBinder transition,
@NonNull TransitionInfo info,
@@ -182,16 +163,48 @@
} else if (transition == mExitViaExpandTransition) {
mExitViaExpandTransition = null;
return startExpandAnimation(info, startTransaction, finishTransaction, finishCallback);
+ } else if (transition == mResizeTransition) {
+ mResizeTransition = null;
+ return startResizeAnimation(info, startTransaction, finishTransaction, finishCallback);
}
return false;
}
- private boolean isLegacyEnter(@NonNull TransitionInfo info) {
+ private boolean startResizeAnimation(@NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
TransitionInfo.Change pipChange = getPipChange(info);
- // If the only change in the changes list is a TO_FRONT mode PiP task,
- // then this is legacy-enter PiP.
- return pipChange != null && pipChange.getMode() == TRANSIT_TO_FRONT
- && info.getChanges().size() == 1;
+ if (pipChange == null) {
+ return false;
+ }
+ SurfaceControl pipLeash = pipChange.getLeash();
+ Rect destinationBounds = pipChange.getEndAbsBounds();
+
+ // Even though the final bounds and crop are applied with finishTransaction since
+ // this is a visible change, we still need to handle the app draw coming in. Snapshot
+ // covering app draw during collection will be removed by startTransaction. So we make
+ // the crop equal to the final bounds and then scale the leash back to starting bounds.
+ startTransaction.setWindowCrop(pipLeash, pipChange.getEndAbsBounds().width(),
+ pipChange.getEndAbsBounds().height());
+ startTransaction.setScale(pipLeash,
+ (float) mPipBoundsState.getBounds().width() / destinationBounds.width(),
+ (float) mPipBoundsState.getBounds().height() / destinationBounds.height());
+ startTransaction.apply();
+
+ finishTransaction.setScale(pipLeash,
+ (float) mPipBoundsState.getBounds().width() / destinationBounds.width(),
+ (float) mPipBoundsState.getBounds().height() / destinationBounds.height());
+
+ // We are done with the transition, but will continue animating leash to final bounds.
+ finishCallback.onTransitionFinished(null);
+
+ // Animate the pip leash with the new buffer
+ final int duration = mContext.getResources().getInteger(
+ R.integer.config_pipResizeAnimationDuration);
+ // TODO: b/275910498 Couple this routine with a new implementation of the PiP animator.
+ startResizeAnimation(pipLeash, mPipBoundsState.getBounds(), destinationBounds, duration);
+ return true;
}
private boolean startBoundsTypeEnterAnimation(@NonNull TransitionInfo info,
@@ -251,6 +264,57 @@
return null;
}
+ private WindowContainerTransaction getEnterPipTransaction(@NonNull IBinder transition,
+ @NonNull TransitionRequestInfo request) {
+ // cache the original task token to check for multi-activity case later
+ final ActivityManager.RunningTaskInfo pipTask = request.getPipTask();
+ PictureInPictureParams pipParams = pipTask.pictureInPictureParams;
+ mPipBoundsState.setBoundsStateForEntry(pipTask.topActivity, pipTask.topActivityInfo,
+ pipParams, mPipBoundsAlgorithm);
+
+ // calculate the entry bounds and notify core to move task to pinned with final bounds
+ final Rect entryBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
+ mPipBoundsState.setBounds(entryBounds);
+
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.movePipActivityToPinnedRootTask(pipTask.token, entryBounds);
+ return wct;
+ }
+
+ private boolean isAutoEnterInButtonNavigation(@NonNull TransitionRequestInfo requestInfo) {
+ final ActivityManager.RunningTaskInfo pipTask = requestInfo.getPipTask();
+ if (pipTask == null) {
+ return false;
+ }
+ if (pipTask.pictureInPictureParams == null) {
+ return false;
+ }
+
+ // Assuming auto-enter is enabled and pipTask is non-null, the TRANSIT_OPEN request type
+ // implies that we are entering PiP in button navigation mode. This is guaranteed by
+ // TaskFragment#startPausing()` in Core which wouldn't get called in gesture nav.
+ return requestInfo.getType() == TRANSIT_OPEN
+ && pipTask.pictureInPictureParams.isAutoEnterEnabled();
+ }
+
+ private boolean isEnterPictureInPictureModeRequest(@NonNull TransitionRequestInfo requestInfo) {
+ return requestInfo.getType() == TRANSIT_PIP;
+ }
+
+ private boolean isLegacyEnter(@NonNull TransitionInfo info) {
+ TransitionInfo.Change pipChange = getPipChange(info);
+ // If the only change in the changes list is a TO_FRONT mode PiP task,
+ // then this is legacy-enter PiP.
+ return pipChange != null && pipChange.getMode() == TRANSIT_TO_FRONT
+ && info.getChanges().size() == 1;
+ }
+
+ /**
+ * TODO: b/275910498 Use a new implementation of the PiP animator here.
+ */
+ private void startResizeAnimation(SurfaceControl leash, Rect startBounds,
+ Rect endBounds, int duration) {}
+
private void onExitPip() {
mPipTaskToken = null;
mPipScheduler.onExitPip();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
index 253acc4..0ca244c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
@@ -158,5 +158,10 @@
* does not expect split to currently be running.
*/
RemoteAnimationTarget[] onStartingSplitLegacy(in RemoteAnimationTarget[] appTargets) = 14;
+
+ /**
+ * Reverse the split.
+ */
+ oneway void switchSplitPosition() = 22;
}
-// Last id = 21
\ No newline at end of file
+// Last id = 22
\ No newline at end of file
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 2ec52bb..70cb2fc 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
@@ -1109,6 +1109,12 @@
mStageCoordinator.onDroppedToSplit(position, dragSessionId);
}
+ void switchSplitPosition(String reason) {
+ if (isSplitScreenVisible()) {
+ mStageCoordinator.switchSplitPosition(reason);
+ }
+ }
+
/**
* Return the {@param exitReason} as a string.
*/
@@ -1473,5 +1479,11 @@
true /* blocking */);
return out[0];
}
+
+ @Override
+ public void switchSplitPosition() {
+ executeRemoteCallWithTaskPermission(mController, "switchSplitPosition",
+ (controller) -> controller.switchSplitPosition("remoteCall"));
+ }
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenShellCommandHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenShellCommandHandler.java
index 7fd03a9..7f16c5e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenShellCommandHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenShellCommandHandler.java
@@ -43,6 +43,8 @@
return runRemoveFromSideStage(args, pw);
case "setSideStagePosition":
return runSetSideStagePosition(args, pw);
+ case "switchSplitPosition":
+ return runSwitchSplitPosition();
default:
pw.println("Invalid command: " + args[0]);
return false;
@@ -84,6 +86,11 @@
return true;
}
+ private boolean runSwitchSplitPosition() {
+ mController.switchSplitPosition("shellCommand");
+ return true;
+ }
+
@Override
public void printShellCommandHelp(PrintWriter pw, String prefix) {
pw.println(prefix + "moveToSideStage <taskId> <SideStagePosition>");
@@ -92,5 +99,7 @@
pw.println(prefix + " Remove a task with given id in split-screen mode.");
pw.println(prefix + "setSideStagePosition <SideStagePosition>");
pw.println(prefix + " Sets the position of the side-stage.");
+ pw.println(prefix + "switchSplitPosition");
+ pw.println(prefix + " Reverses the split.");
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index 3fb0dbf..67fc7e2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -175,6 +175,9 @@
/** Transition to animate task to desktop. */
public static final int TRANSIT_MOVE_TO_DESKTOP = WindowManager.TRANSIT_FIRST_CUSTOM + 15;
+ /** Transition to resize PiP task. */
+ public static final int TRANSIT_RESIZE_PIP = TRANSIT_FIRST_CUSTOM + 16;
+
private final ShellTaskOrganizer mOrganizer;
private final Context mContext;
private final ShellExecutor mMainExecutor;
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 554b1fb..4ba05ce 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
@@ -32,6 +32,7 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
+import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityTaskManager;
@@ -60,7 +61,6 @@
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
-import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
@@ -544,12 +544,22 @@
return true;
}
+ /**
+ * Perform a task size toggle on release of the double-tap, assuming no drag event
+ * was handled during the double-tap.
+ * @param e The motion event that occurred during the double-tap gesture.
+ * @return true if the event should be consumed, false if not
+ */
@Override
- public boolean onDoubleTap(@NonNull MotionEvent e) {
+ public boolean onDoubleTapEvent(@NonNull MotionEvent e) {
+ final int action = e.getActionMasked();
+ if (mIsDragging || (action != MotionEvent.ACTION_UP
+ && action != MotionEvent.ACTION_CANCEL)) {
+ return false;
+ }
final RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
- mDesktopTasksController.ifPresent(c -> {
- c.toggleDesktopTaskSize(taskInfo, mWindowDecorByTaskId.get(taskInfo.taskId));
- });
+ mDesktopTasksController.ifPresent(c -> c.toggleDesktopTaskSize(taskInfo,
+ mWindowDecorByTaskId.get(taskInfo.taskId)));
return true;
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt
index 47bff8d..0d18535 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt
@@ -78,6 +78,14 @@
uiAutomation.dropShellPermissionIdentity()
}
+ override fun onProcessStarted(
+ pid: Int,
+ processUid: Int,
+ packageUid: Int,
+ packageName: String,
+ processName: String
+ ) {}
+
override fun onForegroundActivitiesChanged(pid: Int, uid: Int, foreground: Boolean) {}
override fun onForegroundServicesChanged(pid: Int, uid: Int, serviceTypes: Int) {}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipDoubleTapHelperTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipDoubleTapHelperTest.java
index 0f8db85..b583acd 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipDoubleTapHelperTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipDoubleTapHelperTest.java
@@ -16,10 +16,10 @@
package com.android.wm.shell.pip.phone;
-import static com.android.wm.shell.pip.phone.PipDoubleTapHelper.SIZE_SPEC_CUSTOM;
-import static com.android.wm.shell.pip.phone.PipDoubleTapHelper.SIZE_SPEC_DEFAULT;
-import static com.android.wm.shell.pip.phone.PipDoubleTapHelper.SIZE_SPEC_MAX;
-import static com.android.wm.shell.pip.phone.PipDoubleTapHelper.nextSizeSpec;
+import static com.android.wm.shell.common.pip.PipDoubleTapHelper.SIZE_SPEC_CUSTOM;
+import static com.android.wm.shell.common.pip.PipDoubleTapHelper.SIZE_SPEC_DEFAULT;
+import static com.android.wm.shell.common.pip.PipDoubleTapHelper.SIZE_SPEC_MAX;
+import static com.android.wm.shell.common.pip.PipDoubleTapHelper.nextSizeSpec;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -30,6 +30,7 @@
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipDoubleTapHelper;
import org.junit.Assert;
import org.junit.Before;
@@ -38,7 +39,7 @@
import org.mockito.Mock;
/**
- * Unit test against {@link PipDoubleTapHelper}.
+ * Unit test against {@link com.android.wm.shell.common.pip.PipDoubleTapHelper}.
*/
@RunWith(AndroidTestingRunner.class)
public class PipDoubleTapHelperTest extends ShellTestCase {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
index 12a5594..7f3bfbb 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
@@ -421,6 +421,15 @@
assertEquals(false, controller.supportsMultiInstanceSplit(component));
}
+ @Test
+ public void testSwitchSplitPosition_checksIsSplitScreenVisible() {
+ final String reason = "test";
+ when(mSplitScreenController.isSplitScreenVisible()).thenReturn(true, false);
+ mSplitScreenController.switchSplitPosition(reason);
+ mSplitScreenController.switchSplitPosition(reason);
+ verify(mStageCoordinator, times(1)).switchSplitPosition(reason);
+ }
+
private Intent createStartIntent(String activityName) {
Intent intent = new Intent();
intent.setComponent(new ComponentName(mContext, activityName));
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index b40b73c..0abb6f5 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -229,15 +229,6 @@
path: "apex/java",
}
-java_api_contribution {
- name: "framework-graphics-public-stubs",
- api_surface: "public",
- api_file: "api/current.txt",
- visibility: [
- "//build/orchestrator/apis",
- ],
-}
-
// ------------------------
// APEX
// ------------------------
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 691aa77..425db06 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -183,23 +183,23 @@
* preference} passed by a proxy router. Use {@link RouteDiscoveryPreference#EMPTY} when
* setting a route callback.
* <li>
- * <p>Methods returning non-system {@link RoutingController controllers} always return
- * new instances with the latest data. Do not attempt to compare or store them. Instead,
- * use {@link #getController(String)} or {@link #getControllers()} to query the most
+ * <p>Methods returning non-system {@link RoutingController controllers} always return new
+ * instances with the latest data. Do not attempt to compare or store them. Instead, use
+ * {@link #getController(String)} or {@link #getControllers()} to query the most
* up-to-date state.
* <li>
* <p>Calls to {@link #setOnGetControllerHintsListener} are ignored.
* </ul>
*
* @param clientPackageName the package name of the app to control
- * @throws SecurityException if the caller doesn't have {@link
- * Manifest.permission#MEDIA_CONTENT_CONTROL MEDIA_CONTENT_CONTROL} permission.
- * @hide
+ * @return a proxy MediaRouter2 instance if {@code clientPackageName} exists or {@code null}.
*/
- // TODO (b/311711420): Deprecate once #getInstance(Context, String, UserHandle) reaches public
- // SDK.
- @SystemApi
- @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL)
+ @FlaggedApi(FLAG_ENABLE_CROSS_USER_ROUTING_IN_MEDIA_ROUTER2)
+ @RequiresPermission(
+ anyOf = {
+ Manifest.permission.MEDIA_CONTENT_CONTROL,
+ Manifest.permission.MEDIA_ROUTING_CONTROL
+ })
@Nullable
public static MediaRouter2 getInstance(
@NonNull Context context, @NonNull String clientPackageName) {
@@ -226,9 +226,9 @@
* {@link RouteDiscoveryPreference.Builder#setPreferredFeatures(List) preferred features}
* when setting a route callback.
* <li>
- * <p>Methods returning non-system {@link RoutingController controllers} always return
- * new instances with the latest data. Do not attempt to compare or store them. Instead,
- * use {@link #getController(String)} or {@link #getControllers()} to query the most
+ * <p>Methods returning non-system {@link RoutingController controllers} always return new
+ * instances with the latest data. Do not attempt to compare or store them. Instead, use
+ * {@link #getController(String)} or {@link #getControllers()} to query the most
* up-to-date state.
* <li>
* <p>Calls to {@link #setOnGetControllerHintsListener} are ignored.
@@ -242,8 +242,8 @@
* @throws SecurityException if {@code user} does not match {@link Process#myUserHandle()} and
* the caller does not hold {@code Manifest.permission#INTERACT_ACROSS_USERS_FULL}.
* @throws IllegalArgumentException if {@code clientPackageName} does not exist in {@code user}.
+ * @hide
*/
- @FlaggedApi(FLAG_ENABLE_CROSS_USER_ROUTING_IN_MEDIA_ROUTER2)
@RequiresPermission(
anyOf = {
Manifest.permission.MEDIA_CONTENT_CONTROL,
@@ -251,9 +251,7 @@
})
@NonNull
public static MediaRouter2 getInstance(
- @NonNull Context context,
- @NonNull String clientPackageName,
- @NonNull UserHandle user) {
+ @NonNull Context context, @NonNull String clientPackageName, @NonNull UserHandle user) {
return findOrCreateProxyInstanceForCallingUser(context, clientPackageName, user);
}
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl
index e3dba03..7b58531 100644
--- a/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl
@@ -61,7 +61,8 @@
void onSetTvRecordingInfo(in String recordingId, in TvRecordingInfo recordingInfo, int seq);
void onRequestTvRecordingInfo(in String recordingId, int seq);
void onRequestTvRecordingInfoList(in int type, int seq);
- void onRequestSigning(
- in String id, in String algorithm, in String alias, in byte[] data, int seq);
+ void onRequestSigning(in String id, in String algorithm, in String alias, in byte[] data,
+ int seq);
+ void onRequestCertificate(in String host, int port, int seq);
void onAdRequest(in AdRequest request, int Seq);
}
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppManager.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppManager.aidl
index 0f58b29..1b9450b 100644
--- a/media/java/android/media/tv/interactive/ITvInteractiveAppManager.aidl
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppManager.aidl
@@ -58,6 +58,8 @@
void sendAvailableSpeeds(in IBinder sessionToken, in float[] speeds, int userId);
void sendSigningResult(in IBinder sessionToken, in String signingId, in byte[] result,
int userId);
+ void sendCertificate(in IBinder sessionToken, in String host, int port,
+ in Bundle certBundle, int userId);
void sendTvRecordingInfo(in IBinder sessionToken, in TvRecordingInfo recordingInfo, int userId);
void sendTvRecordingInfoList(in IBinder sessionToken,
in List<TvRecordingInfo> recordingInfoList, int userId);
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppSession.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppSession.aidl
index 06808c9..3969315 100644
--- a/media/java/android/media/tv/interactive/ITvInteractiveAppSession.aidl
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppSession.aidl
@@ -49,6 +49,7 @@
void sendTimeShiftMode(int mode);
void sendAvailableSpeeds(in float[] speeds);
void sendSigningResult(in String signingId, in byte[] result);
+ void sendCertificate(in String host, int port, in Bundle certBundle);
void sendTvRecordingInfo(in TvRecordingInfo recordingInfo);
void sendTvRecordingInfoList(in List<TvRecordingInfo> recordingInfoList);
void notifyError(in String errMsg, in Bundle params);
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl
index 416b8f1..cb89181 100644
--- a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl
@@ -61,5 +61,6 @@
void onRequestTvRecordingInfo(in String recordingId);
void onRequestTvRecordingInfoList(in int type);
void onRequestSigning(in String id, in String algorithm, in String alias, in byte[] data);
+ void onRequestCertificate(in String host, int port);
void onAdRequest(in AdRequest request);
}
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java
index 77730aa..ec6c2bf 100644
--- a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java
@@ -104,6 +104,7 @@
private static final int DO_SEND_AVAILABLE_SPEEDS = 47;
private static final int DO_SEND_SELECTED_TRACK_INFO = 48;
private static final int DO_NOTIFY_VIDEO_FREEZE_UPDATED = 49;
+ private static final int DO_SEND_CERTIFICATE = 50;
private final HandlerCaller mCaller;
private Session mSessionImpl;
@@ -369,6 +370,13 @@
mSessionImpl.notifyVideoFreezeUpdated((Boolean) msg.obj);
break;
}
+ case DO_SEND_CERTIFICATE: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ mSessionImpl.sendCertificate((String) args.arg1, (Integer) args.arg2,
+ (Bundle) args.arg3);
+ args.recycle();
+ break;
+ }
default: {
Log.w(TAG, "Unhandled message code: " + msg.what);
break;
@@ -483,6 +491,12 @@
}
@Override
+ public void sendCertificate(@NonNull String host, int port, @NonNull Bundle certBundle) {
+ mCaller.executeOrSendMessage(
+ mCaller.obtainMessageOOO(DO_SEND_CERTIFICATE, host, port, certBundle));
+ }
+
+ @Override
public void notifyError(@NonNull String errMsg, @NonNull Bundle params) {
mCaller.executeOrSendMessage(
mCaller.obtainMessageOO(DO_NOTIFY_ERROR, errMsg, params));
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppManager.java b/media/java/android/media/tv/interactive/TvInteractiveAppManager.java
index 8a340f6..011744f 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppManager.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppManager.java
@@ -34,6 +34,7 @@
import android.media.tv.TvRecordingInfo;
import android.media.tv.TvTrackInfo;
import android.net.Uri;
+import android.net.http.SslCertificate;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -656,6 +657,18 @@
}
@Override
+ public void onRequestCertificate(String host, int port, int seq) {
+ synchronized (mSessionCallbackRecordMap) {
+ SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+ if (record == null) {
+ Log.e(TAG, "Callback not found for seq " + seq);
+ return;
+ }
+ record.postRequestCertificate(host, port);
+ }
+ }
+
+ @Override
public void onSessionStateChanged(int state, int err, int seq) {
synchronized (mSessionCallbackRecordMap) {
SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
@@ -1328,6 +1341,19 @@
}
}
+ void sendCertificate(@NonNull String host, int port, @NonNull SslCertificate cert) {
+ if (mToken == null) {
+ Log.w(TAG, "The session has been already released");
+ return;
+ }
+ try {
+ mService.sendCertificate(mToken, host, port, SslCertificate.saveState(cert),
+ mUserId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
void notifyError(@NonNull String errMsg, @NonNull Bundle params) {
if (mToken == null) {
Log.w(TAG, "The session has been already released");
@@ -2232,6 +2258,15 @@
});
}
+ void postRequestCertificate(String host, int port) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mSessionCallback.onRequestCertificate(mSession, host, port);
+ }
+ });
+ }
+
void postRequestTvRecordingInfo(String recordingId) {
mHandler.post(new Runnable() {
@Override
@@ -2574,6 +2609,17 @@
}
/**
+ * This is called when the service requests a SSL certificate for client validation.
+ *
+ * @param session A {@link TvInteractiveAppService.Session} associated with this callback.
+ * @param host the host name of the SSL authentication server.
+ * @param port the port of the SSL authentication server. E.g., 443
+ * @hide
+ */
+ public void onRequestCertificate(Session session, String host, int port) {
+ }
+
+ /**
* This is called when {@link TvInteractiveAppService.Session#notifySessionStateChanged} is
* called.
*
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppService.java b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
index 5247a0e..054b272 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppService.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
@@ -46,6 +46,7 @@
import android.media.tv.TvView;
import android.media.tv.interactive.TvInteractiveAppView.TvInteractiveAppCallback;
import android.net.Uri;
+import android.net.http.SslCertificate;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
@@ -734,6 +735,17 @@
}
/**
+ * Receives the requested Certificate
+ *
+ * @param host the host name of the SSL authentication server.
+ * @param port the port of the SSL authentication server. E.g., 443
+ * @param cert the SSL certificate received.
+ * @hide
+ */
+ public void onCertificate(@NonNull String host, int port, @NonNull SslCertificate cert) {
+ }
+
+ /**
* Called when the application sends information of an error.
*
* @param errMsg the message of the error.
@@ -1633,6 +1645,32 @@
}
/**
+ * Requests a SSL certificate for client validation.
+ *
+ * @param host the host name of the SSL authentication server.
+ * @param port the port of the SSL authentication server. E.g., 443
+ * @hide
+ */
+ public void requestCertificate(@NonNull String host, int port) {
+ executeOrPostRunnableOnMainThread(new Runnable() {
+ @MainThread
+ @Override
+ public void run() {
+ try {
+ if (DEBUG) {
+ Log.d(TAG, "requestCertificate");
+ }
+ if (mSessionCallback != null) {
+ mSessionCallback.onRequestCertificate(host, port);
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "error in requestCertificate", e);
+ }
+ }
+ });
+ }
+
+ /**
* Sends an advertisement request to be processed by the related TV input.
*
* @param request The advertisement request
@@ -1725,6 +1763,11 @@
onSigningResult(signingId, result);
}
+ void sendCertificate(String host, int port, Bundle certBundle) {
+ SslCertificate cert = SslCertificate.restoreState(certBundle);
+ onCertificate(host, port, cert);
+ }
+
void notifyError(String errMsg, Bundle params) {
onError(errMsg, params);
}
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppView.java b/media/java/android/media/tv/interactive/TvInteractiveAppView.java
index 5bb61c2..3b29574 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppView.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppView.java
@@ -34,6 +34,7 @@
import android.media.tv.interactive.TvInteractiveAppManager.Session.FinishedInputEventCallback;
import android.media.tv.interactive.TvInteractiveAppManager.SessionCallback;
import android.net.Uri;
+import android.net.http.SslCertificate;
import android.os.Bundle;
import android.os.Handler;
import android.util.AttributeSet;
@@ -756,6 +757,22 @@
}
/**
+ * Send the requested SSL certificate to the TV Interactive App
+ * @param host the host name of the SSL authentication server.
+ * @param port the port of the SSL authentication server. E.g., 443
+ * @param cert the SSL certificate requested
+ * @hide
+ */
+ public void sendCertificate(@NonNull String host, int port, @NonNull SslCertificate cert) {
+ if (DEBUG) {
+ Log.d(TAG, "sendCertificate");
+ }
+ if (mSession != null) {
+ mSession.sendCertificate(host, port, cert);
+ }
+ }
+
+ /**
* Notifies the corresponding {@link TvInteractiveAppService} when there is an error.
*
* @param errMsg the message of the error.
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 89a8dd9..17d9f1b 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -335,4 +335,7 @@
<!-- Default for Settings.BATTERY_CHARGING_STATE_ENFORCE_LEVEL.
-1 means system internal default value is used. -->
<integer name="def_battery_charging_state_enforce_level">-1</integer>
+
+ <!-- Value to use as default scale for fonts -->
+ <item name="def_device_font_scale" format="float" type="dimen">1.0</item>
</resources>
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
index e7d7bb0..38ec931 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
@@ -48,6 +48,7 @@
Settings.System.WIFI_STATIC_DNS2,
Settings.System.BLUETOOTH_DISCOVERABILITY,
Settings.System.BLUETOOTH_DISCOVERABILITY_TIMEOUT,
+ Settings.System.DEFAULT_DEVICE_FONT_SCALE,
Settings.System.FONT_SCALE,
Settings.System.DIM_SCREEN,
Settings.System.SCREEN_OFF_TIMEOUT,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java
index a8a659e..677c81a 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java
@@ -45,6 +45,9 @@
}
};
+ public static final Validator FONT_SCALE_VALIDATOR = new InclusiveFloatRangeValidator(0.25f,
+ 5.0f);
+
public static final Validator NON_NEGATIVE_INTEGER_VALIDATOR = new Validator() {
@Override
public boolean validate(@Nullable String value) {
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
index 572303a..98941c7 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
@@ -20,6 +20,7 @@
import static android.provider.settings.validators.SettingsValidators.ANY_STRING_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.BOOLEAN_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.COMPONENT_NAME_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.FONT_SCALE_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.LENIENT_IP_ADDRESS_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.NON_NEGATIVE_FLOAT_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.NON_NEGATIVE_INTEGER_VALIDATOR;
@@ -31,7 +32,6 @@
import android.content.ComponentName;
import android.hardware.display.ColorDisplayManager;
import android.os.BatteryManager;
-import android.provider.Settings.Global;
import android.provider.Settings.System;
import android.util.ArrayMap;
@@ -93,7 +93,8 @@
return value == null || value.length() < MAX_LENGTH;
}
});
- VALIDATORS.put(System.FONT_SCALE, new InclusiveFloatRangeValidator(0.25f, 5.0f));
+ VALIDATORS.put(System.DEFAULT_DEVICE_FONT_SCALE, FONT_SCALE_VALIDATOR);
+ VALIDATORS.put(System.FONT_SCALE, FONT_SCALE_VALIDATOR);
VALIDATORS.put(System.DIM_SCREEN, BOOLEAN_VALIDATOR);
VALIDATORS.put(
System.DISPLAY_COLOR_MODE,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 3a46f4e..febce97 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -3812,7 +3812,7 @@
}
private final class UpgradeController {
- private static final int SETTINGS_VERSION = 224;
+ private static final int SETTINGS_VERSION = 225;
private final int mUserId;
@@ -6004,6 +6004,13 @@
currentVersion = 224;
}
+ // Version 224: Update the default font scale depending on the
+ // R.dimen.def_device_font_scale configuration property.
+ if (currentVersion == 224) {
+ handleDefaultFontScale(getSystemSettingsLocked(userId));
+ currentVersion = 225;
+ }
+
// vXXX: Add new settings above this point.
if (currentVersion != newVersion) {
@@ -6021,6 +6028,32 @@
return currentVersion;
}
+ @SuppressWarnings("GuardedBy")
+ @GuardedBy("mLock")
+ private void handleDefaultFontScale(@NonNull SettingsState systemSettings) {
+ final float defaultFontScale = getContext().getResources()
+ .getFloat(R.dimen.def_device_font_scale);
+ // Persist the value for future use (e.g. Reset Settings option)
+ systemSettings.insertSettingLocked(
+ Settings.System.DEFAULT_DEVICE_FONT_SCALE,
+ String.valueOf(defaultFontScale),
+ /* tag= */ null,
+ /* makeDefault= */ false,
+ SettingsState.SYSTEM_PACKAGE_NAME);
+ // We verify if there is a pre existing value for font_scale.
+ final Setting existingFontScale = systemSettings.getSettingLocked(
+ Settings.System.FONT_SCALE);
+ if (existingFontScale == null || existingFontScale.isNull()) {
+ // Set the default value only if it didn't exist before
+ systemSettings.insertSettingLocked(
+ Settings.System.FONT_SCALE,
+ String.valueOf(defaultFontScale),
+ /* tag= */ null,
+ /* makeDefault= */ false,
+ SettingsState.SYSTEM_PACKAGE_NAME);
+ }
+ }
+
@GuardedBy("mLock")
private void initGlobalSettingsDefaultValLocked(String key, boolean val) {
initGlobalSettingsDefaultValLocked(key, val ? "1" : "0");
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index d384542..cdb4aea 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -561,7 +561,7 @@
<uses-permission android:name="android.permission.TEST_BIOMETRIC" />
<!-- Permission required for CTS test - android.server.biometrics -->
- <uses-permission android:name="android.permission.MANAGE_BIOMETRIC_DIALOG" />
+ <uses-permission android:name="android.permission.SET_BIOMETRIC_DIALOG_LOGO" />
<!-- Permission required for CTS test - android.server.biometrics -->
<uses-permission android:name="android.permission.USE_BACKGROUND_FACE_AUTHENTICATION" />
@@ -902,6 +902,9 @@
<!-- Permission required for BinaryTransparencyService shell API and host test -->
<uses-permission android:name="android.permission.GET_BACKGROUND_INSTALLED_PACKAGES" />
+ <!-- Permissions required for CTS test - CtsPermissionUiTestCases -->
+ <uses-permission android:name="android.permission.MANAGE_ENHANCED_CONFIRMATION_STATES" />
+
<application
android:label="@string/app_label"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
diff --git a/packages/SystemUI/aconfig/accessibility.aconfig b/packages/SystemUI/aconfig/accessibility.aconfig
index 7ba889b..866aa89 100644
--- a/packages/SystemUI/aconfig/accessibility.aconfig
+++ b/packages/SystemUI/aconfig/accessibility.aconfig
@@ -17,6 +17,13 @@
}
flag {
+ name: "floating_menu_drag_to_edit"
+ namespace: "accessibility"
+ description: "adds a second drag button to allow the user edit the shortcut."
+ bug: "297583708"
+}
+
+flag {
name: "floating_menu_ime_displacement_animation"
namespace: "accessibility"
description: "Adds an animation for when the FAB is displaced by an IME becoming visible."
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 7eca04a..a2530d5 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -366,8 +366,8 @@
}
flag {
- name: "enable_notif_linearlayout_optimized"
- namespace: "systemui"
- description: "Enables notification specific LinearLayout optimization"
- bug: "316110233"
+ name: "keyguard_wm_state_refactor"
+ namespace: "systemui"
+ description: "Enables refactored logic for SysUI+WM unlock/occlusion code paths"
+ bug: "278086361"
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt
index 2a6bea7..be6f022 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt
@@ -33,6 +33,7 @@
import com.android.keyguard.LockIconViewController
import com.android.systemui.Flags.keyguardBottomAreaRefactor
import com.android.systemui.biometrics.AuthController
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.flags.Flags
@@ -47,10 +48,12 @@
import com.android.systemui.statusbar.VibratorHelper
import dagger.Lazy
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
class LockSection
@Inject
constructor(
+ @Application private val applicationScope: CoroutineScope,
private val windowManager: WindowManager,
private val authController: AuthController,
private val featureFlags: FeatureFlagsClassic,
@@ -76,6 +79,7 @@
DeviceEntryIconView(context, null).apply {
id = R.id.device_entry_icon_view
DeviceEntryIconViewBinder.bind(
+ applicationScope,
this,
deviceEntryIconViewModel.get(),
deviceEntryForegroundViewModel.get(),
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
index 030d41d..c82688c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
@@ -203,11 +203,17 @@
whenever(deviceProvisionedController.isUserSetup(anyInt())).thenReturn(true)
featureFlags = FakeFeatureFlags()
- featureFlags.set(Flags.KEYGUARD_WM_STATE_REFACTOR, false)
featureFlags.set(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT, false)
featureFlags.set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false)
- mSetFlagsRule.enableFlags(AConfigFlags.FLAG_REVAMPED_BOUNCER_MESSAGES)
+ mSetFlagsRule.enableFlags(
+ AConfigFlags.FLAG_REVAMPED_BOUNCER_MESSAGES,
+ )
+ mSetFlagsRule.disableFlags(
+ FLAG_SIDEFPS_CONTROLLER_REFACTOR,
+ AConfigFlags.FLAG_KEYGUARD_WM_STATE_REFACTOR
+ )
+
keyguardPasswordViewController =
KeyguardPasswordViewController(
keyguardPasswordView,
@@ -238,7 +244,6 @@
sceneInteractor.setTransitionState(sceneTransitionStateFlow)
deviceEntryInteractor = kosmos.deviceEntryInteractor
- mSetFlagsRule.disableFlags(FLAG_SIDEFPS_CONTROLLER_REFACTOR)
underTest =
KeyguardSecurityContainerController(
view,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
index bb3429e..c979ca6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
@@ -30,7 +30,6 @@
import com.android.systemui.communal.shared.CommunalWidgetHost
import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
import com.android.systemui.communal.widgets.CommunalAppWidgetHost
-import com.android.systemui.communal.widgets.WidgetConfigurator
import com.android.systemui.communal.widgets.widgetConfiguratorFail
import com.android.systemui.communal.widgets.widgetConfiguratorSuccess
import com.android.systemui.coroutines.collectLastValue
@@ -45,8 +44,7 @@
import com.google.common.truth.Truth.assertThat
import java.util.Optional
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -62,24 +60,17 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
- @Mock private lateinit var appWidgetManagerOptional: Optional<AppWidgetManager>
-
@Mock private lateinit var appWidgetManager: AppWidgetManager
-
@Mock private lateinit var appWidgetHost: CommunalAppWidgetHost
-
@Mock private lateinit var stopwatchProviderInfo: AppWidgetProviderInfo
-
@Mock private lateinit var providerInfoA: AppWidgetProviderInfo
-
@Mock private lateinit var communalWidgetHost: CommunalWidgetHost
-
@Mock private lateinit var communalWidgetDao: CommunalWidgetDao
private lateinit var logBuffer: LogBuffer
+ private lateinit var fakeWidgets: MutableStateFlow<Map<CommunalItemRank, CommunalWidgetItem>>
private val kosmos = testKosmos()
- private val testDispatcher = kosmos.testDispatcher
private val testScope = kosmos.testScope
private val fakeAllowlist =
@@ -94,7 +85,7 @@
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
-
+ fakeWidgets = MutableStateFlow(emptyMap())
logBuffer = logcatLogBuffer(name = "CommunalWidgetRepoImplTest")
setAppWidgetIds(emptyList())
@@ -102,13 +93,11 @@
overrideResource(R.array.config_communalWidgetAllowlist, fakeAllowlist.toTypedArray())
whenever(stopwatchProviderInfo.loadLabel(any())).thenReturn("Stopwatch")
- whenever(communalWidgetDao.getWidgets()).thenReturn(flowOf(emptyMap()))
- whenever(appWidgetManagerOptional.isPresent).thenReturn(true)
- whenever(appWidgetManagerOptional.get()).thenReturn(appWidgetManager)
+ whenever(communalWidgetDao.getWidgets()).thenReturn(fakeWidgets)
underTest =
CommunalWidgetRepositoryImpl(
- appWidgetManagerOptional,
+ Optional.of(appWidgetManager),
appWidgetHost,
testScope.backgroundScope,
kosmos.testDispatcher,
@@ -119,30 +108,16 @@
}
@Test
- fun neverQueryDbForWidgets_whenHostIsInactive() =
+ fun communalWidgets_queryWidgetsFromDb() =
testScope.runTest {
- underTest.updateAppWidgetHostActive(false)
- underTest.communalWidgets.launchIn(testScope.backgroundScope)
- runCurrent()
-
- verify(communalWidgetDao, never()).getWidgets()
- }
-
- @Test
- fun communalWidgets_whenHostIsActive_queryWidgetsFromDb() =
- testScope.runTest {
- underTest.updateAppWidgetHostActive(true)
-
val communalItemRankEntry = CommunalItemRank(uid = 1L, rank = 1)
val communalWidgetItemEntry = CommunalWidgetItem(uid = 1L, 1, "pk_name/cls_name", 1L)
- whenever(communalWidgetDao.getWidgets())
- .thenReturn(flowOf(mapOf(communalItemRankEntry to communalWidgetItemEntry)))
+ fakeWidgets.value = mapOf(communalItemRankEntry to communalWidgetItemEntry)
whenever(appWidgetManager.getAppWidgetInfo(anyInt())).thenReturn(providerInfoA)
installedProviders(listOf(stopwatchProviderInfo))
val communalWidgets by collectLastValue(underTest.communalWidgets)
- runCurrent()
verify(communalWidgetDao).getWidgets()
assertThat(communalWidgets)
.containsExactly(
@@ -157,8 +132,6 @@
@Test
fun addWidget_allocateId_bindWidget_andAddToDb() =
testScope.runTest {
- underTest.updateAppWidgetHostActive(true)
-
val provider = ComponentName("pkg_name", "cls_name")
val id = 1
val priority = 1
@@ -176,8 +149,6 @@
@Test
fun addWidget_configurationFails_doNotAddWidgetToDb() =
testScope.runTest {
- underTest.updateAppWidgetHostActive(true)
-
val provider = ComponentName("pkg_name", "cls_name")
val id = 1
val priority = 1
@@ -195,23 +166,13 @@
@Test
fun addWidget_configurationThrowsError_doNotAddWidgetToDb() =
testScope.runTest {
- underTest.updateAppWidgetHostActive(true)
-
val provider = ComponentName("pkg_name", "cls_name")
val id = 1
val priority = 1
whenever(communalWidgetHost.getAppWidgetInfo(id))
.thenReturn(PROVIDER_INFO_REQUIRES_CONFIGURATION)
whenever(communalWidgetHost.allocateIdAndBindWidget(provider)).thenReturn(id)
- underTest.addWidget(
- provider,
- priority,
- object : WidgetConfigurator {
- override suspend fun configureWidget(appWidgetId: Int): Boolean {
- throw IllegalStateException("some error")
- }
- }
- )
+ underTest.addWidget(provider, priority) { throw IllegalStateException("some error") }
runCurrent()
verify(communalWidgetHost).allocateIdAndBindWidget(provider)
@@ -222,8 +183,6 @@
@Test
fun addWidget_configurationNotRequired_doesNotConfigure_addWidgetToDb() =
testScope.runTest {
- underTest.updateAppWidgetHostActive(true)
-
val provider = ComponentName("pkg_name", "cls_name")
val id = 1
val priority = 1
@@ -241,8 +200,6 @@
@Test
fun deleteWidget_removeWidgetId_andDeleteFromDb() =
testScope.runTest {
- underTest.updateAppWidgetHostActive(true)
-
val id = 1
underTest.deleteWidget(id)
runCurrent()
@@ -254,8 +211,6 @@
@Test
fun reorderWidgets_queryDb() =
testScope.runTest {
- underTest.updateAppWidgetHostActive(true)
-
val widgetIdToPriorityMap = mapOf(104 to 1, 103 to 2, 101 to 3)
underTest.updateWidgetOrder(widgetIdToPriorityMap)
runCurrent()
@@ -263,28 +218,6 @@
verify(communalWidgetDao).updateWidgetOrder(widgetIdToPriorityMap)
}
- @Test
- fun appWidgetHost_startListening() =
- testScope.runTest {
- verify(appWidgetHost, never()).startListening()
-
- underTest.updateAppWidgetHostActive(true)
-
- verify(appWidgetHost).startListening()
- }
-
- @Test
- fun appWidgetHost_stopListening() =
- testScope.runTest {
- underTest.updateAppWidgetHostActive(true)
-
- verify(appWidgetHost).startListening()
-
- underTest.updateAppWidgetHostActive(false)
-
- verify(appWidgetHost).stopListening()
- }
-
private fun installedProviders(providers: List<AppWidgetProviderInfo>) {
whenever(appWidgetManager.installedProviders).thenReturn(providers)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorCommunalDisabledTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorCommunalDisabledTest.kt
index e821673..6a3fc2a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorCommunalDisabledTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorCommunalDisabledTest.kt
@@ -80,15 +80,4 @@
assertThat(isCommunalAvailable).isFalse()
}
-
- @Test
- fun updateAppWidgetHostActive_whenStorageUnlock_false() =
- testScope.runTest {
- assertThat(widgetRepository.isHostActive()).isFalse()
-
- keyguardRepository.setIsEncryptedOrLockdown(false)
- runCurrent()
-
- assertThat(widgetRepository.isHostActive()).isFalse()
- }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
index 1b7117f..a083e7c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
@@ -172,20 +172,6 @@
}
@Test
- fun updateAppWidgetHostActive_uponStorageUnlockAsMainUser_true() =
- testScope.runTest {
- collectLastValue(underTest.isCommunalAvailable)
- assertThat(widgetRepository.isHostActive()).isFalse()
-
- keyguardRepository.setIsEncryptedOrLockdown(false)
- userRepository.setSelectedUserInfo(mainUser)
- keyguardRepository.setKeyguardShowing(true)
- runCurrent()
-
- assertThat(widgetRepository.isHostActive()).isTrue()
- }
-
- @Test
fun widget_tutorialCompletedAndWidgetsAvailable_showWidgetContent() =
testScope.runTest {
// Keyguard showing, and tutorial completed.
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt
new file mode 100644
index 0000000..112b0c7
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.widgets
+
+import android.content.pm.UserInfo
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.android.systemui.user.data.repository.fakeUserRepository
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+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.Mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class CommunalAppWidgetHostStartableTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+
+ @Mock private lateinit var appWidgetHost: CommunalAppWidgetHost
+
+ private lateinit var underTest: CommunalAppWidgetHostStartable
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ kosmos.fakeUserRepository.setUserInfos(listOf(MAIN_USER_INFO))
+
+ underTest =
+ CommunalAppWidgetHostStartable(
+ appWidgetHost,
+ kosmos.communalInteractor,
+ kosmos.applicationCoroutineScope,
+ kosmos.testDispatcher,
+ )
+ }
+
+ @Test
+ fun editModeShowingStartsAppWidgetHost() =
+ with(kosmos) {
+ testScope.runTest {
+ setCommunalAvailable(false)
+ communalInteractor.setEditModeOpen(true)
+ verify(appWidgetHost, never()).startListening()
+
+ underTest.start()
+ runCurrent()
+
+ verify(appWidgetHost).startListening()
+ verify(appWidgetHost, never()).stopListening()
+
+ communalInteractor.setEditModeOpen(false)
+ runCurrent()
+
+ verify(appWidgetHost).stopListening()
+ }
+ }
+
+ @Test
+ fun communalShowingStartsAppWidgetHost() =
+ with(kosmos) {
+ testScope.runTest {
+ setCommunalAvailable(true)
+ communalInteractor.setEditModeOpen(false)
+ verify(appWidgetHost, never()).startListening()
+
+ underTest.start()
+ runCurrent()
+
+ verify(appWidgetHost).startListening()
+ verify(appWidgetHost, never()).stopListening()
+
+ setCommunalAvailable(false)
+ runCurrent()
+
+ verify(appWidgetHost).stopListening()
+ }
+ }
+
+ @Test
+ fun communalAndEditModeNotShowingNeverStartListening() =
+ with(kosmos) {
+ testScope.runTest {
+ setCommunalAvailable(false)
+ communalInteractor.setEditModeOpen(false)
+
+ underTest.start()
+ runCurrent()
+
+ verify(appWidgetHost, never()).startListening()
+ verify(appWidgetHost, never()).stopListening()
+ }
+ }
+
+ private suspend fun setCommunalAvailable(available: Boolean) =
+ with(kosmos) {
+ fakeKeyguardRepository.setIsEncryptedOrLockdown(!available)
+ fakeUserRepository.setSelectedUserInfo(MAIN_USER_INFO)
+ fakeKeyguardRepository.setKeyguardShowing(true)
+ }
+
+ private companion object {
+ val MAIN_USER_INFO = UserInfo(0, "primary", UserInfo.FLAG_MAIN)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
index 6a14220..6808f5d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
@@ -38,6 +38,7 @@
import com.android.internal.logging.InstanceId.fakeInstanceId
import com.android.internal.logging.UiEventLogger
import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.Flags as AConfigFlags
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository
import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository
@@ -62,7 +63,6 @@
import com.android.systemui.display.data.repository.display
import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags.KEYGUARD_WM_STATE_REFACTOR
import com.android.systemui.keyguard.data.repository.BiometricType
import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.FakeCommandQueue
@@ -194,7 +194,7 @@
biometricSettingsRepository = FakeBiometricSettingsRepository()
deviceEntryFingerprintAuthRepository = FakeDeviceEntryFingerprintAuthRepository()
trustRepository = FakeTrustRepository()
- featureFlags = FakeFeatureFlags().apply { set(KEYGUARD_WM_STATE_REFACTOR, false) }
+ featureFlags = FakeFeatureFlags()
powerRepository = FakePowerRepository()
powerInteractor =
@@ -252,6 +252,10 @@
.thenReturn(listOf(createFaceSensorProperties(supportsFaceDetection = true)))
whenever(bypassController.bypassEnabled).thenReturn(true)
underTest = createDeviceEntryFaceAuthRepositoryImpl(faceManager, bypassController)
+
+ mSetFlagsRule.disableFlags(
+ AConfigFlags.FLAG_KEYGUARD_WM_STATE_REFACTOR,
+ )
}
private fun createDeviceEntryFaceAuthRepositoryImpl(
@@ -301,7 +305,6 @@
faceAuthBuffer,
keyguardTransitionInteractor,
displayStateInteractor,
- featureFlags,
dumpManager,
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/AuthRippleInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/AuthRippleInteractorTest.kt
new file mode 100644
index 0000000..88ad3f3
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/AuthRippleInteractorTest.kt
@@ -0,0 +1,87 @@
+/*
+ * 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.deviceentry.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
+import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
+import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class AuthRippleInteractorTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val deviceEntrySourceInteractor = kosmos.deviceEntrySourceInteractor
+ private val fingerprintPropertyRepository = kosmos.fingerprintPropertyRepository
+ private val keyguardRepository = kosmos.fakeKeyguardRepository
+ private val underTest = kosmos.authRippleInteractor
+
+ @Test
+ fun enteringDeviceFromDeviceEntryIcon_udfpsNotSupported_doesNotShowAuthRipple() =
+ testScope.runTest {
+ val showUnlockRipple by collectLastValue(underTest.showUnlockRipple)
+ fingerprintPropertyRepository.supportsRearFps()
+ keyguardRepository.setKeyguardDismissible(true)
+ runCurrent()
+ deviceEntrySourceInteractor.attemptEnterDeviceFromDeviceEntryIcon()
+ assertThat(showUnlockRipple).isNull()
+ }
+
+ @Test
+ fun enteringDeviceFromDeviceEntryIcon_udfpsSupported_showsAuthRipple() =
+ testScope.runTest {
+ val showUnlockRipple by collectLastValue(underTest.showUnlockRipple)
+ fingerprintPropertyRepository.supportsUdfps()
+ keyguardRepository.setKeyguardDismissible(true)
+ runCurrent()
+ deviceEntrySourceInteractor.attemptEnterDeviceFromDeviceEntryIcon()
+ assertThat(showUnlockRipple).isEqualTo(BiometricUnlockSource.FINGERPRINT_SENSOR)
+ }
+
+ @Test
+ fun faceUnlocked_showsAuthRipple() =
+ testScope.runTest {
+ val showUnlockRipple by collectLastValue(underTest.showUnlockRipple)
+ keyguardRepository.setBiometricUnlockSource(BiometricUnlockSource.FACE_SENSOR)
+ keyguardRepository.setBiometricUnlockState(BiometricUnlockModel.WAKE_AND_UNLOCK)
+ assertThat(showUnlockRipple).isEqualTo(BiometricUnlockSource.FACE_SENSOR)
+ }
+
+ @Test
+ fun fingerprintUnlocked_showsAuthRipple() =
+ testScope.runTest {
+ val showUnlockRippleFromBiometricUnlock by collectLastValue(underTest.showUnlockRipple)
+ keyguardRepository.setBiometricUnlockSource(BiometricUnlockSource.FINGERPRINT_SENSOR)
+ keyguardRepository.setBiometricUnlockState(BiometricUnlockModel.WAKE_AND_UNLOCK)
+ assertThat(showUnlockRippleFromBiometricUnlock)
+ .isEqualTo(BiometricUnlockSource.FINGERPRINT_SENSOR)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntrySourceInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntrySourceInteractorTest.kt
new file mode 100644
index 0000000..d216fa0
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntrySourceInteractorTest.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.deviceentry.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
+import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
+import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DeviceEntrySourceInteractorTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val keyguardRepository = kosmos.fakeKeyguardRepository
+ private val underTest = kosmos.deviceEntrySourceInteractor
+
+ @Test
+ fun deviceEntryFromFaceUnlock() =
+ testScope.runTest {
+ val deviceEntryFromBiometricAuthentication by
+ collectLastValue(underTest.deviceEntryFromBiometricSource)
+ keyguardRepository.setBiometricUnlockSource(BiometricUnlockSource.FACE_SENSOR)
+ keyguardRepository.setBiometricUnlockState(BiometricUnlockModel.WAKE_AND_UNLOCK)
+ runCurrent()
+ assertThat(deviceEntryFromBiometricAuthentication)
+ .isEqualTo(BiometricUnlockSource.FACE_SENSOR)
+ }
+
+ @Test
+ fun deviceEntryFromFingerprintUnlock() = runTest {
+ val deviceEntryFromBiometricAuthentication by
+ collectLastValue(underTest.deviceEntryFromBiometricSource)
+ keyguardRepository.setBiometricUnlockSource(BiometricUnlockSource.FINGERPRINT_SENSOR)
+ keyguardRepository.setBiometricUnlockState(BiometricUnlockModel.WAKE_AND_UNLOCK)
+ runCurrent()
+ assertThat(deviceEntryFromBiometricAuthentication)
+ .isEqualTo(BiometricUnlockSource.FINGERPRINT_SENSOR)
+ }
+
+ @Test
+ fun noDeviceEntry() = runTest {
+ val deviceEntryFromBiometricAuthentication by
+ collectLastValue(underTest.deviceEntryFromBiometricSource)
+ keyguardRepository.setBiometricUnlockSource(BiometricUnlockSource.FINGERPRINT_SENSOR)
+ // doesn't dismiss keyguard:
+ keyguardRepository.setBiometricUnlockState(BiometricUnlockModel.ONLY_WAKE)
+ runCurrent()
+ assertThat(deviceEntryFromBiometricAuthentication).isNull()
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
index dc8b97a..78ae8b1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
@@ -238,10 +238,10 @@
}
@Test
- fun isKeyguardUnlocked() =
+ fun isKeyguardDismissible() =
testScope.runTest {
whenever(keyguardStateController.isUnlocked).thenReturn(false)
- val isKeyguardUnlocked by collectLastValue(underTest.isKeyguardUnlocked)
+ val isKeyguardUnlocked by collectLastValue(underTest.isKeyguardDismissible)
runCurrent()
assertThat(isKeyguardUnlocked).isFalse()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt
index eb845b2..d9f24b3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt
@@ -35,7 +35,6 @@
import com.android.systemui.common.data.repository.packageChangeRepository
import com.android.systemui.common.data.shared.model.PackageChangeModel
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
@@ -44,6 +43,8 @@
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -52,6 +53,7 @@
import org.mockito.Mock
import org.mockito.MockitoAnnotations
+@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
@@ -82,7 +84,7 @@
underTest =
InstalledTilesComponentRepositoryImpl(
context,
- kosmos.testDispatcher,
+ testScope.backgroundScope,
kosmos.packageChangeRepository
)
}
@@ -103,6 +105,7 @@
.thenReturn(listOf(resolveInfo))
val componentNames by collectLastValue(underTest.getInstalledTilesComponents(userId))
+ runCurrent()
assertThat(componentNames).containsExactly(TEST_COMPONENT)
}
@@ -115,6 +118,8 @@
ResolveInfo(TEST_COMPONENT, hasPermission = true, defaultEnabled = true)
val componentNames by collectLastValue(underTest.getInstalledTilesComponents(userId))
+ runCurrent()
+
assertThat(componentNames).isEmpty()
whenever(
@@ -126,6 +131,7 @@
)
.thenReturn(listOf(resolveInfo))
kosmos.fakePackageChangeRepository.notifyChange(PackageChangeModel.Empty)
+ runCurrent()
assertThat(componentNames).containsExactly(TEST_COMPONENT)
}
@@ -146,6 +152,8 @@
.thenReturn(listOf(resolveInfo))
val componentNames by collectLastValue(underTest.getInstalledTilesComponents(userId))
+ runCurrent()
+
assertThat(componentNames).isEmpty()
}
@@ -165,6 +173,8 @@
.thenReturn(listOf(resolveInfo))
val componentNames by collectLastValue(underTest.getInstalledTilesComponents(userId))
+ runCurrent()
+
assertThat(componentNames).isEmpty()
}
@@ -210,10 +220,31 @@
.thenReturn(listOf(resolveInfo))
val componentNames by collectLastValue(underTest.getInstalledTilesComponents(userId))
+ runCurrent()
assertThat(componentNames).containsExactly(TEST_COMPONENT)
}
+ @Test
+ fun loadComponentsForSameUserTwice_returnsSameFlow() =
+ testScope.runTest {
+ val flowForUser1 = underTest.getInstalledTilesComponents(1)
+ val flowForUser1TheSecondTime = underTest.getInstalledTilesComponents(1)
+ runCurrent()
+
+ assertThat(flowForUser1TheSecondTime).isEqualTo(flowForUser1)
+ }
+
+ @Test
+ fun loadComponentsForDifferentUsers_returnsDifferentFlow() =
+ testScope.runTest {
+ val flowForUser1 = underTest.getInstalledTilesComponents(1)
+ val flowForUser2 = underTest.getInstalledTilesComponents(2)
+ runCurrent()
+
+ assertThat(flowForUser2).isNotEqualTo(flowForUser1)
+ }
+
companion object {
private val INTENT = Intent(TileService.ACTION_QS_TILE)
private val FLAGS =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/util/kotlin/BooleanFlowOperatorsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/kotlin/BooleanFlowOperatorsTest.kt
new file mode 100644
index 0000000..b4a0a37
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/kotlin/BooleanFlowOperatorsTest.kt
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.kotlin
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.android.systemui.util.kotlin.BooleanFlowOperators.and
+import com.android.systemui.util.kotlin.BooleanFlowOperators.not
+import com.android.systemui.util.kotlin.BooleanFlowOperators.or
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class BooleanFlowOperatorsTest : SysuiTestCase() {
+
+ val kosmos = testKosmos()
+ val testScope = kosmos.testScope
+
+ @Test
+ fun and_allTrue_returnsTrue() =
+ testScope.runTest {
+ val result by collectLastValue(and(TRUE, TRUE))
+ assertThat(result).isTrue()
+ }
+
+ @Test
+ fun and_anyFalse_returnsFalse() =
+ testScope.runTest {
+ val result by collectLastValue(and(TRUE, FALSE, TRUE))
+ assertThat(result).isFalse()
+ }
+
+ @Test
+ fun and_allFalse_returnsFalse() =
+ testScope.runTest {
+ val result by collectLastValue(and(FALSE, FALSE, FALSE))
+ assertThat(result).isFalse()
+ }
+
+ @Test
+ fun or_allTrue_returnsTrue() =
+ testScope.runTest {
+ val result by collectLastValue(or(TRUE, TRUE))
+ assertThat(result).isTrue()
+ }
+
+ @Test
+ fun or_anyTrue_returnsTrue() =
+ testScope.runTest {
+ val result by collectLastValue(or(FALSE, TRUE, FALSE))
+ assertThat(result).isTrue()
+ }
+
+ @Test
+ fun or_allFalse_returnsFalse() =
+ testScope.runTest {
+ val result by collectLastValue(or(FALSE, FALSE, FALSE))
+ assertThat(result).isFalse()
+ }
+
+ @Test
+ fun not_true_returnsFalse() =
+ testScope.runTest {
+ val result by collectLastValue(not(TRUE))
+ assertThat(result).isFalse()
+ }
+
+ @Test
+ fun not_false_returnsFalse() =
+ testScope.runTest {
+ val result by collectLastValue(not(FALSE))
+ assertThat(result).isTrue()
+ }
+
+ private companion object {
+ val TRUE: Flow<Boolean>
+ get() = flowOf(true)
+ val FALSE: Flow<Boolean>
+ get() = flowOf(false)
+ }
+}
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 8ec5ccd..2ab0813 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -184,6 +184,7 @@
<item type="id" name="action_move_to_edge_and_hide"/>
<item type="id" name="action_move_out_edge_and_show"/>
<item type="id" name="action_remove_menu"/>
+ <item type="id" name="action_edit"/>
<!-- rounded corner view id -->
<item type="id" name="rounded_corner_top_left"/>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 2b43360..47ac96c 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2560,6 +2560,8 @@
<string name="accessibility_floating_button_action_remove_menu">Remove</string>
<!-- Action in accessibility menu to toggle on/off the accessibility feature. [CHAR LIMIT=30]-->
<string name="accessibility_floating_button_action_double_tap_to_toggle">toggle</string>
+ <!-- Action in accessibility menu to open the shortcut edit menu" [CHAR LIMIT=30]-->
+ <string name="accessibility_floating_button_action_edit">Edit</string>
<!-- Device Controls strings -->
<!-- Device Controls, Quick Settings tile title [CHAR LIMIT=30] -->
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index ecce223..25d7713 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -19,9 +19,9 @@
import static android.app.StatusBarManager.SESSION_KEYGUARD;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISSIBLE_KEYGUARD;
import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_BIOMETRIC;
import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_EXTENDED_ACCESS;
-import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISSIBLE_KEYGUARD;
import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_NONE_SECURITY;
import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_PASSWORD;
import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_SIM;
@@ -84,6 +84,7 @@
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
+import com.android.systemui.keyguard.KeyguardWmStateRefactor;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.ActivityStarter;
@@ -100,8 +101,6 @@
import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.settings.GlobalSettings;
-import dagger.Lazy;
-
import java.io.File;
import java.util.Arrays;
import java.util.Optional;
@@ -109,6 +108,7 @@
import javax.inject.Inject;
import javax.inject.Provider;
+import dagger.Lazy;
import kotlinx.coroutines.Job;
/** Controller for {@link KeyguardSecurityContainer} */
@@ -330,7 +330,7 @@
}
}
- if (mFeatureFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
+ if (KeyguardWmStateRefactor.isEnabled()) {
mKeyguardTransitionInteractor.startDismissKeyguardTransition();
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index d5dc85c..8e98150 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -63,6 +63,7 @@
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor;
+import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
@@ -87,6 +88,8 @@
import javax.inject.Inject;
+import kotlinx.coroutines.ExperimentalCoroutinesApi;
+
/**
* Controls when to show the LockIcon affordance (lock/unlocked icon or circle) on lock screen.
*
@@ -717,6 +720,7 @@
return mDownDetected;
}
+ @ExperimentalCoroutinesApi
@VisibleForTesting
protected void onLongPress() {
cancelTouches();
@@ -727,7 +731,8 @@
// pre-emptively set to true to hide view
mIsBouncerShowing = true;
- if (mUdfpsSupported && mShowUnlockIcon && mAuthRippleController != null) {
+ if (!DeviceEntryUdfpsRefactor.isEnabled()
+ && mUdfpsSupported && mShowUnlockIcon && mAuthRippleController != null) {
mAuthRippleController.showUnlockRipple(FINGERPRINT);
}
updateVisibility();
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationController.java
index 568b24d..7fd72ec 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationController.java
@@ -16,127 +16,138 @@
package com.android.systemui.accessibility.floatingmenu;
+import static android.R.id.empty;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
+import android.util.ArrayMap;
+import android.util.Pair;
import android.view.MotionEvent;
import androidx.annotation.NonNull;
import androidx.dynamicanimation.animation.DynamicAnimation;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.Flags;
+import com.android.wm.shell.common.bubbles.DismissCircleView;
import com.android.wm.shell.common.bubbles.DismissView;
import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
+import java.util.Map;
+import java.util.Objects;
+
/**
* Controls the interaction between {@link MagnetizedObject} and
* {@link MagnetizedObject.MagneticTarget}.
*/
class DragToInteractAnimationController {
- private static final boolean ENABLE_FLING_TO_DISMISS_MENU = false;
private static final float COMPLETELY_OPAQUE = 1.0f;
private static final float COMPLETELY_TRANSPARENT = 0.0f;
private static final float CIRCLE_VIEW_DEFAULT_SCALE = 1.0f;
private static final float ANIMATING_MAX_ALPHA = 0.7f;
+ private final DragToInteractView mInteractView;
private final DismissView mDismissView;
private final MenuView mMenuView;
- private final ValueAnimator mDismissAnimator;
- private final MagnetizedObject<?> mMagnetizedObject;
- private float mMinDismissSize;
+
+ /**
+ * MagnetizedObject cannot differentiate between its MagnetizedTargets,
+ * so we need an object & an animator for every interactable.
+ */
+ private final ArrayMap<Integer, Pair<MagnetizedObject<MenuView>, ValueAnimator>> mInteractMap;
+
+ private float mMinInteractSize;
private float mSizePercent;
+ DragToInteractAnimationController(DragToInteractView interactView, MenuView menuView) {
+ mDismissView = null;
+ mInteractView = interactView;
+ mInteractView.setPivotX(interactView.getWidth() / 2.0f);
+ mInteractView.setPivotY(interactView.getHeight() / 2.0f);
+ mMenuView = menuView;
+
+ updateResources();
+
+ mInteractMap = new ArrayMap<>();
+ interactView.getInteractMap().forEach((viewId, pair) -> {
+ DismissCircleView circleView = pair.getFirst();
+ createMagnetizedObjectAndAnimator(circleView);
+ });
+ }
+
DragToInteractAnimationController(DismissView dismissView, MenuView menuView) {
mDismissView = dismissView;
+ mInteractView = null;
mDismissView.setPivotX(dismissView.getWidth() / 2.0f);
mDismissView.setPivotY(dismissView.getHeight() / 2.0f);
mMenuView = menuView;
updateResources();
- mDismissAnimator = ValueAnimator.ofFloat(COMPLETELY_OPAQUE, COMPLETELY_TRANSPARENT);
- mDismissAnimator.addUpdateListener(dismissAnimation -> {
- final float animatedValue = (float) dismissAnimation.getAnimatedValue();
- final float scaleValue = Math.max(animatedValue, mSizePercent);
- dismissView.getCircle().setScaleX(scaleValue);
- dismissView.getCircle().setScaleY(scaleValue);
-
- menuView.setAlpha(Math.max(animatedValue, ANIMATING_MAX_ALPHA));
- });
-
- mDismissAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(@NonNull Animator animation, boolean isReverse) {
- super.onAnimationEnd(animation, isReverse);
-
- if (isReverse) {
- mDismissView.getCircle().setScaleX(CIRCLE_VIEW_DEFAULT_SCALE);
- mDismissView.getCircle().setScaleY(CIRCLE_VIEW_DEFAULT_SCALE);
- mMenuView.setAlpha(COMPLETELY_OPAQUE);
- }
- }
- });
-
- mMagnetizedObject =
- new MagnetizedObject<MenuView>(mMenuView.getContext(), mMenuView,
- new MenuAnimationController.MenuPositionProperty(
- DynamicAnimation.TRANSLATION_X),
- new MenuAnimationController.MenuPositionProperty(
- DynamicAnimation.TRANSLATION_Y)) {
- @Override
- public void getLocationOnScreen(MenuView underlyingObject, int[] loc) {
- underlyingObject.getLocationOnScreen(loc);
- }
-
- @Override
- public float getHeight(MenuView underlyingObject) {
- return underlyingObject.getHeight();
- }
-
- @Override
- public float getWidth(MenuView underlyingObject) {
- return underlyingObject.getWidth();
- }
- };
-
- final MagnetizedObject.MagneticTarget magneticTarget = new MagnetizedObject.MagneticTarget(
- dismissView.getCircle(), (int) mMinDismissSize);
- mMagnetizedObject.addTarget(magneticTarget);
- mMagnetizedObject.setFlingToTargetEnabled(ENABLE_FLING_TO_DISMISS_MENU);
+ mInteractMap = new ArrayMap<>();
+ createMagnetizedObjectAndAnimator(dismissView.getCircle());
}
- void showDismissView(boolean show) {
- if (show) {
- mDismissView.show();
- } else {
- mDismissView.hide();
+ void showInteractView(boolean show) {
+ if (Flags.floatingMenuDragToEdit() && mInteractView != null) {
+ if (show) {
+ mInteractView.show();
+ } else {
+ mInteractView.hide();
+ }
+ } else if (mDismissView != null) {
+ if (show) {
+ mDismissView.show();
+ } else {
+ mDismissView.hide();
+ }
}
}
void setMagnetListener(MagnetizedObject.MagnetListener magnetListener) {
- mMagnetizedObject.setMagnetListener(magnetListener);
+ mInteractMap.forEach((viewId, pair) -> {
+ MagnetizedObject<?> magnetizedObject = pair.first;
+ magnetizedObject.setMagnetListener(magnetListener);
+ });
}
@VisibleForTesting
- MagnetizedObject.MagnetListener getMagnetListener() {
- return mMagnetizedObject.getMagnetListener();
+ MagnetizedObject.MagnetListener getMagnetListener(int id) {
+ return Objects.requireNonNull(mInteractMap.get(id)).first.getMagnetListener();
}
void maybeConsumeDownMotionEvent(MotionEvent event) {
- mMagnetizedObject.maybeConsumeMotionEvent(event);
+ mInteractMap.forEach((viewId, pair) -> {
+ MagnetizedObject<?> magnetizedObject = pair.first;
+ magnetizedObject.maybeConsumeMotionEvent(event);
+ });
+ }
+
+ private int maybeConsumeMotionEvent(MotionEvent event) {
+ for (Map.Entry<Integer, Pair<MagnetizedObject<MenuView>, ValueAnimator>> set:
+ mInteractMap.entrySet()) {
+ MagnetizedObject<MenuView> magnetizedObject = set.getValue().first;
+ if (magnetizedObject.maybeConsumeMotionEvent(event)) {
+ return set.getKey();
+ }
+ }
+ return empty;
}
/**
- * This used to pass {@link MotionEvent#ACTION_DOWN} to the magnetized object to check if it was
- * within the magnetic field. It should be used in the {@link MenuListViewTouchHandler}.
+ * This used to pass {@link MotionEvent#ACTION_DOWN} to the magnetized objects
+ * to check if it was within a magnetic field.
+ * It should be used in the {@link MenuListViewTouchHandler}.
*
* @param event that move the magnetized object which is also the menu list view.
- * @return true if the location of the motion events moves within the magnetic field of a
- * target, but false if didn't set
+ * @return id of a target if the location of the motion events moves
+ * within the field of the target, otherwise it returns{@link android.R.id#empty}.
+ * <p>
* {@link DragToInteractAnimationController#setMagnetListener(MagnetizedObject.MagnetListener)}.
*/
- boolean maybeConsumeMoveMotionEvent(MotionEvent event) {
- return mMagnetizedObject.maybeConsumeMotionEvent(event);
+ int maybeConsumeMoveMotionEvent(MotionEvent event) {
+ return maybeConsumeMotionEvent(event);
}
/**
@@ -144,31 +155,93 @@
* within the magnetic field. It should be used in the {@link MenuListViewTouchHandler}.
*
* @param event that move the magnetized object which is also the menu list view.
- * @return true if the location of the motion events moves within the magnetic field of a
- * target, but false if didn't set
+ * @return id of a target if the location of the motion events moves
+ * within the field of the target, otherwise it returns{@link android.R.id#empty}.
* {@link DragToInteractAnimationController#setMagnetListener(MagnetizedObject.MagnetListener)}.
*/
- boolean maybeConsumeUpMotionEvent(MotionEvent event) {
- return mMagnetizedObject.maybeConsumeMotionEvent(event);
+ int maybeConsumeUpMotionEvent(MotionEvent event) {
+ return maybeConsumeMotionEvent(event);
}
- void animateDismissMenu(boolean scaleUp) {
+ void animateInteractMenu(int targetViewId, boolean scaleUp) {
+ Pair<MagnetizedObject<MenuView>, ValueAnimator> value = mInteractMap.get(targetViewId);
+ if (value == null) {
+ return;
+ }
+ ValueAnimator animator = value.second;
if (scaleUp) {
- mDismissAnimator.start();
+ animator.start();
} else {
- mDismissAnimator.reverse();
+ animator.reverse();
}
}
void updateResources() {
- final float maxDismissSize = mDismissView.getResources().getDimensionPixelSize(
+ final float maxInteractSize = mMenuView.getResources().getDimensionPixelSize(
com.android.wm.shell.R.dimen.dismiss_circle_size);
- mMinDismissSize = mDismissView.getResources().getDimensionPixelSize(
+ mMinInteractSize = mMenuView.getResources().getDimensionPixelSize(
com.android.wm.shell.R.dimen.dismiss_circle_small);
- mSizePercent = mMinDismissSize / maxDismissSize;
+ mSizePercent = mMinInteractSize / maxInteractSize;
}
- interface DismissCallback {
- void onDismiss();
+ /**
+ * Creates a magnetizedObject & valueAnimator pair for the provided circleView,
+ * and adds them to the interactMap.
+ *
+ * @param circleView circleView to create objects for.
+ */
+ private void createMagnetizedObjectAndAnimator(DismissCircleView circleView) {
+ MagnetizedObject<MenuView> magnetizedObject = new MagnetizedObject<MenuView>(
+ mMenuView.getContext(), mMenuView,
+ new MenuAnimationController.MenuPositionProperty(
+ DynamicAnimation.TRANSLATION_X),
+ new MenuAnimationController.MenuPositionProperty(
+ DynamicAnimation.TRANSLATION_Y)) {
+ @Override
+ public void getLocationOnScreen(MenuView underlyingObject, @NonNull int[] loc) {
+ underlyingObject.getLocationOnScreen(loc);
+ }
+
+ @Override
+ public float getHeight(MenuView underlyingObject) {
+ return underlyingObject.getHeight();
+ }
+
+ @Override
+ public float getWidth(MenuView underlyingObject) {
+ return underlyingObject.getWidth();
+ }
+ };
+ // Avoid unintended selection of an object / option
+ magnetizedObject.setFlingToTargetEnabled(false);
+ magnetizedObject.addTarget(new MagnetizedObject.MagneticTarget(
+ circleView, (int) mMinInteractSize));
+
+ final ValueAnimator animator =
+ ValueAnimator.ofFloat(COMPLETELY_OPAQUE, COMPLETELY_TRANSPARENT);
+
+ animator.addUpdateListener(dismissAnimation -> {
+ final float animatedValue = (float) dismissAnimation.getAnimatedValue();
+ final float scaleValue = Math.max(animatedValue, mSizePercent);
+ circleView.setScaleX(scaleValue);
+ circleView.setScaleY(scaleValue);
+
+ mMenuView.setAlpha(Math.max(animatedValue, ANIMATING_MAX_ALPHA));
+ });
+
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(@NonNull Animator animation, boolean isReverse) {
+ super.onAnimationEnd(animation, isReverse);
+
+ if (isReverse) {
+ circleView.setScaleX(CIRCLE_VIEW_DEFAULT_SCALE);
+ circleView.setScaleY(CIRCLE_VIEW_DEFAULT_SCALE);
+ mMenuView.setAlpha(COMPLETELY_OPAQUE);
+ }
+ }
+ });
+
+ mInteractMap.put(circleView.getId(), new Pair<>(magnetizedObject, animator));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractView.kt b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractView.kt
new file mode 100644
index 0000000..0ef3d20
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractView.kt
@@ -0,0 +1,322 @@
+/*
+ * 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.accessibility.floatingmenu
+
+import android.animation.ObjectAnimator
+import android.content.Context
+import android.graphics.Color
+import android.graphics.drawable.GradientDrawable
+import android.util.ArrayMap
+import android.util.IntProperty
+import android.util.Log
+import android.view.Gravity
+import android.view.View
+import android.view.ViewGroup
+import android.view.WindowInsets
+import android.view.WindowManager
+import android.widget.FrameLayout
+import android.widget.LinearLayout
+import android.widget.Space
+import androidx.annotation.ColorRes
+import androidx.annotation.DimenRes
+import androidx.annotation.DrawableRes
+import androidx.core.content.ContextCompat
+import androidx.dynamicanimation.animation.DynamicAnimation
+import androidx.dynamicanimation.animation.SpringForce.DAMPING_RATIO_LOW_BOUNCY
+import androidx.dynamicanimation.animation.SpringForce.STIFFNESS_LOW
+import com.android.wm.shell.R
+import com.android.wm.shell.animation.PhysicsAnimator
+import com.android.wm.shell.common.bubbles.DismissCircleView
+import com.android.wm.shell.common.bubbles.DismissView
+
+/**
+ * View that handles interactions between DismissCircleView and BubbleStackView.
+ *
+ * @note [setup] method should be called after initialisation
+ */
+class DragToInteractView(context: Context) : FrameLayout(context) {
+ /**
+ * The configuration is used to provide module specific resource ids
+ *
+ * @see [setup] method
+ */
+ data class Config(
+ /** dimen resource id of the dismiss target circle view size */
+ @DimenRes val targetSizeResId: Int,
+ /** dimen resource id of the icon size in the dismiss target */
+ @DimenRes val iconSizeResId: Int,
+ /** dimen resource id of the bottom margin for the dismiss target */
+ @DimenRes var bottomMarginResId: Int,
+ /** dimen resource id of the height for dismiss area gradient */
+ @DimenRes val floatingGradientHeightResId: Int,
+ /** color resource id of the dismiss area gradient color */
+ @ColorRes val floatingGradientColorResId: Int,
+ /** drawable resource id of the dismiss target background */
+ @DrawableRes val backgroundResId: Int,
+ /** drawable resource id of the icon for the dismiss target */
+ @DrawableRes val iconResId: Int
+ )
+
+ companion object {
+ private const val SHOULD_SETUP = "The view isn't ready. Should be called after `setup`"
+ private val TAG = DragToInteractView::class.simpleName
+ }
+
+ // START DragToInteractView modification
+ // We could technically access each DismissCircleView from their Animator,
+ // but the animators only store a weak reference to their targets. This is safer.
+ var interactMap = ArrayMap<Int, Pair<DismissCircleView, PhysicsAnimator<DismissCircleView>>>()
+ // END DragToInteractView modification
+ var isShowing = false
+ var config: Config? = null
+
+ private val spring = PhysicsAnimator.SpringConfig(STIFFNESS_LOW, DAMPING_RATIO_LOW_BOUNCY)
+ private val INTERACT_SCRIM_FADE_MS = 200L
+ private var wm: WindowManager =
+ context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
+ private var gradientDrawable: GradientDrawable? = null
+
+ private val GRADIENT_ALPHA: IntProperty<GradientDrawable> =
+ object : IntProperty<GradientDrawable>("alpha") {
+ override fun setValue(d: GradientDrawable, percent: Int) {
+ d.alpha = percent
+ }
+ override fun get(d: GradientDrawable): Int {
+ return d.alpha
+ }
+ }
+
+ init {
+ clipToPadding = false
+ clipChildren = false
+ visibility = View.INVISIBLE
+
+ // START DragToInteractView modification
+ // Resources included within implementation as we aren't concerned with decoupling them.
+ setup(
+ Config(
+ targetSizeResId = R.dimen.dismiss_circle_size,
+ iconSizeResId = R.dimen.dismiss_target_x_size,
+ bottomMarginResId = R.dimen.floating_dismiss_bottom_margin,
+ floatingGradientHeightResId = R.dimen.floating_dismiss_gradient_height,
+ floatingGradientColorResId = android.R.color.system_neutral1_900,
+ backgroundResId = R.drawable.dismiss_circle_background,
+ iconResId = R.drawable.pip_ic_close_white
+ )
+ )
+ // END DragToInteractView modification
+ }
+
+ /**
+ * Sets up view with the provided resource ids.
+ *
+ * Decouples resource dependency in order to be used externally (e.g. Launcher). Usually called
+ * with default params in module specific extension:
+ *
+ * @see [DismissView.setup] in DismissViewExt.kt
+ */
+ fun setup(config: Config) {
+ this.config = config
+
+ // Setup layout
+ layoutParams =
+ LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ resources.getDimensionPixelSize(config.floatingGradientHeightResId),
+ Gravity.BOTTOM
+ )
+ updatePadding()
+
+ // Setup gradient
+ gradientDrawable = createGradient(color = config.floatingGradientColorResId)
+ background = gradientDrawable
+
+ // START DragToInteractView modification
+
+ // Setup LinearLayout. Added to organize multiple circles.
+ val linearLayout = LinearLayout(context)
+ linearLayout.layoutParams =
+ LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT
+ )
+ linearLayout.weightSum = 0f
+ addView(linearLayout)
+
+ // Setup DismissCircleView. Code block replaced with repeatable functions
+ addSpace(linearLayout)
+ addCircle(
+ config,
+ com.android.systemui.res.R.id.action_remove_menu,
+ R.drawable.pip_ic_close_white,
+ linearLayout
+ )
+ addCircle(
+ config,
+ com.android.systemui.res.R.id.action_edit,
+ com.android.systemui.res.R.drawable.ic_screenshot_edit,
+ linearLayout
+ )
+ // END DragToInteractView modification
+ }
+
+ /** Animates this view in. */
+ fun show() {
+ if (isShowing) return
+ val gradientDrawable = checkExists(gradientDrawable) ?: return
+ isShowing = true
+ visibility = View.VISIBLE
+ val alphaAnim =
+ ObjectAnimator.ofInt(gradientDrawable, GRADIENT_ALPHA, gradientDrawable.alpha, 255)
+ alphaAnim.duration = INTERACT_SCRIM_FADE_MS
+ alphaAnim.start()
+
+ // START DragToInteractView modification
+ interactMap.forEach {
+ val animator = it.value.second
+ animator.cancel()
+ animator.spring(DynamicAnimation.TRANSLATION_Y, 0f, spring).start()
+ }
+ // END DragToInteractView modification
+ }
+
+ /**
+ * Animates this view out, as well as the circle that encircles the bubbles, if they were
+ * dragged into the target and encircled.
+ */
+ fun hide() {
+ if (!isShowing) return
+ val gradientDrawable = checkExists(gradientDrawable) ?: return
+ isShowing = false
+ val alphaAnim =
+ ObjectAnimator.ofInt(gradientDrawable, GRADIENT_ALPHA, gradientDrawable.alpha, 0)
+ alphaAnim.duration = INTERACT_SCRIM_FADE_MS
+ alphaAnim.start()
+
+ // START DragToInteractView modification
+ interactMap.forEach {
+ val animator = it.value.second
+ animator
+ .spring(DynamicAnimation.TRANSLATION_Y, height.toFloat(), spring)
+ .withEndActions({ visibility = View.INVISIBLE })
+ .start()
+ }
+ // END DragToInteractView modification
+ }
+
+ /** Cancels the animator for the dismiss target. */
+ fun cancelAnimators() {
+ // START DragToInteractView modification
+ interactMap.forEach {
+ val animator = it.value.second
+ animator.cancel()
+ }
+ // END DragToInteractView modification
+ }
+
+ fun updateResources() {
+ val config = checkExists(config) ?: return
+ updatePadding()
+ layoutParams.height = resources.getDimensionPixelSize(config.floatingGradientHeightResId)
+ val targetSize = resources.getDimensionPixelSize(config.targetSizeResId)
+
+ // START DragToInteractView modification
+ interactMap.forEach {
+ val circle = it.value.first
+ circle.layoutParams.width = targetSize
+ circle.layoutParams.height = targetSize
+ circle.requestLayout()
+ }
+ // END DragToInteractView modification
+ }
+
+ private fun createGradient(@ColorRes color: Int): GradientDrawable {
+ val gradientColor = ContextCompat.getColor(context, color)
+ val alpha = 0.7f * 255
+ val gradientColorWithAlpha =
+ Color.argb(
+ alpha.toInt(),
+ Color.red(gradientColor),
+ Color.green(gradientColor),
+ Color.blue(gradientColor)
+ )
+ val gd =
+ GradientDrawable(
+ GradientDrawable.Orientation.BOTTOM_TOP,
+ intArrayOf(gradientColorWithAlpha, Color.TRANSPARENT)
+ )
+ gd.setDither(true)
+ gd.alpha = 0
+ return gd
+ }
+
+ private fun updatePadding() {
+ val config = checkExists(config) ?: return
+ val insets: WindowInsets = wm.currentWindowMetrics.windowInsets
+ val navInset = insets.getInsetsIgnoringVisibility(WindowInsets.Type.navigationBars())
+ setPadding(
+ 0,
+ 0,
+ 0,
+ navInset.bottom + resources.getDimensionPixelSize(config.bottomMarginResId)
+ )
+ }
+
+ /**
+ * Checks if the value is set up and exists, if not logs an exception. Used for convenient
+ * logging in case `setup` wasn't called before
+ *
+ * @return value provided as argument
+ */
+ private fun <T> checkExists(value: T?): T? {
+ if (value == null) Log.e(TAG, SHOULD_SETUP)
+ return value
+ }
+
+ // START DragToInteractView modification
+ private fun addSpace(parent: LinearLayout) {
+ val space = Space(context)
+ // Spaces are weighted equally to space out circles evenly
+ space.layoutParams =
+ LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ 1f
+ )
+ parent.addView(space)
+ parent.weightSum = parent.weightSum + 1f
+ }
+
+ private fun addCircle(config: Config, id: Int, iconResId: Int, parent: LinearLayout) {
+ val targetSize = resources.getDimensionPixelSize(config.targetSizeResId)
+ val circleLayoutParams = LinearLayout.LayoutParams(targetSize, targetSize, 0f)
+ circleLayoutParams.gravity = Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL
+ val circle = DismissCircleView(context)
+ circle.id = id
+ circle.setup(config.backgroundResId, iconResId, config.iconSizeResId)
+ circle.layoutParams = circleLayoutParams
+
+ // Initial position with circle offscreen so it's animated up
+ circle.translationY =
+ resources.getDimensionPixelSize(config.floatingGradientHeightResId).toFloat()
+
+ interactMap[circle.id] = Pair(circle, PhysicsAnimator.getInstance(circle))
+ parent.addView(circle)
+ addSpace(parent)
+ }
+ // END DragToInteractView modification
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java
index a270558..d3e85e0 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java
@@ -37,7 +37,6 @@
import androidx.recyclerview.widget.RecyclerView;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.Preconditions;
import com.android.systemui.Flags;
import java.util.HashMap;
@@ -73,7 +72,6 @@
private final ValueAnimator mFadeOutAnimator;
private final Handler mHandler;
private boolean mIsFadeEffectEnabled;
- private DragToInteractAnimationController.DismissCallback mDismissCallback;
private Runnable mSpringAnimationsEndAction;
// Cache the animations state of {@link DynamicAnimation.TRANSLATION_X} and {@link
@@ -170,11 +168,6 @@
mSpringAnimationsEndAction = runnable;
}
- void setDismissCallback(
- DragToInteractAnimationController.DismissCallback dismissCallback) {
- mDismissCallback = dismissCallback;
- }
-
void moveToTopLeftPosition() {
mMenuView.updateMenuMoveToTucked(/* isMoveToTucked= */ false);
final Rect draggableBounds = mMenuView.getMenuDraggableBounds();
@@ -205,13 +198,6 @@
constrainPositionAndUpdate(position, /* writeToPosition = */ true);
}
- void removeMenu() {
- Preconditions.checkArgument(mDismissCallback != null,
- "The dismiss callback should be initialized first.");
-
- mDismissCallback.onDismiss();
- }
-
void flingMenuThenSpringToEdge(float x, float velocityX, float velocityY) {
final boolean shouldMenuFlingLeft = isOnLeftSide()
? velocityX < ESCAPE_VELOCITY
@@ -334,8 +320,6 @@
moveToEdgeAndHide();
return true;
}
-
- fadeOutIfEnabled();
return false;
}
@@ -453,8 +437,6 @@
mMenuView.onBoundsInParentChanged((int) position.x, (int) position.y);
constrainPositionAndUpdate(position, writeToPosition);
- fadeOutIfEnabled();
-
if (mSpringAnimationsEndAction != null) {
mSpringAnimationsEndAction.run();
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegate.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegate.java
index 9c22a77..975a602 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegate.java
@@ -27,6 +27,7 @@
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
import androidx.recyclerview.widget.RecyclerViewAccessibilityDelegate;
+import com.android.systemui.Flags;
import com.android.systemui.res.R;
/**
@@ -35,15 +36,18 @@
*/
class MenuItemAccessibilityDelegate extends RecyclerViewAccessibilityDelegate.ItemDelegate {
private final MenuAnimationController mAnimationController;
+ private final MenuViewLayer mMenuViewLayer;
MenuItemAccessibilityDelegate(@NonNull RecyclerViewAccessibilityDelegate recyclerViewDelegate,
- MenuAnimationController animationController) {
+ MenuAnimationController animationController, MenuViewLayer menuViewLayer) {
super(recyclerViewDelegate);
mAnimationController = animationController;
+ mMenuViewLayer = menuViewLayer;
}
@Override
- public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
+ public void onInitializeAccessibilityNodeInfo(
+ @NonNull View host, @NonNull AccessibilityNodeInfoCompat info) {
super.onInitializeAccessibilityNodeInfo(host, info);
final Resources res = host.getResources();
@@ -90,6 +94,15 @@
R.id.action_remove_menu,
res.getString(R.string.accessibility_floating_button_action_remove_menu));
info.addAction(removeMenu);
+
+ if (Flags.floatingMenuDragToEdit()) {
+ final AccessibilityNodeInfoCompat.AccessibilityActionCompat edit =
+ new AccessibilityNodeInfoCompat.AccessibilityActionCompat(
+ R.id.action_edit,
+ res.getString(
+ R.string.accessibility_floating_button_action_remove_menu));
+ info.addAction(edit);
+ }
}
@Override
@@ -132,8 +145,8 @@
return true;
}
- if (action == R.id.action_remove_menu) {
- mAnimationController.removeMenu();
+ if (action == R.id.action_remove_menu || action == R.id.action_edit) {
+ mMenuViewLayer.dispatchAccessibilityAction(action);
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandler.java
index 52e7b91..7519168 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandler.java
@@ -16,6 +16,8 @@
package com.android.systemui.accessibility.floatingmenu;
+import static android.R.id.empty;
+
import android.graphics.PointF;
import android.view.MotionEvent;
import android.view.VelocityTracker;
@@ -78,10 +80,9 @@
mMenuAnimationController.onDraggingStart();
}
- mDragToInteractAnimationController.showDismissView(/* show= */ true);
-
- if (!mDragToInteractAnimationController.maybeConsumeMoveMotionEvent(
- motionEvent)) {
+ mDragToInteractAnimationController.showInteractView(/* show= */ true);
+ if (mDragToInteractAnimationController.maybeConsumeMoveMotionEvent(motionEvent)
+ == empty) {
mMenuAnimationController.moveToPositionX(mMenuTranslationDown.x + dx);
mMenuAnimationController.moveToPositionYIfNeeded(
mMenuTranslationDown.y + dy);
@@ -94,21 +95,19 @@
final float endX = mMenuTranslationDown.x + dx;
mIsDragging = false;
- if (mMenuAnimationController.maybeMoveToEdgeAndHide(endX)) {
- mDragToInteractAnimationController.showDismissView(/* show= */ false);
- mMenuAnimationController.fadeOutIfEnabled();
+ mDragToInteractAnimationController.showInteractView(/* show= */ false);
+ if (mMenuAnimationController.maybeMoveToEdgeAndHide(endX)) {
+ mMenuAnimationController.fadeOutIfEnabled();
return true;
}
- if (!mDragToInteractAnimationController.maybeConsumeUpMotionEvent(
- motionEvent)) {
+ if (mDragToInteractAnimationController.maybeConsumeUpMotionEvent(motionEvent)
+ == empty) {
mVelocityTracker.computeCurrentVelocity(VELOCITY_UNIT_SECONDS);
mMenuAnimationController.flingMenuThenSpringToEdge(endX,
mVelocityTracker.getXVelocity(), mVelocityTracker.getYVelocity());
- mDragToInteractAnimationController.showDismissView(/* show= */ false);
}
-
// Avoid triggering the listener of the item.
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java
index 76808cb..334cc87 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java
@@ -21,24 +21,28 @@
import android.annotation.SuppressLint;
import android.content.ComponentCallbacks;
import android.content.Context;
+import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.drawable.GradientDrawable;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.provider.SettingsStringUtil;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
-import androidx.core.view.AccessibilityDelegateCompat;
import androidx.lifecycle.Observer;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
-import androidx.recyclerview.widget.RecyclerViewAccessibilityDelegate;
import com.android.internal.accessibility.dialog.AccessibilityTarget;
import com.android.systemui.Flags;
+import com.android.systemui.util.settings.SecureSettings;
import java.util.ArrayList;
import java.util.Collections;
@@ -72,26 +76,20 @@
private final MenuAnimationController mMenuAnimationController;
private OnTargetFeaturesChangeListener mFeaturesChangeListener;
private OnMoveToTuckedListener mMoveToTuckedListener;
+ private SecureSettings mSecureSettings;
- MenuView(Context context, MenuViewModel menuViewModel, MenuViewAppearance menuViewAppearance) {
+ MenuView(Context context, MenuViewModel menuViewModel, MenuViewAppearance menuViewAppearance,
+ SecureSettings secureSettings) {
super(context);
mMenuViewModel = menuViewModel;
mMenuViewAppearance = menuViewAppearance;
+ mSecureSettings = secureSettings;
mMenuAnimationController = new MenuAnimationController(this, menuViewAppearance);
mAdapter = new AccessibilityTargetAdapter(mTargetFeatures);
mTargetFeaturesView = new RecyclerView(context);
mTargetFeaturesView.setAdapter(mAdapter);
mTargetFeaturesView.setLayoutManager(new LinearLayoutManager(context));
- mTargetFeaturesView.setAccessibilityDelegateCompat(
- new RecyclerViewAccessibilityDelegate(mTargetFeaturesView) {
- @NonNull
- @Override
- public AccessibilityDelegateCompat getItemDelegate() {
- return new MenuItemAccessibilityDelegate(/* recyclerViewDelegate= */ this,
- mMenuAnimationController);
- }
- });
setLayoutParams(new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
// Avoid drawing out of bounds of the parent view
setClipToOutline(true);
@@ -278,6 +276,7 @@
if (mFeaturesChangeListener != null) {
mFeaturesChangeListener.onChange(newTargetFeatures);
}
+
mMenuAnimationController.fadeOutIfEnabled();
}
@@ -306,6 +305,10 @@
return mMenuViewAppearance.getMenuPosition();
}
+ RecyclerView getTargetFeaturesView() {
+ return mTargetFeaturesView;
+ }
+
void persistPositionAndUpdateEdge(Position percentagePosition) {
mMenuViewModel.updateMenuSavingPosition(percentagePosition);
mMenuViewAppearance.setPercentagePosition(percentagePosition);
@@ -424,6 +427,35 @@
onPositionChanged();
}
+ void gotoEditScreen() {
+ if (!Flags.floatingMenuDragToEdit()) {
+ return;
+ }
+ mMenuAnimationController.flingMenuThenSpringToEdge(
+ getMenuPosition().x, 100f, 0f);
+ mContext.startActivity(getIntentForEditScreen());
+ }
+
+ Intent getIntentForEditScreen() {
+ List<String> targets = new SettingsStringUtil.ColonDelimitedSet.OfStrings(
+ mSecureSettings.getStringForUser(
+ Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
+ UserHandle.USER_CURRENT)).stream().toList();
+
+ Intent intent = new Intent(
+ Settings.ACTION_ACCESSIBILITY_SHORTCUT_SETTINGS);
+ Bundle args = new Bundle();
+ Bundle fragmentArgs = new Bundle();
+ fragmentArgs.putStringArray("targets", targets.toArray(new String[0]));
+ args.putBundle(":settings:show_fragment_args", fragmentArgs);
+ // TODO: b/318748373 - The fragment should set its own title using the targets
+ args.putString(
+ ":settings:show_fragment_title", "Accessibility Shortcut");
+ intent.replaceExtras(args);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ return intent;
+ }
+
private InstantInsetLayerDrawable getContainerViewInsetLayer() {
return (InstantInsetLayerDrawable) getBackground();
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
index 97999cc..bb5364d 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
@@ -59,7 +59,10 @@
import android.widget.TextView;
import androidx.annotation.NonNull;
+import androidx.core.view.AccessibilityDelegateCompat;
import androidx.lifecycle.Observer;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.recyclerview.widget.RecyclerViewAccessibilityDelegate;
import com.android.internal.accessibility.dialog.AccessibilityTarget;
import com.android.internal.annotations.VisibleForTesting;
@@ -94,6 +97,8 @@
private final MenuListViewTouchHandler mMenuListViewTouchHandler;
private final MenuMessageView mMessageView;
private final DismissView mDismissView;
+ private final DragToInteractView mDragToInteractView;
+
private final MenuViewAppearance mMenuViewAppearance;
private final MenuAnimationController mMenuAnimationController;
private final AccessibilityManager mAccessibilityManager;
@@ -178,7 +183,10 @@
};
MenuViewLayer(@NonNull Context context, WindowManager windowManager,
- AccessibilityManager accessibilityManager, IAccessibilityFloatingMenu floatingMenu,
+ AccessibilityManager accessibilityManager,
+ MenuViewModel menuViewModel,
+ MenuViewAppearance menuViewAppearance, MenuView menuView,
+ IAccessibilityFloatingMenu floatingMenu,
SecureSettings secureSettings) {
super(context);
@@ -190,43 +198,52 @@
mFloatingMenu = floatingMenu;
mSecureSettings = secureSettings;
- mMenuViewModel = new MenuViewModel(context, accessibilityManager, secureSettings);
- mMenuViewAppearance = new MenuViewAppearance(context, windowManager);
- mMenuView = new MenuView(context, mMenuViewModel, mMenuViewAppearance);
+ mMenuViewModel = menuViewModel;
+ mMenuViewAppearance = menuViewAppearance;
+ mMenuView = menuView;
+ RecyclerView targetFeaturesView = mMenuView.getTargetFeaturesView();
+ targetFeaturesView.setAccessibilityDelegateCompat(
+ new RecyclerViewAccessibilityDelegate(targetFeaturesView) {
+ @NonNull
+ @Override
+ public AccessibilityDelegateCompat getItemDelegate() {
+ return new MenuItemAccessibilityDelegate(/* recyclerViewDelegate= */ this,
+ mMenuAnimationController, MenuViewLayer.this);
+ }
+ });
mMenuAnimationController = mMenuView.getMenuAnimationController();
- if (Flags.floatingMenuDragToHide()) {
- mMenuAnimationController.setDismissCallback(this::hideMenuAndShowNotification);
- } else {
- mMenuAnimationController.setDismissCallback(this::hideMenuAndShowMessage);
- }
mMenuAnimationController.setSpringAnimationsEndAction(this::onSpringAnimationsEndAction);
mDismissView = new DismissView(context);
+ mDragToInteractView = new DragToInteractView(context);
DismissViewUtils.setup(mDismissView);
+ mDismissView.getCircle().setId(R.id.action_remove_menu);
mNotificationFactory = new MenuNotificationFactory(context);
mNotificationManager = context.getSystemService(NotificationManager.class);
- mDragToInteractAnimationController = new DragToInteractAnimationController(
- mDismissView, mMenuView);
+
+ if (Flags.floatingMenuDragToEdit()) {
+ mDragToInteractAnimationController = new DragToInteractAnimationController(
+ mDragToInteractView, mMenuView);
+ } else {
+ mDragToInteractAnimationController = new DragToInteractAnimationController(
+ mDismissView, mMenuView);
+ }
mDragToInteractAnimationController.setMagnetListener(new MagnetizedObject.MagnetListener() {
@Override
public void onStuckToTarget(@NonNull MagnetizedObject.MagneticTarget target) {
- mDragToInteractAnimationController.animateDismissMenu(/* scaleUp= */ true);
+ mDragToInteractAnimationController.animateInteractMenu(
+ target.getTargetView().getId(), /* scaleUp= */ true);
}
@Override
public void onUnstuckFromTarget(@NonNull MagnetizedObject.MagneticTarget target,
float velocityX, float velocityY, boolean wasFlungOut) {
- mDragToInteractAnimationController.animateDismissMenu(/* scaleUp= */ false);
+ mDragToInteractAnimationController.animateInteractMenu(
+ target.getTargetView().getId(), /* scaleUp= */ false);
}
@Override
public void onReleasedInTarget(@NonNull MagnetizedObject.MagneticTarget target) {
- if (Flags.floatingMenuDragToHide()) {
- hideMenuAndShowNotification();
- } else {
- hideMenuAndShowMessage();
- }
- mDismissView.hide();
- mDragToInteractAnimationController.animateDismissMenu(/* scaleUp= */ false);
+ dispatchAccessibilityAction(target.getTargetView().getId());
}
});
@@ -262,7 +279,11 @@
});
addView(mMenuView, LayerIndex.MENU_VIEW);
- addView(mDismissView, LayerIndex.DISMISS_VIEW);
+ if (Flags.floatingMenuDragToEdit()) {
+ addView(mDragToInteractView, LayerIndex.DISMISS_VIEW);
+ } else {
+ addView(mDismissView, LayerIndex.DISMISS_VIEW);
+ }
addView(mMessageView, LayerIndex.MESSAGE_VIEW);
if (Flags.floatingMenuAnimatedTuck()) {
@@ -272,6 +293,7 @@
@Override
public void onConfigurationChanged(@NonNull Configuration newConfig) {
+ mDragToInteractView.updateResources();
mDismissView.updateResources();
mDragToInteractAnimationController.updateResources();
}
@@ -428,6 +450,23 @@
}
}
+ void dispatchAccessibilityAction(int id) {
+ if (id == R.id.action_remove_menu) {
+ if (Flags.floatingMenuDragToHide()) {
+ hideMenuAndShowNotification();
+ } else {
+ hideMenuAndShowMessage();
+ }
+ } else if (id == R.id.action_edit
+ && Flags.floatingMenuDragToEdit()) {
+ mMenuView.gotoEditScreen();
+ }
+ mDismissView.hide();
+ mDragToInteractView.hide();
+ mDragToInteractAnimationController.animateInteractMenu(
+ id, /* scaleUp= */ false);
+ }
+
private CharSequence getMigrationMessage() {
final Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_DETAILS_SETTINGS);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
@@ -475,7 +514,8 @@
mEduTooltipView = Optional.empty();
}
- private void hideMenuAndShowMessage() {
+ @VisibleForTesting
+ void hideMenuAndShowMessage() {
final int delayTime = mAccessibilityManager.getRecommendedTimeoutMillis(
SHOW_MESSAGE_DELAY_MS,
AccessibilityManager.FLAG_CONTENT_TEXT
@@ -485,7 +525,8 @@
mMenuAnimationController.startShrinkAnimation(() -> mMenuView.setVisibility(GONE));
}
- private void hideMenuAndShowNotification() {
+ @VisibleForTesting
+ void hideMenuAndShowNotification() {
mMenuAnimationController.startShrinkAnimation(() -> mMenuView.setVisibility(GONE));
showNotification();
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java
index 1f54952..bc9d1ff 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java
@@ -39,7 +39,16 @@
MenuViewLayerController(Context context, WindowManager windowManager,
AccessibilityManager accessibilityManager, SecureSettings secureSettings) {
mWindowManager = windowManager;
- mMenuViewLayer = new MenuViewLayer(context, windowManager, accessibilityManager, this,
+
+ MenuViewModel menuViewModel = new MenuViewModel(
+ context, accessibilityManager, secureSettings);
+ MenuViewAppearance menuViewAppearance = new MenuViewAppearance(context, windowManager);
+
+ mMenuViewLayer = new MenuViewLayer(context, windowManager, accessibilityManager,
+ menuViewModel,
+ menuViewAppearance,
+ new MenuView(context, menuViewModel, menuViewAppearance, secureSettings),
+ this,
secureSettings);
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index 71d0e7d..d2c6227 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -25,6 +25,7 @@
import android.hardware.biometrics.BiometricSourceType
import android.util.DisplayMetrics
import androidx.annotation.VisibleForTesting
+import androidx.lifecycle.repeatOnLifecycle
import com.android.app.animation.Interpolators
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
@@ -35,7 +36,11 @@
import com.android.systemui.biometrics.data.repository.FacePropertyRepository
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.deviceentry.domain.interactor.AuthRippleInteractor
+import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.keyguard.WakefulnessLifecycle
+import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
+import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.log.core.LogLevel
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.res.R
@@ -78,6 +83,7 @@
private val logger: KeyguardLogger,
private val biometricUnlockController: BiometricUnlockController,
private val lightRevealScrim: LightRevealScrim,
+ private val authRippleInteractor: AuthRippleInteractor,
private val facePropertyRepository: FacePropertyRepository,
rippleView: AuthRippleView?
) :
@@ -100,6 +106,22 @@
init()
}
+ init {
+ if (DeviceEntryUdfpsRefactor.isEnabled) {
+ rippleView?.repeatWhenAttached {
+ repeatOnLifecycle(androidx.lifecycle.Lifecycle.State.CREATED) {
+ authRippleInteractor.showUnlockRipple.collect { biometricUnlockSource ->
+ if (biometricUnlockSource == BiometricUnlockSource.FINGERPRINT_SENSOR) {
+ showUnlockRippleInternal(BiometricSourceType.FINGERPRINT)
+ } else {
+ showUnlockRippleInternal(BiometricSourceType.FACE)
+ }
+ }
+ }
+ }
+ }
+ }
+
@VisibleForTesting
public override fun onViewAttached() {
authController.addCallback(authControllerCallback)
@@ -111,7 +133,9 @@
keyguardStateController.addCallback(this)
wakefulnessLifecycle.addObserver(this)
commandRegistry.registerCommand("auth-ripple") { AuthRippleCommand() }
- biometricUnlockController.addListener(biometricModeListener)
+ if (!DeviceEntryUdfpsRefactor.isEnabled) {
+ biometricUnlockController.addListener(biometricModeListener)
+ }
}
private val biometricModeListener =
@@ -119,8 +143,9 @@
override fun onBiometricUnlockedWithKeyguardDismissal(
biometricSourceType: BiometricSourceType?
) {
+ DeviceEntryUdfpsRefactor.assertInLegacyMode()
if (biometricSourceType != null) {
- showUnlockRipple(biometricSourceType)
+ showUnlockRippleInternal(biometricSourceType)
} else {
logger.log(TAG,
LogLevel.ERROR,
@@ -143,7 +168,13 @@
notificationShadeWindowController.setForcePluginOpen(false, this)
}
- fun showUnlockRipple(biometricSourceType: BiometricSourceType) {
+ @Deprecated("Update authRippleInteractor.showUnlockRipple instead of calling this.")
+ fun showUnlockRipple(biometricSourceType: BiometricSourceType) {
+ DeviceEntryUdfpsRefactor.assertInLegacyMode()
+ showUnlockRippleInternal(biometricSourceType)
+ }
+
+ private fun showUnlockRippleInternal(biometricSourceType: BiometricSourceType) {
val keyguardNotShowing = !keyguardStateController.isShowing
val unlockNotAllowed = !keyguardUpdateMonitor
.isUnlockingWithBiometricAllowed(biometricSourceType)
@@ -377,12 +408,12 @@
}
"fingerprint" -> {
pw.println("fingerprint ripple sensorLocation=$fingerprintSensorLocation")
- showUnlockRipple(BiometricSourceType.FINGERPRINT)
+ showUnlockRippleInternal(BiometricSourceType.FINGERPRINT)
}
"face" -> {
// note: only shows when about to proceed to the home screen
pw.println("face ripple sensorLocation=$faceSensorLocation")
- showUnlockRipple(BiometricSourceType.FACE)
+ showUnlockRippleInternal(BiometricSourceType.FACE)
}
"custom" -> {
if (args.size != 3 ||
@@ -409,7 +440,7 @@
pw.println(" custom <x-location: int> <y-location: int>")
}
- fun invalidCommand(pw: PrintWriter) {
+ private fun invalidCommand(pw: PrintWriter) {
pw.println("invalid command")
help(pw)
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt
index c36e0e2..80d37b4 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt
@@ -121,11 +121,13 @@
if (it.isAttachedToWindow) {
lottie = it.requireViewById<LottieAnimationView>(R.id.sidefps_animation)
lottie?.pauseAnimation()
+ lottie?.removeAllLottieOnCompositionLoadedListener()
windowManager.get().removeView(it)
}
}
overlayView = layoutInflater.get().inflate(R.layout.sidefps_view, null, false)
+
val overlayViewModel =
SideFpsOverlayViewModel(
applicationContext,
@@ -163,8 +165,10 @@
val lottie = it.requireViewById<LottieAnimationView>(R.id.sidefps_animation)
lottie.addLottieOnCompositionLoadedListener { composition: LottieComposition ->
- viewModel.setLottieBounds(composition.bounds)
- overlayView.visibility = View.VISIBLE
+ if (overlayView.visibility != View.VISIBLE) {
+ viewModel.setLottieBounds(composition.bounds)
+ overlayView.visibility = View.VISIBLE
+ }
}
it.alpha = 0f
val overlayShowAnimator =
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
index 3287ed4..f36547b 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
@@ -27,21 +27,17 @@
import com.android.systemui.communal.widgets.CommunalAppWidgetHost
import com.android.systemui.communal.widgets.WidgetConfigurator
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.Logger
import com.android.systemui.log.dagger.CommunalLog
+import com.android.systemui.util.kotlin.getValue
import java.util.Optional
import javax.inject.Inject
import kotlin.coroutines.cancellation.CancellationException
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
@@ -67,18 +63,15 @@
* @param widgetIdToPriorityMap mapping of the widget ids to the priority of the widget.
*/
fun updateWidgetOrder(widgetIdToPriorityMap: Map<Int, Int>) {}
-
- /** Update whether the app widget host should be active. */
- fun updateAppWidgetHostActive(active: Boolean)
}
@SysUISingleton
class CommunalWidgetRepositoryImpl
@Inject
constructor(
- private val appWidgetManager: Optional<AppWidgetManager>,
+ appWidgetManagerOptional: Optional<AppWidgetManager>,
private val appWidgetHost: CommunalAppWidgetHost,
- @Application private val applicationScope: CoroutineScope,
+ @Background private val bgScope: CoroutineScope,
@Background private val bgDispatcher: CoroutineDispatcher,
private val communalWidgetHost: CommunalWidgetHost,
private val communalWidgetDao: CommunalWidgetDao,
@@ -90,41 +83,22 @@
private val logger = Logger(logBuffer, TAG)
- override fun updateAppWidgetHostActive(active: Boolean) {
- if (active == isHostActive.value) {
- return
- }
+ private val appWidgetManager by appWidgetManagerOptional
- if (active) {
- appWidgetHost.startListening()
- } else {
- appWidgetHost.stopListening()
- }
- isHostActive.value = active
- }
-
- private val isHostActive = MutableStateFlow(false)
-
- @OptIn(ExperimentalCoroutinesApi::class)
override val communalWidgets: Flow<List<CommunalWidgetContentModel>> =
- isHostActive.flatMapLatest { isHostActive ->
- if (!isHostActive || !appWidgetManager.isPresent) {
- return@flatMapLatest flowOf(emptyList())
- }
- communalWidgetDao
- .getWidgets()
- .map { it.map(::mapToContentModel) }
- // As this reads from a database and triggers IPCs to AppWidgetManager,
- // it should be executed in the background.
- .flowOn(bgDispatcher)
- }
+ communalWidgetDao
+ .getWidgets()
+ .map { it.mapNotNull(::mapToContentModel) }
+ // As this reads from a database and triggers IPCs to AppWidgetManager,
+ // it should be executed in the background.
+ .flowOn(bgDispatcher)
override fun addWidget(
provider: ComponentName,
priority: Int,
configurator: WidgetConfigurator?
) {
- applicationScope.launch(bgDispatcher) {
+ bgScope.launch {
val id = communalWidgetHost.allocateIdAndBindWidget(provider)
if (id == null) {
logger.e("Failed to allocate widget id to ${provider.flattenToString()}")
@@ -170,7 +144,7 @@
}
override fun deleteWidget(widgetId: Int) {
- applicationScope.launch(bgDispatcher) {
+ bgScope.launch {
communalWidgetDao.deleteWidgetById(widgetId)
appWidgetHost.deleteAppWidgetId(widgetId)
logger.i("Deleted widget with id $widgetId.")
@@ -178,7 +152,7 @@
}
override fun updateWidgetOrder(widgetIdToPriorityMap: Map<Int, Int>) {
- applicationScope.launch(bgDispatcher) {
+ bgScope.launch {
communalWidgetDao.updateWidgetOrder(widgetIdToPriorityMap)
logger.i({ "Updated the order of widget list with ids: $str1." }) {
str1 = widgetIdToPriorityMap.toString()
@@ -189,11 +163,12 @@
@WorkerThread
private fun mapToContentModel(
entry: Map.Entry<CommunalItemRank, CommunalWidgetItem>
- ): CommunalWidgetContentModel {
+ ): CommunalWidgetContentModel? {
val (_, widgetId) = entry.value
+ val providerInfo = appWidgetManager?.getAppWidgetInfo(widgetId) ?: return null
return CommunalWidgetContentModel(
appWidgetId = widgetId,
- providerInfo = appWidgetManager.get().getAppWidgetInfo(widgetId),
+ providerInfo = providerInfo,
priority = entry.key.rank,
)
}
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 c36f7fa..28adb77 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
@@ -37,18 +37,22 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.smartspace.data.repository.SmartspaceRepository
import com.android.systemui.user.data.repository.UserRepository
+import com.android.systemui.util.kotlin.BooleanFlowOperators.and
+import com.android.systemui.util.kotlin.BooleanFlowOperators.not
+import com.android.systemui.util.kotlin.BooleanFlowOperators.or
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
/** Encapsulates business-logic related to communal mode. */
@@ -68,6 +72,11 @@
private val appWidgetHost: CommunalAppWidgetHost,
private val editWidgetsActivityStarter: EditWidgetsActivityStarter
) {
+ private val _editModeOpen = MutableStateFlow(false)
+
+ /** Whether edit mode is currently open. */
+ val editModeOpen: StateFlow<Boolean> = _editModeOpen.asStateFlow()
+
/** Whether communal features are enabled. */
val isCommunalEnabled: Boolean
get() = communalRepository.isCommunalEnabled
@@ -80,21 +89,17 @@
val isCommunalAvailable: StateFlow<Boolean> =
flowOf(isCommunalEnabled)
.flatMapLatest { enabled ->
- if (enabled)
- combine(
- keyguardInteractor.isEncryptedOrLockdown,
- userRepository.selectedUserInfo,
- keyguardInteractor.isKeyguardVisible,
- keyguardInteractor.isDreaming,
- ) { isEncryptedOrLockdown, selectedUserInfo, isKeyguardVisible, isDreaming ->
- !isEncryptedOrLockdown &&
- selectedUserInfo.isMain &&
- (isKeyguardVisible || isDreaming)
- }
- else flowOf(false)
+ if (enabled) {
+ val isMainUser = userRepository.selectedUserInfo.map { it.isMain }
+ and(
+ isMainUser,
+ not(keyguardInteractor.isEncryptedOrLockdown),
+ or(keyguardInteractor.isKeyguardVisible, keyguardInteractor.isDreaming),
+ )
+ } else {
+ flowOf(false)
+ }
}
- .distinctUntilChanged()
- .onEach { available -> widgetRepository.updateAppWidgetHostActive(available) }
.stateIn(
scope = applicationScope,
started = SharingStarted.WhileSubscribed(),
@@ -166,6 +171,10 @@
communalRepository.setDesiredScene(newScene)
}
+ fun setEditModeOpen(isOpen: Boolean) {
+ _editModeOpen.value = isOpen
+ }
+
/** Show the widget editor Activity. */
fun showWidgetEditor() {
editWidgetsActivityStarter.startActivity()
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
index 237a0c0..4b98f1a 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
@@ -75,4 +75,7 @@
_reorderingWidgets.value = false
uiEventLogger.log(CommunalUiEvent.COMMUNAL_HUB_REORDER_WIDGET_CANCEL)
}
+
+ /** Sets whether edit mode is currently open */
+ fun setEditModeOpen(isOpen: Boolean) = communalInteractor.setEditModeOpen(isOpen)
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartable.kt
new file mode 100644
index 0000000..586df32
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartable.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.widgets
+
+import com.android.systemui.CoreStartable
+import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.util.kotlin.BooleanFlowOperators.or
+import com.android.systemui.util.kotlin.pairwise
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.withContext
+
+@SysUISingleton
+class CommunalAppWidgetHostStartable
+@Inject
+constructor(
+ private val appWidgetHost: CommunalAppWidgetHost,
+ private val communalInteractor: CommunalInteractor,
+ @Background private val bgScope: CoroutineScope,
+ @Main private val uiDispatcher: CoroutineDispatcher
+) : CoreStartable {
+ override fun start() {
+ or(communalInteractor.isCommunalAvailable, communalInteractor.editModeOpen)
+ // Only trigger updates on state changes, ignoring the initial false value.
+ .pairwise(false)
+ .filter { (previous, new) -> previous != new }
+ .onEach { (_, shouldListen) -> updateAppWidgetHostActive(shouldListen) }
+ .launchIn(bgScope)
+ }
+
+ private suspend fun updateAppWidgetHostActive(active: Boolean) =
+ // Always ensure this is called on the main/ui thread.
+ withContext(uiDispatcher) {
+ if (active) {
+ appWidgetHost.startListening()
+ } else {
+ appWidgetHost.stopListening()
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
index c7a14f9..a257543 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
@@ -86,6 +86,8 @@
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
+ communalViewModel.setEditModeOpen(true)
+
val windowInsetsController = window.decorView.windowInsetsController
windowInsetsController?.hide(WindowInsets.Type.systemBars())
window.setDecorFitsSystemWindows(false)
@@ -138,13 +140,16 @@
override fun onStart() {
super.onStart()
-
uiEventLogger.log(CommunalUiEvent.COMMUNAL_HUB_EDIT_MODE_SHOWN)
}
override fun onStop() {
super.onStop()
-
uiEventLogger.log(CommunalUiEvent.COMMUNAL_HUB_EDIT_MODE_GONE)
}
+
+ override fun onDestroy() {
+ super.onDestroy()
+ communalViewModel.setEditModeOpen(false)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
index 8d82b55..95233f7 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
@@ -25,6 +25,7 @@
import com.android.systemui.biometrics.BiometricNotificationService
import com.android.systemui.clipboardoverlay.ClipboardListener
import com.android.systemui.communal.log.CommunalLoggerStartable
+import com.android.systemui.communal.widgets.CommunalAppWidgetHostStartable
import com.android.systemui.controls.dagger.StartControlsStartableModule
import com.android.systemui.dagger.qualifiers.PerUser
import com.android.systemui.dreams.AssistantAttentionMonitor
@@ -324,4 +325,11 @@
@IntoMap
@ClassKey(CommunalLoggerStartable::class)
abstract fun bindCommunalLoggerStartable(impl: CommunalLoggerStartable): CoreStartable
+
+ @Binds
+ @IntoMap
+ @ClassKey(CommunalAppWidgetHostStartable::class)
+ abstract fun bindCommunalAppWidgetHostStartable(
+ impl: CommunalAppWidgetHostStartable
+ ): CoreStartable
}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt
index 7a70c4a..cf7d601 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt
@@ -40,8 +40,7 @@
import com.android.systemui.deviceentry.shared.model.HelpFaceAuthenticationStatus
import com.android.systemui.deviceentry.shared.model.SuccessFaceAuthenticationStatus
import com.android.systemui.dump.DumpManager
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.KeyguardWmStateRefactor
import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.BiometricType
import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
@@ -63,10 +62,6 @@
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.Job
@@ -88,6 +83,10 @@
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
+import java.io.PrintWriter
+import java.util.Arrays
+import java.util.stream.Collectors
+import javax.inject.Inject
/**
* API to run face authentication and detection for device entry / on keyguard (as opposed to the
@@ -165,7 +164,6 @@
@FaceAuthTableLog private val faceAuthLog: TableLogBuffer,
private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
private val displayStateInteractor: DisplayStateInteractor,
- private val featureFlags: FeatureFlags,
dumpManager: DumpManager,
) : DeviceEntryFaceAuthRepository, Dumpable {
private var authCancellationSignal: CancellationSignal? = null
@@ -315,7 +313,7 @@
// or device starts going to sleep.
merge(
powerInteractor.isAsleep,
- if (featureFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
+ if (KeyguardWmStateRefactor.isEnabled) {
keyguardTransitionInteractor.isInTransitionToState(KeyguardState.GONE)
} else {
keyguardRepository.keyguardDoneAnimationsFinished.map { true }
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt
index 08e8c2d..8283438 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt
@@ -8,35 +8,26 @@
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.keyguard.data.repository.KeyguardRepository
-import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
-import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.user.data.repository.UserRepository
-import com.android.systemui.util.kotlin.sample
import dagger.Binds
import dagger.Module
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
-import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.filter
-import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.withContext
/** Interface for classes that can access device-entry-related application state. */
interface DeviceEntryRepository {
- /** Whether the device is immediately entering the device after a biometric unlock. */
- val enteringDeviceFromBiometricUnlock: Flow<BiometricUnlockSource>
-
/**
* Whether the device is unlocked.
*
@@ -85,12 +76,6 @@
keyguardStateController: KeyguardStateController,
keyguardRepository: KeyguardRepository,
) : DeviceEntryRepository {
- override val enteringDeviceFromBiometricUnlock =
- keyguardRepository.biometricUnlockState
- .filter { BiometricUnlockModel.dismissesKeyguard(it) }
- .sample(
- keyguardRepository.biometricUnlockSource.filterNotNull(),
- )
private val _isUnlocked = MutableStateFlow(false)
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/AuthRippleInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/AuthRippleInteractor.kt
new file mode 100644
index 0000000..337fe1e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/AuthRippleInteractor.kt
@@ -0,0 +1,55 @@
+/*
+ * 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.deviceentry.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
+
+/** Business logic for device entry auth ripple interactions. */
+@ExperimentalCoroutinesApi
+@SysUISingleton
+class AuthRippleInteractor
+@Inject
+constructor(
+ deviceEntrySourceInteractor: DeviceEntrySourceInteractor,
+ deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
+) {
+ private val showUnlockRippleFromDeviceEntryIcon: Flow<BiometricUnlockSource> =
+ deviceEntryUdfpsInteractor.isUdfpsSupported.flatMapLatest { isUdfpsSupported ->
+ if (isUdfpsSupported) {
+ deviceEntrySourceInteractor.deviceEntryFromDeviceEntryIcon.map {
+ BiometricUnlockSource.FINGERPRINT_SENSOR
+ }
+ } else {
+ emptyFlow()
+ }
+ }
+
+ private val showUnlockRippleFromBiometricUnlock: Flow<BiometricUnlockSource> =
+ deviceEntrySourceInteractor.deviceEntryFromBiometricSource
+ val showUnlockRipple: Flow<BiometricUnlockSource> =
+ merge(
+ showUnlockRippleFromDeviceEntryIcon,
+ showUnlockRippleFromBiometricUnlock,
+ )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractor.kt
index 649a971..782bce4 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractor.kt
@@ -46,7 +46,7 @@
class DeviceEntryHapticsInteractor
@Inject
constructor(
- deviceEntryInteractor: DeviceEntryInteractor,
+ deviceEntrySourceInteractor: DeviceEntrySourceInteractor,
deviceEntryFingerprintAuthInteractor: DeviceEntryFingerprintAuthInteractor,
deviceEntryBiometricAuthInteractor: DeviceEntryBiometricAuthInteractor,
fingerprintPropertyRepository: FingerprintPropertyRepository,
@@ -80,7 +80,7 @@
}
val playSuccessHaptic: Flow<Unit> =
- deviceEntryInteractor.enteringDeviceFromBiometricUnlock
+ deviceEntrySourceInteractor.deviceEntryFromBiometricSource
.sample(
combine(
powerButtonSideFpsEnrolled,
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
index 0985357..73389cb 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
@@ -23,7 +23,6 @@
import com.android.systemui.deviceentry.data.repository.DeviceEntryFaceAuthRepository
import com.android.systemui.deviceentry.data.repository.DeviceEntryRepository
import com.android.systemui.keyguard.data.repository.TrustRepository
-import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlags
import com.android.systemui.scene.shared.model.SceneKey
@@ -31,7 +30,6 @@
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.collectLatest
@@ -55,7 +53,7 @@
@Inject
constructor(
@Application private val applicationScope: CoroutineScope,
- repository: DeviceEntryRepository,
+ private val repository: DeviceEntryRepository,
private val authenticationInteractor: AuthenticationInteractor,
private val sceneInteractor: SceneInteractor,
deviceEntryFaceAuthRepository: DeviceEntryFaceAuthRepository,
@@ -63,9 +61,6 @@
flags: SceneContainerFlags,
deviceUnlockedInteractor: DeviceUnlockedInteractor,
) {
- val enteringDeviceFromBiometricUnlock: Flow<BiometricUnlockSource> =
- repository.enteringDeviceFromBiometricUnlock
-
/**
* Whether the device is unlocked.
*
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntrySourceInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntrySourceInteractor.kt
new file mode 100644
index 0000000..d4f76a8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntrySourceInteractor.kt
@@ -0,0 +1,64 @@
+/*
+ * 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.deviceentry.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
+import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
+import com.android.systemui.util.kotlin.sample
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.map
+
+/**
+ * Hosts application business logic related to the source of the user entering the device. Note: The
+ * source of the user entering the device isn't equivalent to the reason the device is unlocked.
+ *
+ * For example, the user successfully enters the device when they dismiss the lockscreen via a
+ * bypass biometric or, if the device is already unlocked, by triggering an affordance that
+ * dismisses the lockscreen.
+ */
+@ExperimentalCoroutinesApi
+@SysUISingleton
+class DeviceEntrySourceInteractor
+@Inject
+constructor(
+ keyguardInteractor: KeyguardInteractor,
+) {
+ val deviceEntryFromBiometricSource: Flow<BiometricUnlockSource> =
+ keyguardInteractor.biometricUnlockState
+ .filter { BiometricUnlockModel.dismissesKeyguard(it) }
+ .sample(
+ keyguardInteractor.biometricUnlockSource.filterNotNull(),
+ )
+
+ private val attemptEnterDeviceFromDeviceEntryIcon: MutableSharedFlow<Unit> = MutableSharedFlow()
+ val deviceEntryFromDeviceEntryIcon: Flow<Unit> =
+ attemptEnterDeviceFromDeviceEntryIcon
+ .sample(keyguardInteractor.isKeyguardDismissible)
+ .filter { it } // only send events if the keyguard is dismissible
+ .map {} // map to Unit
+
+ suspend fun attemptEnterDeviceFromDeviceEntryIcon() {
+ attemptEnterDeviceFromDeviceEntryIcon.emit(Unit)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index d2883cc..c69c9ef 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -220,19 +220,6 @@
@JvmField
val WALLPAPER_PICKER_PREVIEW_ANIMATION = releasedFlag("wallpaper_picker_preview_animation")
- /**
- * TODO(b/278086361): Tracking bug
- * Complete rewrite of the interactions between System UI and Window Manager involving keyguard
- * state. When enabled, calls to ActivityTaskManagerService from System UI will exclusively
- * occur from [WmLockscreenVisibilityManager] rather than the legacy KeyguardViewMediator.
- *
- * This flag is under development; some types of unlock may not animate properly if you enable
- * it.
- */
- @JvmField
- val KEYGUARD_WM_STATE_REFACTOR: UnreleasedFlag =
- unreleasedFlag("keyguard_wm_state_refactor")
-
// 300 - power menu
// TODO(b/254512600): Tracking Bug
@JvmField val POWER_MENU_LITE = releasedFlag("power_menu_lite")
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index e2ab20e..f10b87e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -77,7 +77,6 @@
import com.android.systemui.SystemUIApplication;
import com.android.systemui.dagger.qualifiers.Application;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.ui.binder.KeyguardSurfaceBehindParamsApplier;
import com.android.systemui.keyguard.ui.binder.KeyguardSurfaceBehindViewBinder;
import com.android.systemui.keyguard.ui.binder.WindowManagerLockscreenVisibilityViewBinder;
@@ -329,7 +328,7 @@
mFlags = featureFlags;
mPowerInteractor = powerInteractor;
- if (mFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
+ if (KeyguardWmStateRefactor.isEnabled()) {
WindowManagerLockscreenVisibilityViewBinder.bind(
wmLockscreenVisibilityViewModel,
wmLockscreenVisibilityManager,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
index 01ba0d2..53c81e5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
@@ -419,7 +419,7 @@
*/
fun canPerformInWindowLauncherAnimations(): Boolean {
// TODO(b/278086361): Refactor in-window animations.
- return !featureFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR) &&
+ return !KeyguardWmStateRefactor.isEnabled &&
isSupportedLauncherUnderneath() &&
// If the launcher is underneath, but we're about to launch an activity, don't do
// the animations since they won't be visible.
@@ -866,7 +866,7 @@
}
surfaceBehindRemoteAnimationTargets?.forEach { surfaceBehindRemoteAnimationTarget ->
- if (!featureFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
+ if (!KeyguardWmStateRefactor.isEnabled) {
val surfaceHeight: Int =
surfaceBehindRemoteAnimationTarget.screenSpaceBounds.height()
@@ -1005,7 +1005,7 @@
if (keyguardStateController.isShowing) {
// Hide the keyguard, with no fade out since we animated it away during the unlock.
- if (!featureFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
+ if (!KeyguardWmStateRefactor.isEnabled) {
keyguardViewController.hide(
surfaceBehindRemoteAnimationStartTime,
0 /* fadeOutDuration */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 50caf17..8e3b196 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -139,7 +139,6 @@
import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.flags.SystemPropertiesHelper;
import com.android.systemui.keyguard.dagger.KeyguardModule;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
@@ -175,8 +174,6 @@
import com.android.systemui.wallpapers.data.repository.WallpaperRepository;
import com.android.wm.shell.keyguard.KeyguardTransitions;
-import dagger.Lazy;
-
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -186,6 +183,7 @@
import java.util.concurrent.Executor;
import java.util.function.Consumer;
+import dagger.Lazy;
import kotlinx.coroutines.CoroutineDispatcher;
/**
@@ -1051,7 +1049,7 @@
IRemoteAnimationFinishedCallback finishedCallback) {
Trace.beginSection("mExitAnimationRunner.onAnimationStart#startKeyguardExitAnimation");
startKeyguardExitAnimation(transit, apps, wallpapers, nonApps, finishedCallback);
- if (mFeatureFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
+ if (KeyguardWmStateRefactor.isEnabled()) {
mWmLockscreenVisibilityManager.get().onKeyguardGoingAwayRemoteAnimationStart(
transit, apps, wallpapers, nonApps, finishedCallback);
}
@@ -1061,7 +1059,7 @@
@Override // Binder interface
public void onAnimationCancelled() {
cancelKeyguardExitAnimation();
- if (mFeatureFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
+ if (KeyguardWmStateRefactor.isEnabled()) {
mWmLockscreenVisibilityManager.get().onKeyguardGoingAwayRemoteAnimationCancelled();
}
}
@@ -2757,7 +2755,7 @@
mUiBgExecutor.execute(() -> {
Log.d(TAG, "updateActivityLockScreenState(" + showing + ", " + aodShowing + ")");
- if (mFeatureFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
+ if (KeyguardWmStateRefactor.isEnabled()) {
// Handled in WmLockscreenVisibilityManager if flag is enabled.
return;
}
@@ -2811,7 +2809,7 @@
setShowingLocked(true, hidingOrGoingAway /* force */);
mHiding = false;
- if (!mFeatureFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
+ if (!KeyguardWmStateRefactor.isEnabled()) {
// Handled directly in StatusBarKeyguardViewManager if enabled.
mKeyguardViewControllerLazy.get().show(options);
}
@@ -2888,7 +2886,7 @@
mKeyguardViewControllerLazy.get().setKeyguardGoingAwayState(true);
// Handled in WmLockscreenVisibilityManager if flag is enabled.
- if (!mFeatureFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
+ if (!KeyguardWmStateRefactor.isEnabled()) {
// Don't actually hide the Keyguard at the moment, wait for window manager
// until it tells us it's safe to do so with startKeyguardExitAnimation.
// Posting to mUiOffloadThread to ensure that calls to ActivityTaskManager
@@ -2994,7 +2992,7 @@
} else {
Log.d(TAG, "Hiding keyguard while occluded. Just hide the keyguard view and exit.");
- if (!mFeatureFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
+ if (!KeyguardWmStateRefactor.isEnabled()) {
mKeyguardViewControllerLazy.get().hide(
mSystemClock.uptimeMillis() + mHideAnimation.getStartOffset(),
mHideAnimation.getDuration());
@@ -3030,7 +3028,7 @@
// If the flag is enabled, remote animation state is handled in
// WmLockscreenVisibilityManager.
if (finishedCallback != null
- && !mFeatureFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
+ && !KeyguardWmStateRefactor.isEnabled()) {
// There will not execute animation, send a finish callback to ensure the remote
// animation won't hang there.
try {
@@ -3056,7 +3054,7 @@
new IRemoteAnimationFinishedCallback() {
@Override
public void onAnimationFinished() throws RemoteException {
- if (!mFeatureFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
+ if (!KeyguardWmStateRefactor.isEnabled()) {
try {
finishedCallback.onAnimationFinished();
} catch (RemoteException e) {
@@ -3088,7 +3086,7 @@
// it will dismiss the panel in that case.
} else if (!mStatusBarStateController.leaveOpenOnKeyguardHide()
&& apps != null && apps.length > 0) {
- if (!mFeatureFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
+ if (!KeyguardWmStateRefactor.isEnabled()) {
// Handled in WmLockscreenVisibilityManager. Other logic in this class will
// short circuit when this is null.
mSurfaceBehindRemoteAnimationFinishedCallback = finishedCallback;
@@ -3112,7 +3110,7 @@
createInteractionJankMonitorConf(
CUJ_LOCKSCREEN_UNLOCK_ANIMATION, "RemoteAnimationDisabled"));
- if (!mFeatureFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
+ if (!KeyguardWmStateRefactor.isEnabled()) {
// Handled directly in StatusBarKeyguardViewManager if enabled.
mKeyguardViewControllerLazy.get().hide(startTime, fadeoutDuration);
}
@@ -3131,7 +3129,7 @@
Slog.e(TAG, "Keyguard exit without a corresponding app to show.");
try {
- if (!mFeatureFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
+ if (!KeyguardWmStateRefactor.isEnabled()) {
finishedCallback.onAnimationFinished();
}
} catch (RemoteException e) {
@@ -3163,7 +3161,7 @@
@Override
public void onAnimationEnd(Animator animation) {
try {
- if (!mFeatureFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
+ if (!KeyguardWmStateRefactor.isEnabled()) {
finishedCallback.onAnimationFinished();
}
} catch (RemoteException e) {
@@ -3176,7 +3174,7 @@
@Override
public void onAnimationCancel(Animator animation) {
try {
- if (!mFeatureFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
+ if (!KeyguardWmStateRefactor.isEnabled()) {
finishedCallback.onAnimationFinished();
}
} catch (RemoteException e) {
@@ -3341,7 +3339,7 @@
flags |= KEYGUARD_GOING_AWAY_FLAG_TO_LAUNCHER_CLEAR_SNAPSHOT;
}
- if (!mFeatureFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
+ if (!KeyguardWmStateRefactor.isEnabled()) {
// Handled in WmLockscreenVisibilityManager.
mActivityTaskManagerService.keyguardGoingAway(flags);
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardWmStateRefactor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardWmStateRefactor.kt
new file mode 100644
index 0000000..ddccc5d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardWmStateRefactor.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard
+
+import com.android.systemui.Flags
+import com.android.systemui.flags.FlagToken
+import com.android.systemui.flags.RefactorFlagUtils
+
+/** Helper for reading or using the keyguard wm state refactor flag state. */
+@Suppress("NOTHING_TO_INLINE")
+object KeyguardWmStateRefactor {
+ /** The aconfig flag name */
+ const val FLAG_NAME = Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR
+
+ /** A token used for dependency declaration */
+ val token: FlagToken
+ get() = FlagToken(FLAG_NAME, isEnabled)
+
+ /** Is the refactor enabled */
+ @JvmStatic
+ inline val isEnabled
+ get() = Flags.keyguardWmStateRefactor()
+
+ /**
+ * Called to ensure code is only run when the flag is enabled. This protects users from the
+ * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
+ * build to ensure that the refactor author catches issues in testing.
+ */
+ @JvmStatic
+ inline fun isUnexpectedlyInLegacyMode() =
+ RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)
+
+ /**
+ * Called to ensure code is only run when the flag is disabled. This will throw an exception if
+ * the flag is enabled to ensure that the refactor author catches issues in testing.
+ */
+ @JvmStatic
+ inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
index 1437194..1c6056c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
@@ -101,7 +101,7 @@
* Whether the device is locked or unlocked right now. This is true when keyguard has been
* dismissed or can be dismissed by a swipe
*/
- val isKeyguardUnlocked: StateFlow<Boolean>
+ val isKeyguardDismissible: StateFlow<Boolean>
/**
* Observable for the signal that keyguard is about to go away.
@@ -388,7 +388,7 @@
}
.distinctUntilChanged()
- override val isKeyguardUnlocked: StateFlow<Boolean> =
+ override val isKeyguardDismissible: StateFlow<Boolean> =
conflatedCallbackFlow {
val callback =
object : KeyguardStateController.Callback {
@@ -396,7 +396,7 @@
trySendWithFailureLogging(
keyguardStateController.isUnlocked,
TAG,
- "updated isKeyguardUnlocked due to onUnlockedChanged"
+ "updated isKeyguardDismissible due to onUnlockedChanged"
)
}
@@ -404,7 +404,7 @@
trySendWithFailureLogging(
keyguardStateController.isUnlocked,
TAG,
- "updated isKeyguardUnlocked due to onKeyguardShowingChanged"
+ "updated isKeyguardDismissible due to onKeyguardShowingChanged"
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index 8b2b45f..3965648 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -23,7 +23,7 @@
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.KeyguardWmStateRefactor
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardSurfaceBehindModel
@@ -230,7 +230,7 @@
combine(
startedKeyguardTransitionStep,
keyguardInteractor.statusBarState,
- keyguardInteractor.isKeyguardUnlocked,
+ keyguardInteractor.isKeyguardDismissible,
::Triple
),
::toQuad
@@ -307,7 +307,7 @@
}
private fun listenForLockscreenToGone() {
- if (flags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
+ if (KeyguardWmStateRefactor.isEnabled) {
return
}
@@ -324,7 +324,7 @@
}
private fun listenForLockscreenToGoneDragging() {
- if (flags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
+ if (KeyguardWmStateRefactor.isEnabled) {
return
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
index 33b6373..acbd9fb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
@@ -23,7 +23,7 @@
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.KeyguardWmStateRefactor
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardSurfaceBehindModel
@@ -217,7 +217,7 @@
}
private fun listenForPrimaryBouncerToGone() {
- if (flags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
+ if (KeyguardWmStateRefactor.isEnabled) {
// This is handled in KeyguardSecurityContainerController and
// StatusBarKeyguardViewManager, which calls the transition interactor to kick off a
// transition vs. listening to legacy state flags.
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index 36bd905..22d11d0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -32,6 +32,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
+import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
import com.android.systemui.keyguard.shared.model.CameraLaunchSourceModel
import com.android.systemui.keyguard.shared.model.DozeStateModel
import com.android.systemui.keyguard.shared.model.DozeStateModel.Companion.isDozeOff
@@ -162,8 +163,8 @@
/** Whether the keyguard is showing or not. */
val isKeyguardShowing: Flow<Boolean> = repository.isKeyguardShowing
- /** Whether the keyguard is unlocked or not. */
- val isKeyguardUnlocked: Flow<Boolean> = repository.isKeyguardUnlocked
+ /** Whether the keyguard is dismissible or not. */
+ val isKeyguardDismissible: Flow<Boolean> = repository.isKeyguardDismissible
/** Whether the keyguard is occluded (covered by an activity). */
val isKeyguardOccluded: Flow<Boolean> = repository.isKeyguardOccluded
@@ -194,6 +195,9 @@
/** Observable for the [StatusBarState] */
val statusBarState: Flow<StatusBarState> = repository.statusBarState
+ /** Source of the most recent biometric unlock, such as fingerprint or face. */
+ val biometricUnlockSource: Flow<BiometricUnlockSource?> = repository.biometricUnlockSource
+
/**
* Observable for [BiometricUnlockModel] when biometrics like face or any fingerprint (rear,
* side, under display) is used to unlock the device.
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
index a02e8ac..703bb87 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
@@ -32,6 +32,7 @@
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.statusbar.VibratorHelper
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.launch
@@ -48,6 +49,7 @@
@SuppressLint("ClickableViewAccessibility")
@JvmStatic
fun bind(
+ applicationScope: CoroutineScope,
view: DeviceEntryIconView,
viewModel: DeviceEntryIconViewModel,
fgViewModel: DeviceEntryForegroundViewModel,
@@ -69,7 +71,7 @@
view,
HapticFeedbackConstants.CONFIRM,
)
- viewModel.onLongPress()
+ applicationScope.launch { viewModel.onLongPress() }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
index 0bf9ad0..3fc9b42 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
@@ -31,6 +31,7 @@
import com.android.keyguard.LockIconViewController
import com.android.systemui.Flags.keyguardBottomAreaRefactor
import com.android.systemui.biometrics.AuthController
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
@@ -46,6 +47,7 @@
import com.android.systemui.statusbar.VibratorHelper
import dagger.Lazy
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
/** Includes the device entry icon. */
@@ -53,6 +55,7 @@
class DefaultDeviceEntrySection
@Inject
constructor(
+ @Application private val applicationScope: CoroutineScope,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
private val authController: AuthController,
private val windowManager: WindowManager,
@@ -91,6 +94,7 @@
if (DeviceEntryUdfpsRefactor.isEnabled) {
constraintLayout.findViewById<DeviceEntryIconView?>(deviceEntryIconViewId)?.let {
DeviceEntryIconViewBinder.bind(
+ applicationScope,
it,
deviceEntryIconViewModel.get(),
deviceEntryForegroundViewModel.get(),
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
index eacaa40..a3d5453 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
@@ -20,6 +20,7 @@
import android.animation.IntEvaluator
import com.android.keyguard.KeyguardViewController
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntrySourceInteractor
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
@@ -56,6 +57,7 @@
private val sceneContainerFlags: SceneContainerFlags,
private val keyguardViewController: Lazy<KeyguardViewController>,
private val deviceEntryInteractor: DeviceEntryInteractor,
+ private val deviceEntrySourceInteractor: DeviceEntrySourceInteractor,
) {
private val intEvaluator = IntEvaluator()
private val floatEvaluator = FloatEvaluator()
@@ -208,14 +210,13 @@
}
}
- fun onLongPress() {
- // TODO (b/309804148): play auth ripple via an interactor
-
+ suspend fun onLongPress() {
if (sceneContainerFlags.isEnabled()) {
deviceEntryInteractor.attemptDeviceEntry()
} else {
keyguardViewController.get().showPrimaryBouncer(/* scrim */ true)
}
+ deviceEntrySourceInteractor.attemptEnterDeviceFromDeviceEntryIcon()
}
private fun DeviceEntryIconView.IconType.toAccessibilityHintType():
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
index fa03dc2..93a6eee 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
@@ -34,6 +34,8 @@
import androidx.core.os.postDelayed
import androidx.core.view.isVisible
import androidx.dynamicanimation.animation.DynamicAnimation
+import com.android.internal.jank.Cuj.CUJ_BACK_PANEL_ARROW
+import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.util.LatencyTracker
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.NavigationEdgeBackPlugin
@@ -86,6 +88,7 @@
private val vibratorHelper: VibratorHelper,
private val configurationController: ConfigurationController,
private val latencyTracker: LatencyTracker,
+ private val interactionJankMonitor: InteractionJankMonitor,
) : ViewController<BackPanel>(BackPanel(context, latencyTracker)), NavigationEdgeBackPlugin {
/**
@@ -103,6 +106,7 @@
private val vibratorHelper: VibratorHelper,
private val configurationController: ConfigurationController,
private val latencyTracker: LatencyTracker,
+ private val interactionJankMonitor: InteractionJankMonitor,
) {
/** Construct a [BackPanelController]. */
fun create(context: Context): BackPanelController {
@@ -115,6 +119,7 @@
vibratorHelper,
configurationController,
latencyTracker,
+ interactionJankMonitor
)
backPanelController.init()
return backPanelController
@@ -183,7 +188,7 @@
/* Arrow is animating in */
ENTRY,
- /* could be entry, neutral, or stretched, releasing will commit back */
+ /* releasing will commit back */
ACTIVE,
/* releasing will cancel back */
@@ -366,6 +371,7 @@
// Receiving a CANCEL implies that something else intercepted
// the gesture, i.e., the user did not cancel their gesture.
// Therefore, disappear immediately, with minimum fanfare.
+ interactionJankMonitor.cancel(CUJ_BACK_PANEL_ARROW)
updateArrowState(GestureState.GONE)
velocityTracker = null
}
@@ -813,7 +819,7 @@
scale =
when (currentState) {
GestureState.ACTIVE,
- GestureState.FLUNG, -> params.activeIndicator.scale
+ GestureState.FLUNG -> params.activeIndicator.scale
GestureState.COMMITTED -> params.committedIndicator.scale
else -> params.preThresholdIndicator.scale
},
@@ -877,6 +883,16 @@
previousState = currentState
currentState = newState
+ // First, update the jank tracker
+ when (currentState) {
+ GestureState.ENTRY -> {
+ interactionJankMonitor.cancel(CUJ_BACK_PANEL_ARROW)
+ interactionJankMonitor.begin(mView, CUJ_BACK_PANEL_ARROW)
+ }
+ GestureState.GONE -> interactionJankMonitor.end(CUJ_BACK_PANEL_ARROW)
+ else -> {}
+ }
+
when (currentState) {
GestureState.CANCELLED -> {
backCallback.cancelBack()
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepository.kt
index bcd09bd..dc39c97 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepository.kt
@@ -25,6 +25,7 @@
import android.content.pm.PackageManager.ResolveInfoFlags
import android.os.UserHandle
import android.service.quicksettings.TileService
+import androidx.annotation.GuardedBy
import com.android.systemui.common.data.repository.PackageChangeRepository
import com.android.systemui.common.data.shared.model.PackageChangeModel
import com.android.systemui.dagger.SysUISingleton
@@ -32,12 +33,13 @@
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.util.kotlin.isComponentActuallyEnabled
import javax.inject.Inject
-import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.flow.shareIn
interface InstalledTilesComponentRepository {
@@ -49,33 +51,39 @@
@Inject
constructor(
@Application private val applicationContext: Context,
- @Background private val backgroundDispatcher: CoroutineDispatcher,
+ @Background private val backgroundScope: CoroutineScope,
private val packageChangeRepository: PackageChangeRepository
) : InstalledTilesComponentRepository {
- override fun getInstalledTilesComponents(userId: Int): Flow<Set<ComponentName>> {
- /*
- * In order to query [PackageManager] for different users, this implementation will call
- * [Context.createContextAsUser] and retrieve the [PackageManager] from that context.
- */
- val packageManager =
- if (applicationContext.userId == userId) {
- applicationContext.packageManager
- } else {
- applicationContext
- .createContextAsUser(
- UserHandle.of(userId),
- /* flags */ 0,
- )
- .packageManager
+ @GuardedBy("userMap") private val userMap = mutableMapOf<Int, Flow<Set<ComponentName>>>()
+
+ override fun getInstalledTilesComponents(userId: Int): Flow<Set<ComponentName>> =
+ synchronized(userMap) {
+ userMap.getOrPut(userId) {
+ /*
+ * In order to query [PackageManager] for different users, this implementation will
+ * call [Context.createContextAsUser] and retrieve the [PackageManager] from that
+ * context.
+ */
+ val packageManager =
+ if (applicationContext.userId == userId) {
+ applicationContext.packageManager
+ } else {
+ applicationContext
+ .createContextAsUser(
+ UserHandle.of(userId),
+ /* flags */ 0,
+ )
+ .packageManager
+ }
+ packageChangeRepository
+ .packageChanged(UserHandle.of(userId))
+ .onStart { emit(PackageChangeModel.Empty) }
+ .map { reloadComponents(userId, packageManager) }
+ .distinctUntilChanged()
+ .shareIn(backgroundScope, SharingStarted.WhileSubscribed(), replay = 1)
}
- return packageChangeRepository
- .packageChanged(UserHandle.of(userId))
- .onStart { emit(PackageChangeModel.Empty) }
- .map { reloadComponents(userId, packageManager) }
- .distinctUntilChanged()
- .flowOn(backgroundDispatcher)
- }
+ }
@WorkerThread
private fun reloadComponents(userId: Int, packageManager: PackageManager): Set<ComponentName> {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
index d04e4f5..53f287b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
@@ -33,11 +33,13 @@
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
+import androidx.annotation.VisibleForTesting;
+
import com.android.settingslib.Utils;
-import com.android.systemui.res.R;
import com.android.systemui.plugins.qs.QSIconView;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTile.State;
+import com.android.systemui.res.R;
import java.util.Objects;
@@ -52,7 +54,10 @@
private boolean mDisabledByPolicy = false;
private int mTint;
@Nullable
- private QSTile.Icon mLastIcon;
+ @VisibleForTesting
+ QSTile.Icon mLastIcon;
+
+ private boolean mIconChangeScheduled;
private ValueAnimator mColorAnimator = new ValueAnimator();
@@ -112,6 +117,7 @@
}
protected void updateIcon(ImageView iv, State state, boolean allowAnimations) {
+ mIconChangeScheduled = false;
final QSTile.Icon icon = state.iconSupplier != null ? state.iconSupplier.get() : state.icon;
if (!Objects.equals(icon, iv.getTag(R.id.qs_icon_tag))) {
boolean shouldAnimate = allowAnimations && shouldAnimate(iv);
@@ -167,7 +173,12 @@
mState = state.state;
mDisabledByPolicy = state.disabledByPolicy;
if (mTint != 0 && allowAnimations && shouldAnimate(iv)) {
- animateGrayScale(mTint, color, iv, () -> updateIcon(iv, state, allowAnimations));
+ mIconChangeScheduled = true;
+ animateGrayScale(mTint, color, iv, () -> {
+ if (mIconChangeScheduled) {
+ updateIcon(iv, state, allowAnimations);
+ }
+ });
} else {
setTint(iv, color);
updateIcon(iv, state, allowAnimations);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index cc53aab..a9dd25b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -85,8 +85,8 @@
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.keyguard.KeyguardUnlockAnimationController;
+import com.android.systemui.keyguard.KeyguardWmStateRefactor;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.keyguard.ui.view.InWindowLauncherUnlockAnimationManager;
import com.android.systemui.model.SysUiState;
@@ -616,7 +616,7 @@
mDisplayTracker = displayTracker;
mUnfoldTransitionProgressForwarder = unfoldTransitionProgressForwarder;
- if (!featureFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
+ if (!KeyguardWmStateRefactor.isEnabled()) {
mSysuiUnlockAnimationController = sysuiUnlockAnimationController;
} else {
mSysuiUnlockAnimationController = inWindowLauncherUnlockAnimationManager;
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index c0ceba3..530c124 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -3576,11 +3576,6 @@
}
@Override
- public NotificationStackScrollLayoutController getNotificationStackScrollLayoutController() {
- return mNotificationStackScrollLayoutController;
- }
-
- @Override
public void disableHeader(int state1, int state2, boolean animated) {
mShadeHeaderController.disable(state1, state2, animated);
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt
index e54286f..4f970b3 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt
@@ -17,7 +17,6 @@
import android.view.ViewPropertyAnimator
import com.android.systemui.statusbar.GestureRecorder
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.statusbar.policy.HeadsUpManager
@@ -44,9 +43,6 @@
/** Animates the view from its current alpha to zero then runs the runnable. */
fun fadeOut(startDelayMs: Long, durationMs: Long, endAction: Runnable): ViewPropertyAnimator
- /** Returns the NSSL controller. */
- val notificationStackScrollLayoutController: NotificationStackScrollLayoutController
-
/** Set whether the bouncer is showing. */
fun setBouncerShowing(bouncerShowing: Boolean)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 04db653..dd04531 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -4917,30 +4917,12 @@
public void removeContainerView(View v) {
Assert.isMainThread();
removeView(v);
- if (!FooterViewRefactor.isEnabled()) {
- // A notification was removed, and we're not currently showing the empty shade view.
- if (v instanceof ExpandableNotificationRow && !mController.isShowingEmptyShadeView()) {
- mController.updateShowEmptyShadeView();
- updateFooter();
- mController.updateImportantForAccessibility();
- }
- }
-
updateSpeedBumpIndex();
}
public void addContainerView(View v) {
Assert.isMainThread();
addView(v);
- if (!FooterViewRefactor.isEnabled()) {
- // A notification was added, and we're currently showing the empty shade view.
- if (v instanceof ExpandableNotificationRow && mController.isShowingEmptyShadeView()) {
- mController.updateShowEmptyShadeView();
- updateFooter();
- mController.updateImportantForAccessibility();
- }
- }
-
updateSpeedBumpIndex();
}
@@ -4948,14 +4930,6 @@
Assert.isMainThread();
ensureRemovedFromTransientContainer(v);
addView(v, index);
- // A notification was added, and we're currently showing the empty shade view.
- if (!FooterViewRefactor.isEnabled() && v instanceof ExpandableNotificationRow
- && mController.isShowingEmptyShadeView()) {
- mController.updateShowEmptyShadeView();
- updateFooter();
- mController.updateImportantForAccessibility();
- }
-
updateSpeedBumpIndex();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 1143481..49fde39 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -2137,6 +2137,7 @@
if (!FooterViewRefactor.isEnabled()) {
updateShowEmptyShadeView();
+ updateImportantForAccessibility();
}
}
}
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 266c19c..7952511 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -2587,8 +2587,7 @@
// So if AOD is off or unsupported we need to trigger these updates at screen on
// when the keyguard is occluded.
mLockscreenUserManager.updatePublicMode();
- mShadeSurface.getNotificationStackScrollLayoutController()
- .updateSensitivenessForOccludedWakeup();
+ mStackScrollerController.updateSensitivenessForOccludedWakeup();
}
if (mLaunchCameraWhenFinishedWaking) {
mCameraLauncherLazy.get().launchCamera(mLastCameraLaunchSource,
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 88347ab..4c83ca2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -69,6 +69,7 @@
import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
+import com.android.systemui.keyguard.KeyguardWmStateRefactor;
import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.keyguard.domain.interactor.WindowManagerLockscreenVisibilityInteractor;
@@ -474,7 +475,7 @@
mIsDocked = mDockManager.isDocked();
}
- if (mFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
+ if (KeyguardWmStateRefactor.isEnabled()) {
// Show the keyguard views whenever we've told WM that the lockscreen is visible.
mShadeViewController.postToView(() ->
collectFlow(
@@ -1428,7 +1429,7 @@
executeAfterKeyguardGoneAction();
}
- if (mFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
+ if (KeyguardWmStateRefactor.isEnabled()) {
mKeyguardTransitionInteractor.startDismissKeyguardTransition();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/BooleanFlowOperators.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/BooleanFlowOperators.kt
new file mode 100644
index 0000000..693a835
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/BooleanFlowOperators.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.kotlin
+
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.map
+
+object BooleanFlowOperators {
+ /**
+ * Logical AND operator for boolean flows. Will collect all flows and [combine] them to
+ * determine the result.
+ *
+ * Usage:
+ * ```
+ * val result = and(flow1, flow2)
+ * ```
+ */
+ fun and(vararg flows: Flow<Boolean>): Flow<Boolean> =
+ combine(flows.asIterable()) { values -> values.all { it } }
+
+ /**
+ * Logical NOT operator for a boolean flow.
+ *
+ * Usage:
+ * ```
+ * val negatedFlow = not(flow)
+ * ```
+ */
+ fun not(flow: Flow<Boolean>) = flow.map { !it }
+
+ /**
+ * Logical OR operator for a boolean flow. Will collect all flows and [combine] them to
+ * determine the result.
+ */
+ fun or(vararg flows: Flow<Boolean>): Flow<Boolean> =
+ combine(flows.asIterable()) { values -> values.any { it } }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/Dagger.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/Dagger.kt
index c587f2e..5150389 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/Dagger.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/Dagger.kt
@@ -17,6 +17,7 @@
package com.android.systemui.util.kotlin
import dagger.Lazy
+import java.util.Optional
import kotlin.reflect.KProperty
/**
@@ -30,3 +31,16 @@
* ```
*/
operator fun <T> Lazy<T>.getValue(thisRef: Any?, property: KProperty<*>): T = get()
+
+/**
+ * Extension operator that allows developers to use [java.util.Optional] as a nullable property
+ * delegate:
+ * ```kotlin
+ * class MyClass @Inject constructor(
+ * optionalDependency: Optional<Foo>,
+ * ) {
+ * val dependency: Foo? by optionalDependency
+ * }
+ * ```
+ */
+operator fun <T> Optional<T>.getValue(thisRef: Any?, property: KProperty<*>): T? = getOrNull()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java
index 9bcab57..9087816 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java
@@ -16,10 +16,12 @@
package com.android.systemui.accessibility.floatingmenu;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
+import android.annotation.NonNull;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.WindowManager;
@@ -27,10 +29,12 @@
import androidx.test.filters.SmallTest;
+import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.accessibility.utils.TestUtils;
import com.android.systemui.util.settings.SecureSettings;
-import com.android.wm.shell.bubbles.DismissViewUtils;
import com.android.wm.shell.common.bubbles.DismissView;
+import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
import org.junit.Before;
import org.junit.Rule;
@@ -46,6 +50,7 @@
@TestableLooper.RunWithLooper
public class DragToInteractAnimationControllerTest extends SysuiTestCase {
private DragToInteractAnimationController mDragToInteractAnimationController;
+ private DragToInteractView mInteractView;
private DismissView mDismissView;
@Rule
@@ -57,29 +62,72 @@
@Before
public void setUp() throws Exception {
final WindowManager stubWindowManager = mContext.getSystemService(WindowManager.class);
+ final SecureSettings mockSecureSettings = TestUtils.mockSecureSettings();
final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mAccessibilityManager,
- mock(SecureSettings.class));
+ mockSecureSettings);
final MenuViewAppearance stubMenuViewAppearance = new MenuViewAppearance(mContext,
stubWindowManager);
- final MenuView stubMenuView = new MenuView(mContext, stubMenuViewModel,
- stubMenuViewAppearance);
+ final MenuView stubMenuView = spy(new MenuView(mContext, stubMenuViewModel,
+ stubMenuViewAppearance, mockSecureSettings));
+ mInteractView = spy(new DragToInteractView(mContext));
mDismissView = spy(new DismissView(mContext));
- DismissViewUtils.setup(mDismissView);
- mDragToInteractAnimationController = new DragToInteractAnimationController(
- mDismissView, stubMenuView);
+
+ if (Flags.floatingMenuDragToEdit()) {
+ mDragToInteractAnimationController = new DragToInteractAnimationController(
+ mInteractView, stubMenuView);
+ } else {
+ mDragToInteractAnimationController = new DragToInteractAnimationController(
+ mDismissView, stubMenuView);
+ }
+
+ mDragToInteractAnimationController.setMagnetListener(new MagnetizedObject.MagnetListener() {
+ @Override
+ public void onStuckToTarget(@NonNull MagnetizedObject.MagneticTarget target) {
+
+ }
+
+ @Override
+ public void onUnstuckFromTarget(@NonNull MagnetizedObject.MagneticTarget target,
+ float velX, float velY, boolean wasFlungOut) {
+
+ }
+
+ @Override
+ public void onReleasedInTarget(@NonNull MagnetizedObject.MagneticTarget target) {
+
+ }
+ });
}
@Test
- public void showDismissView_success() {
- mDragToInteractAnimationController.showDismissView(true);
+ @DisableFlags(Flags.FLAG_FLOATING_MENU_DRAG_TO_EDIT)
+ public void showDismissView_success_old() {
+ mDragToInteractAnimationController.showInteractView(true);
verify(mDismissView).show();
}
@Test
- public void hideDismissView_success() {
- mDragToInteractAnimationController.showDismissView(false);
+ @DisableFlags(Flags.FLAG_FLOATING_MENU_DRAG_TO_EDIT)
+ public void hideDismissView_success_old() {
+ mDragToInteractAnimationController.showInteractView(false);
verify(mDismissView).hide();
}
+
+ @Test
+ @EnableFlags(Flags.FLAG_FLOATING_MENU_DRAG_TO_EDIT)
+ public void showDismissView_success() {
+ mDragToInteractAnimationController.showInteractView(true);
+
+ verify(mInteractView).show();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_FLOATING_MENU_DRAG_TO_EDIT)
+ public void hideDismissView_success() {
+ mDragToInteractAnimationController.showInteractView(false);
+
+ verify(mInteractView).hide();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java
index 215f93d..e0df1e0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java
@@ -17,6 +17,7 @@
package com.android.systemui.accessibility.floatingmenu;
import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -42,6 +43,7 @@
import com.android.systemui.Flags;
import com.android.systemui.Prefs;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.accessibility.utils.TestUtils;
import com.android.systemui.util.settings.SecureSettings;
import org.junit.After;
@@ -79,10 +81,12 @@
final WindowManager stubWindowManager = mContext.getSystemService(WindowManager.class);
final MenuViewAppearance stubMenuViewAppearance = new MenuViewAppearance(mContext,
stubWindowManager);
+ final SecureSettings secureSettings = TestUtils.mockSecureSettings();
final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mAccessibilityManager,
- mock(SecureSettings.class));
+ secureSettings);
- mMenuView = spy(new MenuView(mContext, stubMenuViewModel, stubMenuViewAppearance));
+ mMenuView = spy(new MenuView(mContext, stubMenuViewModel, stubMenuViewAppearance,
+ secureSettings));
mViewPropertyAnimator = spy(mMenuView.animate());
doReturn(mViewPropertyAnimator).when(mMenuView).animate();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
index 9c8de30..c2ed7d4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
@@ -22,10 +22,13 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import android.graphics.Rect;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.WindowManager;
@@ -37,7 +40,9 @@
import androidx.recyclerview.widget.RecyclerViewAccessibilityDelegate;
import androidx.test.filters.SmallTest;
+import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.accessibility.utils.TestUtils;
import com.android.systemui.res.R;
import com.android.systemui.util.settings.SecureSettings;
@@ -49,6 +54,8 @@
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
+import java.util.concurrent.atomic.AtomicBoolean;
+
/** Tests for {@link MenuItemAccessibilityDelegate}. */
@SmallTest
@TestableLooper.RunWithLooper
@@ -59,17 +66,16 @@
@Mock
private AccessibilityManager mAccessibilityManager;
- @Mock
- private SecureSettings mSecureSettings;
- @Mock
- private DragToInteractAnimationController.DismissCallback mStubDismissCallback;
-
+ private final SecureSettings mSecureSettings = TestUtils.mockSecureSettings();
private RecyclerView mStubListView;
private MenuView mMenuView;
+ private MenuViewLayer mMenuViewLayer;
private MenuItemAccessibilityDelegate mMenuItemAccessibilityDelegate;
private MenuAnimationController mMenuAnimationController;
private final Rect mDraggableBounds = new Rect(100, 200, 300, 400);
+ private final AtomicBoolean mEditReceived = new AtomicBoolean(false);
+
@Before
public void setUp() {
final WindowManager stubWindowManager = mContext.getSystemService(WindowManager.class);
@@ -80,20 +86,28 @@
final int halfScreenHeight =
stubWindowManager.getCurrentWindowMetrics().getBounds().height() / 2;
- mMenuView = spy(new MenuView(mContext, stubMenuViewModel, stubMenuViewAppearance));
+ mMenuView = spy(new MenuView(mContext, stubMenuViewModel, stubMenuViewAppearance,
+ mSecureSettings));
mMenuView.setTranslationY(halfScreenHeight);
+ mMenuViewLayer = spy(new MenuViewLayer(
+ mContext, stubWindowManager, mAccessibilityManager,
+ stubMenuViewModel, stubMenuViewAppearance, mMenuView,
+ mock(IAccessibilityFloatingMenu.class), mSecureSettings));
+
doReturn(mDraggableBounds).when(mMenuView).getMenuDraggableBounds();
mStubListView = new RecyclerView(mContext);
mMenuAnimationController = spy(new MenuAnimationController(mMenuView,
stubMenuViewAppearance));
mMenuItemAccessibilityDelegate =
new MenuItemAccessibilityDelegate(new RecyclerViewAccessibilityDelegate(
- mStubListView), mMenuAnimationController);
+ mStubListView), mMenuAnimationController, mMenuViewLayer);
+ mEditReceived.set(false);
}
@Test
- public void getAccessibilityActionList_matchSize() {
+ @DisableFlags(Flags.FLAG_FLOATING_MENU_DRAG_TO_EDIT)
+ public void getAccessibilityActionList_matchSize_withoutEdit() {
final AccessibilityNodeInfoCompat info =
new AccessibilityNodeInfoCompat(new AccessibilityNodeInfo());
@@ -103,6 +117,17 @@
}
@Test
+ @EnableFlags(Flags.FLAG_FLOATING_MENU_DRAG_TO_EDIT)
+ public void getAccessibilityActionList_matchSize() {
+ final AccessibilityNodeInfoCompat info =
+ new AccessibilityNodeInfoCompat(new AccessibilityNodeInfo());
+
+ mMenuItemAccessibilityDelegate.onInitializeAccessibilityNodeInfo(mStubListView, info);
+
+ assertThat(info.getActionList().size()).isEqualTo(7);
+ }
+
+ @Test
public void performMoveTopLeftAction_matchPosition() {
final boolean moveTopLeftAction =
mMenuItemAccessibilityDelegate.performAccessibilityAction(mStubListView,
@@ -169,13 +194,22 @@
@Test
public void performRemoveMenuAction_success() {
- mMenuAnimationController.setDismissCallback(mStubDismissCallback);
final boolean removeMenuAction =
mMenuItemAccessibilityDelegate.performAccessibilityAction(mStubListView,
R.id.action_remove_menu, null);
assertThat(removeMenuAction).isTrue();
- verify(mMenuAnimationController).removeMenu();
+ verify(mMenuViewLayer).dispatchAccessibilityAction(R.id.action_remove_menu);
+ }
+
+ @Test
+ public void performEditAction_success() {
+ final boolean editAction =
+ mMenuItemAccessibilityDelegate.performAccessibilityAction(mStubListView,
+ R.id.action_edit, null);
+
+ assertThat(editAction).isTrue();
+ verify(mMenuViewLayer).dispatchAccessibilityAction(R.id.action_edit);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
index e1522f5..9e8c6b3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
@@ -16,6 +16,7 @@
package com.android.systemui.accessibility.floatingmenu;
+import static android.R.id.empty;
import static android.view.View.OVER_SCROLL_NEVER;
import static com.google.common.truth.Truth.assertThat;
@@ -27,6 +28,8 @@
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.MotionEvent;
@@ -38,10 +41,11 @@
import androidx.test.filters.SmallTest;
import com.android.internal.accessibility.dialog.AccessibilityTarget;
+import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.accessibility.MotionEventHelper;
+import com.android.systemui.accessibility.utils.TestUtils;
import com.android.systemui.util.settings.SecureSettings;
-import com.android.wm.shell.bubbles.DismissViewUtils;
import com.android.wm.shell.common.bubbles.DismissView;
import org.junit.After;
@@ -71,6 +75,7 @@
private DragToInteractAnimationController mDragToInteractAnimationController;
private RecyclerView mStubListView;
private DismissView mDismissView;
+ private DragToInteractView mInteractView;
@Rule
public MockitoRule mockito = MockitoJUnit.rule();
@@ -81,19 +86,28 @@
@Before
public void setUp() throws Exception {
final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
+ final SecureSettings secureSettings = TestUtils.mockSecureSettings();
final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mAccessibilityManager,
- mock(SecureSettings.class));
+ secureSettings);
final MenuViewAppearance stubMenuViewAppearance = new MenuViewAppearance(mContext,
windowManager);
- mStubMenuView = new MenuView(mContext, stubMenuViewModel, stubMenuViewAppearance);
+ mStubMenuView = new MenuView(mContext, stubMenuViewModel, stubMenuViewAppearance,
+ secureSettings);
mStubMenuView.setTranslationX(0);
mStubMenuView.setTranslationY(0);
mMenuAnimationController = spy(new MenuAnimationController(
mStubMenuView, stubMenuViewAppearance));
+ mInteractView = spy(new DragToInteractView(mContext));
mDismissView = spy(new DismissView(mContext));
- DismissViewUtils.setup(mDismissView);
- mDragToInteractAnimationController =
- spy(new DragToInteractAnimationController(mDismissView, mStubMenuView));
+
+ if (Flags.floatingMenuDragToEdit()) {
+ mDragToInteractAnimationController = spy(new DragToInteractAnimationController(
+ mInteractView, mStubMenuView));
+ } else {
+ mDragToInteractAnimationController = spy(new DragToInteractAnimationController(
+ mDismissView, mStubMenuView));
+ }
+
mTouchHandler = new MenuListViewTouchHandler(mMenuAnimationController,
mDragToInteractAnimationController);
final AccessibilityTargetAdapter stubAdapter = new AccessibilityTargetAdapter(mStubTargets);
@@ -115,7 +129,7 @@
@Test
public void onActionMoveEvent_notConsumedEvent_shouldMoveToPosition() {
- doReturn(false).when(mDragToInteractAnimationController).maybeConsumeMoveMotionEvent(
+ doReturn(empty).when(mDragToInteractAnimationController).maybeConsumeMoveMotionEvent(
any(MotionEvent.class));
final int offset = 100;
final MotionEvent stubDownEvent =
@@ -136,6 +150,7 @@
}
@Test
+ @DisableFlags(Flags.FLAG_FLOATING_MENU_DRAG_TO_EDIT)
public void onActionMoveEvent_shouldShowDismissView() {
final int offset = 100;
final MotionEvent stubDownEvent =
@@ -154,6 +169,25 @@
}
@Test
+ @EnableFlags(Flags.FLAG_FLOATING_MENU_DRAG_TO_EDIT)
+ public void onActionMoveEvent_shouldShowInteractView() {
+ final int offset = 100;
+ final MotionEvent stubDownEvent =
+ mMotionEventHelper.obtainMotionEvent(/* downTime= */ 0, /* eventTime= */ 1,
+ MotionEvent.ACTION_DOWN, mStubMenuView.getTranslationX(),
+ mStubMenuView.getTranslationY());
+ final MotionEvent stubMoveEvent =
+ mMotionEventHelper.obtainMotionEvent(/* downTime= */ 0, /* eventTime= */ 3,
+ MotionEvent.ACTION_MOVE, mStubMenuView.getTranslationX() + offset,
+ mStubMenuView.getTranslationY() + offset);
+
+ mTouchHandler.onInterceptTouchEvent(mStubListView, stubDownEvent);
+ mTouchHandler.onInterceptTouchEvent(mStubListView, stubMoveEvent);
+
+ verify(mInteractView).show();
+ }
+
+ @Test
public void dragAndDrop_shouldFlingMenuThenSpringToEdge() {
final int offset = 100;
final MotionEvent stubDownEvent =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
index bc9a0a5..4a1bdbc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
@@ -30,6 +30,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
@@ -72,6 +73,8 @@
import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.SysuiTestableContext;
+import com.android.systemui.accessibility.utils.TestUtils;
+import com.android.systemui.res.R;
import com.android.systemui.util.settings.SecureSettings;
import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
@@ -81,6 +84,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatcher;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnit;
@@ -122,18 +126,17 @@
private SysuiTestableContext mSpyContext = getContext();
@Mock
private IAccessibilityFloatingMenu mFloatingMenu;
-
- @Mock
- private SecureSettings mSecureSettings;
-
@Mock
private WindowManager mStubWindowManager;
-
@Mock
private AccessibilityManager mStubAccessibilityManager;
+ private final SecureSettings mSecureSettings = TestUtils.mockSecureSettings();
private final NotificationManager mMockNotificationManager = mock(NotificationManager.class);
+ private final ArgumentMatcher<IntentFilter> mNotificationMatcher =
+ (arg) -> arg.hasAction(ACTION_UNDO) && arg.hasAction(ACTION_DELETE);
+
@Before
public void setUp() throws Exception {
mSpyContext.addMockSystemService(Context.NOTIFICATION_SERVICE, mMockNotificationManager);
@@ -145,8 +148,16 @@
new WindowMetrics(mDisplayBounds, fakeDisplayInsets(), /* density = */ 0.0f));
doReturn(mWindowMetrics).when(mStubWindowManager).getCurrentWindowMetrics();
- mMenuViewLayer = new MenuViewLayer(mSpyContext, mStubWindowManager,
- mStubAccessibilityManager, mFloatingMenu, mSecureSettings);
+ MenuViewModel menuViewModel = new MenuViewModel(
+ mSpyContext, mStubAccessibilityManager, mSecureSettings);
+ MenuViewAppearance menuViewAppearance = new MenuViewAppearance(
+ mSpyContext, mStubWindowManager);
+ mMenuView = spy(
+ new MenuView(mSpyContext, menuViewModel, menuViewAppearance, mSecureSettings));
+
+ mMenuViewLayer = spy(new MenuViewLayer(mSpyContext, mStubWindowManager,
+ mStubAccessibilityManager, menuViewModel, menuViewAppearance, mMenuView,
+ mFloatingMenu, mSecureSettings));
mMenuView = (MenuView) mMenuViewLayer.getChildAt(LayerIndex.MENU_VIEW);
mMenuAnimationController = mMenuView.getMenuAnimationController();
@@ -236,6 +247,27 @@
}
@Test
+ @EnableFlags(Flags.FLAG_FLOATING_MENU_DRAG_TO_EDIT)
+ public void onEditAction_gotoEditScreen_isCalled() {
+ mMenuViewLayer.dispatchAccessibilityAction(R.id.action_edit);
+ verify(mMenuView).gotoEditScreen();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_FLOATING_MENU_DRAG_TO_HIDE)
+ public void onDismissAction_hideMenuAndShowNotification() {
+ mMenuViewLayer.dispatchAccessibilityAction(R.id.action_remove_menu);
+ verify(mMenuViewLayer).hideMenuAndShowNotification();
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_FLOATING_MENU_DRAG_TO_HIDE)
+ public void onDismissAction_hideMenuAndShowMessage() {
+ mMenuViewLayer.dispatchAccessibilityAction(R.id.action_remove_menu);
+ verify(mMenuViewLayer).hideMenuAndShowMessage();
+ }
+
+ @Test
public void showingImeInsetsChange_notOverlapOnIme_menuKeepOriginalPosition() {
final float menuTop = STATUS_BAR_HEIGHT + 100;
mMenuAnimationController.moveAndPersistPosition(new PointF(0, menuTop));
@@ -307,19 +339,13 @@
@Test
@EnableFlags(Flags.FLAG_FLOATING_MENU_DRAG_TO_HIDE)
public void onReleasedInTarget_hideMenuAndShowNotificationWithExpectedActions() {
- dragMenuThenReleasedInTarget();
+ dragMenuThenReleasedInTarget(R.id.action_remove_menu);
verify(mMockNotificationManager).notify(
eq(SystemMessageProto.SystemMessage.NOTE_A11Y_FLOATING_MENU_HIDDEN),
any(Notification.class));
- ArgumentCaptor<IntentFilter> intentFilterCaptor = ArgumentCaptor.forClass(
- IntentFilter.class);
verify(mSpyContext).registerReceiver(
- any(BroadcastReceiver.class),
- intentFilterCaptor.capture(),
- anyInt());
- assertThat(intentFilterCaptor.getValue().matchAction(ACTION_UNDO)).isTrue();
- assertThat(intentFilterCaptor.getValue().matchAction(ACTION_DELETE)).isTrue();
+ any(BroadcastReceiver.class), argThat(mNotificationMatcher), anyInt());
}
@Test
@@ -327,10 +353,10 @@
public void receiveActionUndo_dismissNotificationAndMenuVisible() {
ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor = ArgumentCaptor.forClass(
BroadcastReceiver.class);
- dragMenuThenReleasedInTarget();
+ dragMenuThenReleasedInTarget(R.id.action_remove_menu);
verify(mSpyContext).registerReceiver(broadcastReceiverCaptor.capture(),
- any(IntentFilter.class), anyInt());
+ argThat(mNotificationMatcher), anyInt());
broadcastReceiverCaptor.getValue().onReceive(mSpyContext, new Intent(ACTION_UNDO));
verify(mSpyContext).unregisterReceiver(broadcastReceiverCaptor.getValue());
@@ -344,10 +370,10 @@
public void receiveActionDelete_dismissNotificationAndHideMenu() {
ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor = ArgumentCaptor.forClass(
BroadcastReceiver.class);
- dragMenuThenReleasedInTarget();
+ dragMenuThenReleasedInTarget(R.id.action_remove_menu);
verify(mSpyContext).registerReceiver(broadcastReceiverCaptor.capture(),
- any(IntentFilter.class), anyInt());
+ argThat(mNotificationMatcher), anyInt());
broadcastReceiverCaptor.getValue().onReceive(mSpyContext, new Intent(ACTION_DELETE));
verify(mSpyContext).unregisterReceiver(broadcastReceiverCaptor.getValue());
@@ -423,10 +449,12 @@
});
}
- private void dragMenuThenReleasedInTarget() {
+ private void dragMenuThenReleasedInTarget(int id) {
MagnetizedObject.MagnetListener magnetListener =
- mMenuViewLayer.getDragToInteractAnimationController().getMagnetListener();
+ mMenuViewLayer.getDragToInteractAnimationController().getMagnetListener(id);
+ View view = mock(View.class);
+ when(view.getId()).thenReturn(id);
magnetListener.onReleasedInTarget(
- new MagnetizedObject.MagneticTarget(mock(View.class), 200));
+ new MagnetizedObject.MagneticTarget(view, 200));
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
index 8da6cf9..7c97f53 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
@@ -17,15 +17,19 @@
package com.android.systemui.accessibility.floatingmenu;
import static android.app.UiModeManager.MODE_NIGHT_YES;
+
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Mockito.mock;
+
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import android.app.UiModeManager;
+import android.content.Intent;
import android.graphics.Rect;
import android.graphics.drawable.GradientDrawable;
import android.platform.test.annotations.EnableFlags;
+import android.provider.Settings;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.WindowManager;
@@ -36,6 +40,8 @@
import com.android.systemui.Flags;
import com.android.systemui.Prefs;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.SysuiTestableContext;
+import com.android.systemui.accessibility.utils.TestUtils;
import com.android.systemui.util.settings.SecureSettings;
import org.junit.After;
@@ -65,17 +71,23 @@
@Mock
private AccessibilityManager mAccessibilityManager;
+ private SysuiTestableContext mSpyContext;
+
@Before
public void setUp() throws Exception {
mUiModeManager = mContext.getSystemService(UiModeManager.class);
mNightMode = mUiModeManager.getNightMode();
mUiModeManager.setNightMode(MODE_NIGHT_YES);
+
+ mSpyContext = spy(mContext);
+ final SecureSettings secureSettings = TestUtils.mockSecureSettings();
final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mAccessibilityManager,
- mock(SecureSettings.class));
+ secureSettings);
final WindowManager stubWindowManager = mContext.getSystemService(WindowManager.class);
- mStubMenuViewAppearance = new MenuViewAppearance(mContext, stubWindowManager);
- mMenuView = spy(new MenuView(mContext, stubMenuViewModel, mStubMenuViewAppearance));
- mLastPosition = Prefs.getString(mContext,
+ mStubMenuViewAppearance = new MenuViewAppearance(mSpyContext, stubWindowManager);
+ mMenuView = spy(new MenuView(mSpyContext, stubMenuViewModel, mStubMenuViewAppearance,
+ secureSettings));
+ mLastPosition = Prefs.getString(mSpyContext,
Prefs.Key.ACCESSIBILITY_FLOATING_MENU_POSITION, /* defaultValue= */ null);
}
@@ -154,6 +166,25 @@
assertThat(radiiAnimator.isStarted()).isTrue();
}
+ @Test
+ public void getIntentForEditScreen_validate() {
+ Intent intent = mMenuView.getIntentForEditScreen();
+ String[] targets = intent.getBundleExtra(
+ ":settings:show_fragment_args").getStringArray("targets");
+
+ assertThat(intent.getAction()).isEqualTo(Settings.ACTION_ACCESSIBILITY_SHORTCUT_SETTINGS);
+ assertThat(targets).asList().containsExactlyElementsIn(TestUtils.TEST_BUTTON_TARGETS);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_FLOATING_MENU_DRAG_TO_EDIT)
+ public void gotoEditScreen_sendsIntent() {
+ // Notably, this shouldn't crash the settings app,
+ // because the button target args are configured.
+ mMenuView.gotoEditScreen();
+ verify(mSpyContext).startActivity(any());
+ }
+
private InstantInsetLayerDrawable getMenuViewInsetLayer() {
return (InstantInsetLayerDrawable) mMenuView.getBackground();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/utils/TestUtils.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/utils/TestUtils.java
index 10c8caa..8399fa8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/utils/TestUtils.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/utils/TestUtils.java
@@ -16,11 +16,27 @@
package com.android.systemui.accessibility.utils;
-import android.os.SystemClock;
+import static com.android.internal.accessibility.common.ShortcutConstants.SERVICES_SEPARATOR;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.provider.Settings;
+
+import com.android.systemui.util.settings.SecureSettings;
+
+import java.util.Set;
+import java.util.StringJoiner;
import java.util.function.BooleanSupplier;
public class TestUtils {
+ private static final ComponentName TEST_COMPONENT_A = new ComponentName("pkg", "A");
+ private static final ComponentName TEST_COMPONENT_B = new ComponentName("pkg", "B");
+ public static final String[] TEST_BUTTON_TARGETS = {
+ TEST_COMPONENT_A.flattenToString(), TEST_COMPONENT_B.flattenToString()};
public static long DEFAULT_CONDITION_DURATION = 5_000;
/**
@@ -55,4 +71,28 @@
SystemClock.sleep(sleepMs);
}
}
+
+ /**
+ * Returns a mock secure settings configured to return information needed for tests.
+ * Currently, this only includes button targets.
+ */
+ public static SecureSettings mockSecureSettings() {
+ SecureSettings secureSettings = mock(SecureSettings.class);
+
+ final String targets = getShortcutTargets(
+ Set.of(TEST_COMPONENT_A, TEST_COMPONENT_B));
+ when(secureSettings.getStringForUser(
+ Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
+ UserHandle.USER_CURRENT)).thenReturn(targets);
+
+ return secureSettings;
+ }
+
+ private static String getShortcutTargets(Set<ComponentName> components) {
+ final StringJoiner stringJoiner = new StringJoiner(String.valueOf(SERVICES_SEPARATOR));
+ for (ComponentName target : components) {
+ stringJoiner.add(target.flattenToString());
+ }
+ return stringJoiner.toString();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
index a47e288..7c03d78 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
@@ -27,12 +27,13 @@
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.keyguard.logging.KeyguardLogger
+import com.android.systemui.Flags
import com.android.systemui.Flags.FLAG_LIGHT_REVEAL_MIGRATION
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository
-import com.android.systemui.log.logcatLogBuffer
-import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.deviceentry.domain.interactor.AuthRippleInteractor
import com.android.systemui.keyguard.WakefulnessLifecycle
+import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.LightRevealScrim
import com.android.systemui.statusbar.NotificationShadeWindowController
@@ -42,7 +43,7 @@
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.leak.RotationUtils
import com.android.systemui.util.mockito.any
-import javax.inject.Provider
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import org.junit.After
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
@@ -61,8 +62,10 @@
import org.mockito.MockitoAnnotations
import org.mockito.MockitoSession
import org.mockito.quality.Strictness
+import javax.inject.Provider
+@ExperimentalCoroutinesApi
@SmallTest
@RunWith(AndroidTestingRunner::class)
class AuthRippleControllerTest : SysuiTestCase() {
@@ -74,6 +77,7 @@
@Mock private lateinit var configurationController: ConfigurationController
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@Mock private lateinit var authController: AuthController
+ @Mock private lateinit var authRippleInteractor: AuthRippleInteractor
@Mock private lateinit var keyguardStateController: KeyguardStateController
@Mock
private lateinit var wakefulnessLifecycle: WakefulnessLifecycle
@@ -88,8 +92,6 @@
@Mock
private lateinit var statusBarStateController: StatusBarStateController
@Mock
- private lateinit var featureFlags: FeatureFlags
- @Mock
private lateinit var lightRevealScrim: LightRevealScrim
@Mock
private lateinit var fpSensorProp: FingerprintSensorPropertiesInternal
@@ -103,6 +105,7 @@
@Before
fun setUp() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
MockitoAnnotations.initMocks(this)
staticMockSession = mockitoSession()
.mockStatic(RotationUtils::class.java)
@@ -128,6 +131,7 @@
KeyguardLogger(logcatLogBuffer(AuthRippleController.TAG)),
biometricUnlockController,
lightRevealScrim,
+ authRippleInteractor,
facePropertyRepository,
rippleView,
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorTest.kt
similarity index 95%
rename from packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsInteractorTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorTest.kt
index 0dfdeca..bdf0e06 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.deviceentry.data.repository
+package com.android.systemui.deviceentry.domain.interactor
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -23,12 +23,13 @@
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
import com.android.systemui.biometrics.shared.model.SensorStrength
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.deviceentry.domain.interactor.deviceEntryHapticsInteractor
import com.android.systemui.deviceentry.shared.model.FailedFaceAuthenticationStatus
import com.android.systemui.keyevent.data.repository.fakeKeyEventRepository
import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
import com.android.systemui.keyguard.data.repository.deviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFaceAuthRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
+import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
import com.android.systemui.keyguard.shared.model.FailFingerprintAuthenticationStatus
import com.android.systemui.kosmos.testScope
@@ -158,9 +159,10 @@
}
private suspend fun enterDeviceFromBiometricUnlock() {
- kosmos.fakeDeviceEntryRepository.enteringDeviceFromBiometricUnlock(
+ kosmos.fakeKeyguardRepository.setBiometricUnlockSource(
BiometricUnlockSource.FINGERPRINT_SENSOR
)
+ kosmos.fakeKeyguardRepository.setBiometricUnlockState(BiometricUnlockModel.WAKE_AND_UNLOCK)
}
private fun fingerprintFailure() {
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 8a3a434..1183964 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -25,6 +25,7 @@
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
import static com.android.systemui.Flags.FLAG_REFACTOR_GET_CURRENT_USER;
+import static com.android.systemui.Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR;
import static com.android.systemui.keyguard.KeyguardViewMediator.DELAYED_KEYGUARD_ACTION;
import static com.android.systemui.keyguard.KeyguardViewMediator.KEYGUARD_LOCK_AFTER_DELAY_DEFAULT;
import static com.android.systemui.keyguard.KeyguardViewMediator.REBOOT_MAINLINE_UPDATE;
@@ -270,8 +271,8 @@
mSceneContainerFlags,
mKosmos::getCommunalInteractor);
mFeatureFlags = new FakeFeatureFlags();
- mFeatureFlags.set(Flags.KEYGUARD_WM_STATE_REFACTOR, false);
mSetFlagsRule.enableFlags(FLAG_REFACTOR_GET_CURRENT_USER);
+ mSetFlagsRule.disableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR);
DejankUtils.setImmediate(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index 4f3a63d..e93ad0be3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -21,6 +21,7 @@
import com.android.keyguard.KeyguardSecurityModel
import com.android.keyguard.KeyguardSecurityModel.SecurityMode.PIN
import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
+import com.android.systemui.Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR
import com.android.systemui.SysuiTestCase
import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
@@ -29,7 +30,6 @@
import com.android.systemui.communal.shared.model.CommunalSceneKey
import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState
import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.FakeCommandQueue
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardSurfaceBehindRepository
@@ -137,8 +137,8 @@
whenever(keyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(PIN)
- featureFlags = FakeFeatureFlags().apply { set(Flags.KEYGUARD_WM_STATE_REFACTOR, false) }
mSetFlagsRule.enableFlags(FLAG_COMMUNAL_HUB)
+ featureFlags = FakeFeatureFlags()
keyguardInteractor = createKeyguardInteractor()
@@ -299,6 +299,10 @@
powerInteractor = powerInteractor,
)
.apply { start() }
+
+ mSetFlagsRule.disableFlags(
+ FLAG_KEYGUARD_WM_STATE_REFACTOR,
+ )
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt
index c864704..699284e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt
@@ -39,6 +39,7 @@
import com.android.systemui.statusbar.VibratorHelper
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -71,6 +72,7 @@
FakeFeatureFlagsClassic().apply { set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false) }
underTest =
DefaultDeviceEntrySection(
+ TestScope().backgroundScope,
keyguardUpdateMonitor,
authController,
windowManager,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt
index f93d52b..aa54565 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt
@@ -28,6 +28,7 @@
import android.view.ViewConfiguration
import android.view.WindowManager
import androidx.test.filters.SmallTest
+import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.util.LatencyTracker
import com.android.systemui.SysuiTestCase
import com.android.systemui.plugins.NavigationEdgeBackPlugin
@@ -40,8 +41,10 @@
import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
+import org.mockito.Mockito.anyInt
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@SmallTest
@@ -59,12 +62,16 @@
@Mock private lateinit var windowManager: WindowManager
@Mock private lateinit var configurationController: ConfigurationController
@Mock private lateinit var latencyTracker: LatencyTracker
+ @Mock private lateinit var interactionJankMonitor: InteractionJankMonitor
@Mock private lateinit var layoutParams: WindowManager.LayoutParams
@Mock private lateinit var backCallback: NavigationEdgeBackPlugin.BackCallback
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
+ `when`(interactionJankMonitor.begin(any(), anyInt())).thenReturn(true)
+ `when`(interactionJankMonitor.end(anyInt())).thenReturn(true)
+ `when`(interactionJankMonitor.cancel(anyInt())).thenReturn(true)
mBackPanelController =
BackPanelController(
context,
@@ -74,6 +81,7 @@
vibratorHelper,
configurationController,
latencyTracker,
+ interactionJankMonitor,
)
mBackPanelController.setLayoutParams(layoutParams)
mBackPanelController.setBackCallback(backCallback)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest_311121830.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest_311121830.kt
new file mode 100644
index 0000000..e8aa8f0
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest_311121830.kt
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tileimpl
+
+import android.animation.AnimatorTestRule
+import android.content.Context
+import android.service.quicksettings.Tile
+import android.testing.AndroidTestingRunner
+import android.testing.UiThreadTest
+import android.view.ContextThemeWrapper
+import android.view.View
+import android.widget.ImageView
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.connectivity.WifiIcons
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import org.junit.Rule
+import org.junit.runner.RunWith
+
+/** Test for regression b/311121830 */
+@RunWith(AndroidTestingRunner::class)
+@UiThreadTest
+@SmallTest
+class QSIconViewImplTest_311121830 : SysuiTestCase() {
+
+ @get:Rule val animatorRule = AnimatorTestRule()
+
+ @Test
+ fun alwaysLastIcon() {
+ // Need to inflate with the correct theme so the colors can be retrieved and the animations
+ // are run
+ val iconView =
+ AnimateQSIconViewImpl(
+ ContextThemeWrapper(context, R.style.Theme_SystemUI_QuickSettings)
+ )
+
+ val initialState =
+ QSTile.State().apply {
+ state = Tile.STATE_INACTIVE
+ icon = QSTileImpl.ResourceIcon.get(R.drawable.ic_qs_no_internet_available)
+ }
+ val firstState =
+ QSTile.State().apply {
+ state = Tile.STATE_ACTIVE
+ icon = QSTileImpl.ResourceIcon.get(WifiIcons.WIFI_NO_INTERNET_ICONS[4])
+ }
+ val secondState =
+ QSTile.State().apply {
+ state = Tile.STATE_ACTIVE
+ icon = QSTileImpl.ResourceIcon.get(WifiIcons.WIFI_FULL_ICONS[4])
+ }
+
+ // Start with the initial state
+ iconView.setIcon(initialState, /* allowAnimations= */ false)
+
+ // Set the first state to animate, and advance time to half the time of the animation
+ iconView.setIcon(firstState, /* allowAnimations= */ true)
+ animatorRule.advanceTimeBy(QSIconViewImpl.QS_ANIM_LENGTH / 2)
+
+ // Set the second state to animate (it shouldn't, because `State.state` is the same) and
+ // advance time to 2 animations length
+ iconView.setIcon(secondState, /* allowAnimations= */ true)
+ animatorRule.advanceTimeBy(QSIconViewImpl.QS_ANIM_LENGTH * 2)
+
+ assertThat(iconView.mLastIcon).isEqualTo(secondState.icon)
+ }
+
+ private class AnimateQSIconViewImpl(context: Context) : QSIconViewImpl(context) {
+ override fun createIcon(): View {
+ return object : ImageView(context) {
+ override fun isShown(): Boolean {
+ return true
+ }
+ }
+ }
+ }
+}
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 70a48f5..e9f2132 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
@@ -131,7 +131,10 @@
whenever(packageManager.resolveServiceAsUser(any(), anyInt(), anyInt()))
.thenReturn(mock(ResolveInfo::class.java))
- featureFlags.set(Flags.KEYGUARD_WM_STATE_REFACTOR, false)
+ mSetFlagsRule.disableFlags(
+ com.android.systemui.Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR,
+ )
+
subject =
OverviewProxyService(
context,
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 1ab4c32..dbe63f2 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
@@ -860,9 +860,6 @@
when(mNotificationStackScrollLayout.onKeyguard()).thenReturn(true);
mController.getNotifStackController().setNotifStats(NotifStats.getEmpty());
- // WHEN: call updateImportantForAccessibility
- mController.updateImportantForAccessibility();
-
// THEN: mNotificationStackScrollLayout should not be important for A11y
verify(mNotificationStackScrollLayout)
.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
@@ -884,9 +881,6 @@
/* hasClearableSilentNotifs = */ false)
);
- // WHEN: call updateImportantForAccessibility
- mController.updateImportantForAccessibility();
-
// THEN: mNotificationStackScrollLayout should be important for A11y
verify(mNotificationStackScrollLayout)
.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
@@ -908,9 +902,6 @@
/* hasClearableSilentNotifs = */ false)
);
- // WHEN: call updateImportantForAccessibility
- mController.updateImportantForAccessibility();
-
// THEN: mNotificationStackScrollLayout should be important for A11y
verify(mNotificationStackScrollLayout)
.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
@@ -925,9 +916,6 @@
when(mNotificationStackScrollLayout.onKeyguard()).thenReturn(false);
mController.getNotifStackController().setNotifStats(NotifStats.getEmpty());
- // WHEN: call updateImportantForAccessibility
- mController.updateImportantForAccessibility();
-
// THEN: mNotificationStackScrollLayout should be important for A11y
verify(mNotificationStackScrollLayout)
.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
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 8dde935..cb45315 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
@@ -182,8 +182,10 @@
when(mBouncerViewDelegate.getBackCallback()).thenReturn(mBouncerViewDelegateBackCallback);
mFeatureFlags = new FakeFeatureFlags();
mFeatureFlags.set(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT, false);
- mFeatureFlags.set(Flags.KEYGUARD_WM_STATE_REFACTOR, false);
- mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
+ mSetFlagsRule.disableFlags(
+ com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR,
+ com.android.systemui.Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR
+ );
when(mNotificationShadeWindowController.getWindowRootView())
.thenReturn(mNotificationShadeWindowView);
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
index e25e8c0..bc7e7af 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
@@ -39,13 +39,4 @@
private fun onConfigured(id: Int, providerInfo: AppWidgetProviderInfo, priority: Int) {
_communalWidgets.value += listOf(CommunalWidgetContentModel(id, providerInfo, priority))
}
-
- private var isHostActive = false
- override fun updateAppWidgetHostActive(active: Boolean) {
- isHostActive = active
- }
-
- fun isHostActive(): Boolean {
- return isHostActive
- }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryRepository.kt
index 6436a38..77caeaa 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryRepository.kt
@@ -16,25 +16,16 @@
package com.android.systemui.deviceentry.data.repository
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
import dagger.Binds
import dagger.Module
import javax.inject.Inject
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
/** Fake implementation of [DeviceEntryRepository] */
@SysUISingleton
class FakeDeviceEntryRepository @Inject constructor() : DeviceEntryRepository {
- private val _enteringDeviceFromBiometricUnlock: MutableSharedFlow<BiometricUnlockSource> =
- MutableSharedFlow()
- override val enteringDeviceFromBiometricUnlock: Flow<BiometricUnlockSource> =
- _enteringDeviceFromBiometricUnlock.asSharedFlow()
-
private var isLockscreenEnabled = true
private val _isBypassEnabled = MutableStateFlow(false)
@@ -62,10 +53,6 @@
fun setBypassEnabled(isBypassEnabled: Boolean) {
_isBypassEnabled.value = isBypassEnabled
}
-
- suspend fun enteringDeviceFromBiometricUnlock(sourceType: BiometricUnlockSource) {
- _enteringDeviceFromBiometricUnlock.emit(sourceType)
- }
}
@Module
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/AuthRippleInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/AuthRippleInteractorKosmos.kt
new file mode 100644
index 0000000..3070cf4
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/AuthRippleInteractorKosmos.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.deviceentry.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+@OptIn(ExperimentalCoroutinesApi::class)
+val Kosmos.authRippleInteractor by
+ Kosmos.Fixture {
+ AuthRippleInteractor(
+ deviceEntrySourceInteractor = deviceEntrySourceInteractor,
+ deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorKosmos.kt
index de58ae5..878e385 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorKosmos.kt
@@ -30,7 +30,7 @@
val Kosmos.deviceEntryHapticsInteractor by
Kosmos.Fixture {
DeviceEntryHapticsInteractor(
- deviceEntryInteractor = deviceEntryInteractor,
+ deviceEntrySourceInteractor = deviceEntrySourceInteractor,
deviceEntryFingerprintAuthInteractor = deviceEntryFingerprintAuthInteractor,
deviceEntryBiometricAuthInteractor = deviceEntryBiometricAuthInteractor,
fingerprintPropertyRepository = fingerprintPropertyRepository,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorKosmos.kt
index 8dcdd3a..0d1a31f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorKosmos.kt
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
package com.android.systemui.deviceentry.domain.interactor
import com.android.systemui.authentication.domain.interactor.authenticationInteractor
@@ -28,6 +26,7 @@
import com.android.systemui.scene.shared.flag.sceneContainerFlags
import kotlinx.coroutines.ExperimentalCoroutinesApi
+@ExperimentalCoroutinesApi
val Kosmos.deviceEntryInteractor by
Kosmos.Fixture {
DeviceEntryInteractor(
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntrySourceInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntrySourceInteractorKosmos.kt
new file mode 100644
index 0000000..0b9ec92
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntrySourceInteractorKosmos.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.deviceentry.domain.interactor
+
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
+import com.android.systemui.kosmos.Kosmos
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+@ExperimentalCoroutinesApi
+val Kosmos.deviceEntrySourceInteractor by
+ Kosmos.Fixture {
+ DeviceEntrySourceInteractor(
+ keyguardInteractor = keyguardInteractor,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index 5766f7a..793e2d7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -65,7 +65,7 @@
override val isKeyguardShowing: Flow<Boolean> = _isKeyguardShowing
private val _isKeyguardUnlocked = MutableStateFlow(false)
- override val isKeyguardUnlocked: StateFlow<Boolean> = _isKeyguardUnlocked.asStateFlow()
+ override val isKeyguardDismissible: StateFlow<Boolean> = _isKeyguardUnlocked.asStateFlow()
private val _isKeyguardOccluded = MutableStateFlow(false)
override val isKeyguardOccluded: Flow<Boolean> = _isKeyguardOccluded
@@ -165,7 +165,7 @@
_isKeyguardOccluded.value = isOccluded
}
- fun setKeyguardUnlocked(isUnlocked: Boolean) {
+ fun setKeyguardDismissible(isUnlocked: Boolean) {
_isKeyguardUnlocked.value = isUnlocked
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelKosmos.kt
index 5ceefde..73fd999 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelKosmos.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.ui.viewmodel
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
+import com.android.systemui.deviceentry.domain.interactor.deviceEntrySourceInteractor
import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor
import com.android.systemui.keyguard.domain.interactor.burnInInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
@@ -27,6 +28,7 @@
import com.android.systemui.scene.shared.flag.sceneContainerFlags
import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.statusbar.phone.statusBarKeyguardViewManager
+import kotlinx.coroutines.ExperimentalCoroutinesApi
val Kosmos.fakeDeviceEntryIconViewModelTransition by Fixture { FakeDeviceEntryIconTransition() }
@@ -34,6 +36,7 @@
setOf<DeviceEntryIconTransition>(fakeDeviceEntryIconViewModelTransition)
}
+@ExperimentalCoroutinesApi
val Kosmos.deviceEntryIconViewModel by Fixture {
DeviceEntryIconViewModel(
transitions = deviceEntryIconViewModelTransitionsMock,
@@ -46,5 +49,6 @@
sceneContainerFlags = sceneContainerFlags,
keyguardViewController = { statusBarKeyguardViewManager },
deviceEntryInteractor = deviceEntryInteractor,
+ deviceEntrySourceInteractor = deviceEntrySourceInteractor,
)
}
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index 5c93991..f914ed5 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -31,9 +31,12 @@
import android.os.ICancellationSignal;
import android.os.RemoteException;
import android.service.autofill.AutofillService;
+import android.service.autofill.ConvertCredentialRequest;
+import android.service.autofill.ConvertCredentialResponse;
import android.service.autofill.FillRequest;
import android.service.autofill.FillResponse;
import android.service.autofill.IAutoFillService;
+import android.service.autofill.IConvertCredentialCallback;
import android.service.autofill.IFillCallback;
import android.service.autofill.ISaveCallback;
import android.service.autofill.SaveRequest;
@@ -69,6 +72,7 @@
private int mPendingFillRequestId = INVALID_REQUEST_ID;
private AtomicReference<IFillCallback> mFillCallback;
private AtomicReference<ISaveCallback> mSaveCallback;
+ private AtomicReference<IConvertCredentialCallback> mConvertCredentialCallback;
private final ComponentName mComponentName;
private final boolean mIsCredentialAutofillService;
@@ -81,13 +85,20 @@
extends AbstractRemoteService.VultureCallback<RemoteFillService> {
void onFillRequestSuccess(int requestId, @Nullable FillResponse response,
@NonNull String servicePackageName, int requestFlags);
+
void onFillRequestFailure(int requestId, @Nullable CharSequence message);
+
void onFillRequestTimeout(int requestId);
+
void onSaveRequestSuccess(@NonNull String servicePackageName,
@Nullable IntentSender intentSender);
+
// TODO(b/80093094): add timeout here too?
void onSaveRequestFailure(@Nullable CharSequence message,
@NonNull String servicePackageName);
+
+ void onConvertCredentialRequestSuccess(@NonNull ConvertCredentialResponse
+ convertCredentialResponse);
}
RemoteFillService(Context context, ComponentName componentName, int userId,
@@ -211,6 +222,32 @@
}
}
+ static class IConvertCredentialCallbackDelegate extends IConvertCredentialCallback.Stub {
+
+ private WeakReference<IConvertCredentialCallback> mCallbackWeakRef;
+
+ IConvertCredentialCallbackDelegate(IConvertCredentialCallback callback) {
+ mCallbackWeakRef = new WeakReference(callback);
+ }
+
+ @Override
+ public void onSuccess(ConvertCredentialResponse convertCredentialResponse)
+ throws RemoteException {
+ IConvertCredentialCallback callback = mCallbackWeakRef.get();
+ if (callback != null) {
+ callback.onSuccess(convertCredentialResponse);
+ }
+ }
+
+ @Override
+ public void onFailure(CharSequence message) throws RemoteException {
+ IConvertCredentialCallback callback = mCallbackWeakRef.get();
+ if (callback != null) {
+ callback.onFailure(message);
+ }
+ }
+ }
+
/**
* Wraps an {@link IFillCallback} object using weak reference.
*
@@ -237,6 +274,18 @@
return callback;
}
+ /**
+ * Wraps an {@link IConvertCredentialCallback} object using weak reference
+ */
+ private IConvertCredentialCallback maybeWrapWithWeakReference(
+ IConvertCredentialCallback callback) {
+ if (remoteFillServiceUseWeakReference()) {
+ mConvertCredentialCallback = new AtomicReference<>(callback);
+ return new IConvertCredentialCallbackDelegate(callback);
+ }
+ return callback;
+ }
+
public void onFillCredentialRequest(@NonNull FillRequest request,
IAutoFillManagerClient autofillCallback) {
if (sVerbose) {
@@ -378,6 +427,52 @@
}));
}
+ public void onConvertCredentialRequest(
+ @NonNull ConvertCredentialRequest convertCredentialRequest) {
+ if (sVerbose) Slog.v(TAG, "calling onConvertCredentialRequest()");
+ CompletableFuture<ConvertCredentialResponse>
+ connectThenConvertCredentialRequest = postAsync(
+ remoteService -> {
+ if (sVerbose) {
+ Slog.v(TAG, "calling onConvertCredentialRequest()");
+ }
+ CompletableFuture<ConvertCredentialResponse>
+ convertCredentialCompletableFuture = new CompletableFuture<>();
+ remoteService.onConvertCredentialRequest(convertCredentialRequest,
+ maybeWrapWithWeakReference(
+ new IConvertCredentialCallback.Stub() {
+ @Override
+ public void onSuccess(ConvertCredentialResponse
+ convertCredentialResponse) {
+ convertCredentialCompletableFuture
+ .complete(convertCredentialResponse);
+ }
+
+ @Override
+ public void onFailure(CharSequence message) {
+ String errorMessage =
+ message == null ? "" :
+ String.valueOf(message);
+ convertCredentialCompletableFuture
+ .completeExceptionally(
+ new RuntimeException(errorMessage));
+ }
+ })
+ );
+ return convertCredentialCompletableFuture;
+ }).orTimeout(TIMEOUT_REMOTE_REQUEST_MILLIS, TimeUnit.MILLISECONDS);
+
+ connectThenConvertCredentialRequest.whenComplete(
+ (res, err) -> Handler.getMain().post(() -> {
+ if (err == null) {
+ mCallbacks.onConvertCredentialRequestSuccess(res);
+ } else {
+ // TODO: Add a callback function to log this failure
+ Slog.e(TAG, "Error calling on convert credential request", err);
+ }
+ }));
+ }
+
public void onSaveRequest(@NonNull SaveRequest request) {
postAsync(service -> {
if (sVerbose) Slog.v(TAG, "calling onSaveRequest()");
diff --git a/services/autofill/java/com/android/server/autofill/SecondaryProviderHandler.java b/services/autofill/java/com/android/server/autofill/SecondaryProviderHandler.java
index 1234703..0af703e 100644
--- a/services/autofill/java/com/android/server/autofill/SecondaryProviderHandler.java
+++ b/services/autofill/java/com/android/server/autofill/SecondaryProviderHandler.java
@@ -25,6 +25,7 @@
import android.content.Context;
import android.content.IntentSender;
import android.os.Bundle;
+import android.service.autofill.ConvertCredentialResponse;
import android.service.autofill.FillRequest;
import android.service.autofill.FillResponse;
import android.util.Slog;
@@ -98,6 +99,12 @@
}
+ @Override
+ public void onConvertCredentialRequestSuccess(@NonNull ConvertCredentialResponse
+ convertCredentialResponse) {
+
+ }
+
/**
* Requests a new fill response.
*/
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index f3b74ea..049feee 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -24,6 +24,7 @@
import static android.service.autofill.Dataset.PICK_REASON_PROVIDER_DETECTION_ONLY;
import static android.service.autofill.Dataset.PICK_REASON_PROVIDER_DETECTION_PREFERRED_WITH_PCC;
import static android.service.autofill.Dataset.PICK_REASON_UNKNOWN;
+import static android.service.autofill.FillEventHistory.Event.UI_TYPE_CREDMAN_BOTTOM_SHEET;
import static android.service.autofill.FillEventHistory.Event.UI_TYPE_DIALOG;
import static android.service.autofill.FillEventHistory.Event.UI_TYPE_INLINE;
import static android.service.autofill.FillEventHistory.Event.UI_TYPE_MENU;
@@ -44,6 +45,7 @@
import static android.view.autofill.AutofillManager.ACTION_VIEW_EXITED;
import static android.view.autofill.AutofillManager.COMMIT_REASON_SESSION_DESTROYED;
import static android.view.autofill.AutofillManager.COMMIT_REASON_UNKNOWN;
+import static android.view.autofill.AutofillManager.EXTRA_AUTOFILL_REQUEST_ID;
import static android.view.autofill.AutofillManager.FLAG_SMART_SUGGESTION_SYSTEM;
import static android.view.autofill.AutofillManager.getSmartSuggestionModeToString;
@@ -130,6 +132,7 @@
import android.service.autofill.AutofillFieldClassificationService.Scores;
import android.service.autofill.AutofillService;
import android.service.autofill.CompositeUserData;
+import android.service.autofill.ConvertCredentialResponse;
import android.service.autofill.Dataset;
import android.service.autofill.Dataset.DatasetEligibleReason;
import android.service.autofill.Field;
@@ -2429,6 +2432,29 @@
removeFromService();
}
+ // FillServiceCallbacks
+ @Override
+ public void onConvertCredentialRequestSuccess(@NonNull ConvertCredentialResponse
+ convertCredentialResponse) {
+ Dataset dataset = convertCredentialResponse.getDataset();
+ Bundle clientState = convertCredentialResponse.getClientState();
+ if (dataset != null) {
+ int requestId = -1;
+ if (clientState != null) {
+ requestId = clientState.getInt(EXTRA_AUTOFILL_REQUEST_ID);
+ } else {
+ Slog.e(TAG, "onConvertCredentialRequestSuccess(): client state is null, this "
+ + "would cause loss in logging.");
+ }
+ // TODO: Add autofill related logging; consider whether to log the index
+ fill(requestId, /* datasetIndex=*/ -1, dataset, UI_TYPE_CREDMAN_BOTTOM_SHEET);
+ } else {
+ // TODO: Add logging to log this error case
+ Slog.e(TAG, "onConvertCredentialRequestSuccess(): dataset inside response is "
+ + "null");
+ }
+ }
+
/**
* Gets the {@link FillContext} for a request.
*
diff --git a/services/companion/TEST_MAPPING b/services/companion/TEST_MAPPING
index 37c47ba..ae6d591 100644
--- a/services/companion/TEST_MAPPING
+++ b/services/companion/TEST_MAPPING
@@ -9,5 +9,10 @@
{
"name": "CtsCompanionDeviceManagerNoCompanionServicesTestCases"
}
+ ],
+ "postsubmit": [
+ {
+ "name": "CtsCompanionDeviceManagerMultiProcessTestCases"
+ }
]
}
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 2e01ced..5019428 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -368,8 +368,10 @@
if (blueToothDevices != null) {
for (BluetoothDevice bluetoothDevice : blueToothDevices) {
- final List<ParcelUuid> deviceUuids = bluetoothDevice.getUuids() == null
- ? Collections.emptyList() : Arrays.asList(bluetoothDevice.getUuids());
+ final ParcelUuid[] bluetoothDeviceUuids = bluetoothDevice.getUuids();
+
+ final List<ParcelUuid> deviceUuids = ArrayUtils.isEmpty(bluetoothDeviceUuids)
+ ? Collections.emptyList() : Arrays.asList(bluetoothDeviceUuids);
for (AssociationInfo ai:
mAssociationStore.getAssociationsByAddress(bluetoothDevice.getAddress())) {
diff --git a/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java b/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java
index 7eca119..c514f3e 100644
--- a/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java
+++ b/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java
@@ -38,6 +38,7 @@
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ArrayUtils;
import com.android.server.companion.AssociationStore;
import com.android.server.companion.ObservableUuid;
import com.android.server.companion.ObservableUuidStore;
@@ -172,8 +173,10 @@
mAssociationStore.getAssociationsByAddress(device.getAddress());
final List<ObservableUuid> observableUuids =
mObservableUuidStore.getObservableUuidsForUser(userId);
- final List<ParcelUuid> deviceUuids = device.getUuids() == null
- ? Collections.emptyList() : Arrays.asList(device.getUuids());
+ final ParcelUuid[] bluetoothDeviceUuids = device.getUuids();
+
+ final List<ParcelUuid> deviceUuids = ArrayUtils.isEmpty(bluetoothDeviceUuids)
+ ? Collections.emptyList() : Arrays.asList(bluetoothDeviceUuids);
if (DEBUG) {
Log.d(TAG, "onDevice_ConnectivityChanged() " + btDeviceToString(device)
diff --git a/services/companion/java/com/android/server/companion/virtual/InputController.java b/services/companion/java/com/android/server/companion/virtual/InputController.java
index 3b9d92d..8962bf0 100644
--- a/services/companion/java/com/android/server/companion/virtual/InputController.java
+++ b/services/companion/java/com/android/server/companion/virtual/InputController.java
@@ -163,7 +163,7 @@
createDeviceInternal(InputDeviceDescriptor.TYPE_MOUSE, deviceName, vendorId, productId,
deviceToken, displayId, phys,
() -> mNativeWrapper.openUinputMouse(deviceName, vendorId, productId, phys));
- mInputManagerInternal.setVirtualMousePointerDisplayId(displayId);
+ setVirtualMousePointerDisplayId(displayId);
}
void createTouchscreen(@NonNull String deviceName, int vendorId, int productId,
@@ -235,8 +235,7 @@
// id if there's another mouse (choose the most recent). The inputDeviceDescriptor must be
// removed from the mInputDeviceDescriptors instance variable prior to this point.
if (inputDeviceDescriptor.isMouse()) {
- if (mInputManagerInternal.getVirtualMousePointerDisplayId()
- == inputDeviceDescriptor.getDisplayId()) {
+ if (getVirtualMousePointerDisplayId() == inputDeviceDescriptor.getDisplayId()) {
updateActivePointerDisplayIdLocked();
}
}
@@ -271,6 +270,7 @@
mWindowManager.setDisplayImePolicy(displayId, policy);
}
+ // TODO(b/293587049): Remove after pointer icon refactor is complete.
@GuardedBy("mLock")
private void updateActivePointerDisplayIdLocked() {
InputDeviceDescriptor mostRecentlyCreatedMouse = null;
@@ -285,11 +285,11 @@
}
}
if (mostRecentlyCreatedMouse != null) {
- mInputManagerInternal.setVirtualMousePointerDisplayId(
+ setVirtualMousePointerDisplayId(
mostRecentlyCreatedMouse.getDisplayId());
} else {
// All mice have been unregistered
- mInputManagerInternal.setVirtualMousePointerDisplayId(Display.INVALID_DISPLAY);
+ setVirtualMousePointerDisplayId(Display.INVALID_DISPLAY);
}
}
@@ -349,10 +349,8 @@
if (inputDeviceDescriptor == null) {
return false;
}
- if (inputDeviceDescriptor.getDisplayId()
- != mInputManagerInternal.getVirtualMousePointerDisplayId()) {
- mInputManagerInternal.setVirtualMousePointerDisplayId(
- inputDeviceDescriptor.getDisplayId());
+ if (inputDeviceDescriptor.getDisplayId() != getVirtualMousePointerDisplayId()) {
+ setVirtualMousePointerDisplayId(inputDeviceDescriptor.getDisplayId());
}
return mNativeWrapper.writeButtonEvent(inputDeviceDescriptor.getNativePointer(),
event.getButtonCode(), event.getAction(), event.getEventTimeNanos());
@@ -380,10 +378,8 @@
if (inputDeviceDescriptor == null) {
return false;
}
- if (inputDeviceDescriptor.getDisplayId()
- != mInputManagerInternal.getVirtualMousePointerDisplayId()) {
- mInputManagerInternal.setVirtualMousePointerDisplayId(
- inputDeviceDescriptor.getDisplayId());
+ if (inputDeviceDescriptor.getDisplayId() != getVirtualMousePointerDisplayId()) {
+ setVirtualMousePointerDisplayId(inputDeviceDescriptor.getDisplayId());
}
return mNativeWrapper.writeRelativeEvent(inputDeviceDescriptor.getNativePointer(),
event.getRelativeX(), event.getRelativeY(), event.getEventTimeNanos());
@@ -397,10 +393,8 @@
if (inputDeviceDescriptor == null) {
return false;
}
- if (inputDeviceDescriptor.getDisplayId()
- != mInputManagerInternal.getVirtualMousePointerDisplayId()) {
- mInputManagerInternal.setVirtualMousePointerDisplayId(
- inputDeviceDescriptor.getDisplayId());
+ if (inputDeviceDescriptor.getDisplayId() != getVirtualMousePointerDisplayId()) {
+ setVirtualMousePointerDisplayId(inputDeviceDescriptor.getDisplayId());
}
return mNativeWrapper.writeScrollEvent(inputDeviceDescriptor.getNativePointer(),
event.getXAxisMovement(), event.getYAxisMovement(), event.getEventTimeNanos());
@@ -415,12 +409,11 @@
throw new IllegalArgumentException(
"Could not get cursor position for input device for given token");
}
- if (inputDeviceDescriptor.getDisplayId()
- != mInputManagerInternal.getVirtualMousePointerDisplayId()) {
- mInputManagerInternal.setVirtualMousePointerDisplayId(
- inputDeviceDescriptor.getDisplayId());
+ if (inputDeviceDescriptor.getDisplayId() != getVirtualMousePointerDisplayId()) {
+ setVirtualMousePointerDisplayId(inputDeviceDescriptor.getDisplayId());
}
- return LocalServices.getService(InputManagerInternal.class).getCursorPosition();
+ return LocalServices.getService(InputManagerInternal.class).getCursorPosition(
+ inputDeviceDescriptor.getDisplayId());
}
}
@@ -847,4 +840,22 @@
/** Returns true if the calling thread is a valid thread for device creation. */
boolean isValidThread();
}
+
+ // TODO(b/293587049): Remove after pointer icon refactor is complete.
+ private void setVirtualMousePointerDisplayId(int displayId) {
+ if (com.android.input.flags.Flags.enablePointerChoreographer()) {
+ // We no longer need to set the pointer display when pointer choreographer is enabled.
+ return;
+ }
+ mInputManagerInternal.setVirtualMousePointerDisplayId(displayId);
+ }
+
+ // TODO(b/293587049): Remove after pointer icon refactor is complete.
+ private int getVirtualMousePointerDisplayId() {
+ if (com.android.input.flags.Flags.enablePointerChoreographer()) {
+ // We no longer need to get the pointer display when pointer choreographer is enabled.
+ return Display.INVALID_DISPLAY;
+ }
+ return mInputManagerInternal.getVirtualMousePointerDisplayId();
+ }
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 57c52c2..45f657d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -3754,6 +3754,11 @@
}
@Override
+ public void onProcessStarted(int pid, int processUid, int packageUid, String packageName,
+ String processName) {
+ }
+
+ @Override
public void onForegroundServicesChanged(int pid, int uid, int serviceTypes) {
}
diff --git a/services/core/java/com/android/server/am/AppBatteryExemptionTracker.java b/services/core/java/com/android/server/am/AppBatteryExemptionTracker.java
index b07d9a6..9c2e69b 100644
--- a/services/core/java/com/android/server/am/AppBatteryExemptionTracker.java
+++ b/services/core/java/com/android/server/am/AppBatteryExemptionTracker.java
@@ -520,7 +520,7 @@
/**
* Default value to {@link #mTrackerEnabled}.
*/
- static final boolean DEFAULT_BG_BATTERY_EXEMPTION_ENABLED = true;
+ static final boolean DEFAULT_BG_BATTERY_EXEMPTION_ENABLED = false;
AppBatteryExemptionPolicy(@NonNull Injector injector,
@NonNull AppBatteryExemptionTracker tracker) {
diff --git a/services/core/java/com/android/server/am/AppFGSTracker.java b/services/core/java/com/android/server/am/AppFGSTracker.java
index 1f98aba..fb89b8e 100644
--- a/services/core/java/com/android/server/am/AppFGSTracker.java
+++ b/services/core/java/com/android/server/am/AppFGSTracker.java
@@ -102,6 +102,11 @@
}
@Override
+ public void onProcessStarted(int pid, int processUid, int packageUid, String packageName,
+ String processName) {
+ }
+
+ @Override
public void onProcessDied(int pid, int uid) {
}
};
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index fa5dbd2..f5c34a5 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -2852,6 +2852,7 @@
? PROC_START_TIMEOUT_WITH_WRAPPER : PROC_START_TIMEOUT);
}
}
+ dispatchProcessStarted(app, pid);
checkSlow(app.getStartTime(), "startProcess: done updating pids map");
return true;
}
@@ -4977,6 +4978,22 @@
}
}
+ void dispatchProcessStarted(ProcessRecord app, int pid) {
+ int i = mProcessObservers.beginBroadcast();
+ while (i > 0) {
+ i--;
+ final IProcessObserver observer = mProcessObservers.getBroadcastItem(i);
+ if (observer != null) {
+ try {
+ observer.onProcessStarted(pid, app.uid, app.info.uid,
+ app.info.packageName, app.processName);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+ mProcessObservers.finishBroadcast();
+ }
+
void dispatchProcessDied(int pid, int uid) {
int i = mProcessObservers.beginBroadcast();
while (i > 0) {
diff --git a/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java b/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java
index 684d6a0..cdd147a 100644
--- a/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java
+++ b/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java
@@ -177,6 +177,11 @@
}
@Override
+ public void onProcessStarted(int pid, int processUid, int packageUid, String packageName,
+ String processName) {
+ }
+
+ @Override
public void onForegroundServicesChanged(int pid, int uid, int serviceTypes) {
}
};
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 57b19cd..690c37a 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -914,28 +914,27 @@
di.mDeviceCodecFormat = codec;
mConnectedDevices.replace(key, di);
codecChange = true;
- }
- final int res = mAudioSystem.handleDeviceConfigChange(
- btInfo.mAudioSystemDevice, address, BtHelper.getName(btDevice), codec);
+ final int res = mAudioSystem.handleDeviceConfigChange(
+ btInfo.mAudioSystemDevice, address,
+ BtHelper.getName(btDevice), codec);
+ if (res != AudioSystem.AUDIO_STATUS_OK) {
+ AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
+ "APM handleDeviceConfigChange failed for A2DP device addr="
+ + address + " codec="
+ + AudioSystem.audioFormatToString(codec))
+ .printLog(TAG));
- if (res != AudioSystem.AUDIO_STATUS_OK) {
- AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
- "APM handleDeviceConfigChange failed for A2DP device addr="
- + address + " codec="
- + AudioSystem.audioFormatToString(codec))
- .printLog(TAG));
-
- // force A2DP device disconnection in case of error so that AudioService
- // state is consistent with audio policy manager state
- setBluetoothActiveDevice(new AudioDeviceBroker.BtDeviceInfo(btInfo,
- BluetoothProfile.STATE_DISCONNECTED));
- } else {
- AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
- "APM handleDeviceConfigChange success for A2DP device addr="
- + address
- + " codec=" + AudioSystem.audioFormatToString(codec))
- .printLog(TAG));
-
+ // force A2DP device disconnection in case of error so that AudioService
+ // state is consistent with audio policy manager state
+ setBluetoothActiveDevice(new AudioDeviceBroker.BtDeviceInfo(btInfo,
+ BluetoothProfile.STATE_DISCONNECTED));
+ } else {
+ AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
+ "APM handleDeviceConfigChange success for A2DP device addr="
+ + address
+ + " codec=" + AudioSystem.audioFormatToString(codec))
+ .printLog(TAG));
+ }
}
}
if (!codecChange) {
diff --git a/services/core/java/com/android/server/audio/SoundDoseHelper.java b/services/core/java/com/android/server/audio/SoundDoseHelper.java
index a30cdc4..9610034ca 100644
--- a/services/core/java/com/android/server/audio/SoundDoseHelper.java
+++ b/services/core/java/com/android/server/audio/SoundDoseHelper.java
@@ -1203,7 +1203,7 @@
@GuardedBy("mCsdStateLock")
private void sanitizeDoseRecords_l() {
if (mDoseRecords.size() > MAX_NUMBER_OF_CACHED_RECORDS) {
- int nrToRemove = MAX_NUMBER_OF_CACHED_RECORDS - mDoseRecords.size();
+ int nrToRemove = mDoseRecords.size() - MAX_NUMBER_OF_CACHED_RECORDS;
Log.w(TAG,
"Removing " + nrToRemove + " records from the total of " + mDoseRecords.size());
// Remove older elements to fit into persisted settings max length
diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java
index 21e6bac..3f3540e 100644
--- a/services/core/java/com/android/server/biometrics/AuthService.java
+++ b/services/core/java/com/android/server/biometrics/AuthService.java
@@ -20,7 +20,7 @@
// TODO(b/141025588): Create separate internal and external permissions for AuthService.
// TODO(b/141025588): Get rid of the USE_FINGERPRINT permission.
-import static android.Manifest.permission.MANAGE_BIOMETRIC_DIALOG;
+import static android.Manifest.permission.SET_BIOMETRIC_DIALOG_LOGO;
import static android.Manifest.permission.TEST_BIOMETRIC;
import static android.Manifest.permission.USE_BIOMETRIC;
import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
@@ -305,7 +305,7 @@
if (promptInfo.containsPrivateApiConfigurations()) {
checkInternalPermission();
}
- if (promptInfo.containsManageBioApiConfigurations()) {
+ if (promptInfo.containsSetLogoApiConfigurations()) {
checkManageBiometricPermission();
}
@@ -997,8 +997,8 @@
}
private void checkManageBiometricPermission() {
- getContext().enforceCallingOrSelfPermission(MANAGE_BIOMETRIC_DIALOG,
- "Must have MANAGE_BIOMETRIC_DIALOG permission");
+ getContext().enforceCallingOrSelfPermission(SET_BIOMETRIC_DIALOG_LOGO,
+ "Must have SET_BIOMETRIC_DIALOG_LOGO permission");
}
private void checkPermission() {
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index 6ec6a12..77cb08b 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -204,6 +204,10 @@
}
@Override
+ public void onProcessStarted(int pid, int processUid, int packageUid, String packageName,
+ String processName) {}
+
+ @Override
public void onProcessDied(int pid, int uid) {}
@Override
diff --git a/services/core/java/com/android/server/input/InputManagerInternal.java b/services/core/java/com/android/server/input/InputManagerInternal.java
index 380106b..b963a4b 100644
--- a/services/core/java/com/android/server/input/InputManagerInternal.java
+++ b/services/core/java/com/android/server/input/InputManagerInternal.java
@@ -87,12 +87,16 @@
* connected, the caller may be blocked for an arbitrary period of time.
*
* @return true if the pointer displayId was set successfully, or false if it fails.
+ *
+ * @deprecated TODO(b/293587049): Not needed - remove after Pointer Icon Refactor is complete.
*/
public abstract boolean setVirtualMousePointerDisplayId(int pointerDisplayId);
/**
* Gets the display id that the MouseCursorController is being forced to target. Returns
* {@link android.view.Display#INVALID_DISPLAY} if there is no override
+ *
+ * @deprecated TODO(b/293587049): Not needed - remove after Pointer Icon Refactor is complete.
*/
public abstract int getVirtualMousePointerDisplayId();
@@ -101,7 +105,7 @@
*
* Returns NaN-s as the coordinates if the cursor is not available.
*/
- public abstract PointF getCursorPosition();
+ public abstract PointF getCursorPosition(int displayId);
/**
* Enables or disables pointer acceleration for mouse movements.
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 338744b..687def0 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -1448,6 +1448,10 @@
}
private boolean setVirtualMousePointerDisplayIdBlocking(int overrideDisplayId) {
+ if (com.android.input.flags.Flags.enablePointerChoreographer()) {
+ throw new IllegalStateException(
+ "This must not be used when PointerChoreographer is enabled");
+ }
final boolean isRemovingOverride = overrideDisplayId == Display.INVALID_DISPLAY;
// Take care to not make calls to window manager while holding internal locks.
@@ -1486,6 +1490,10 @@
}
private int getVirtualMousePointerDisplayId() {
+ if (com.android.input.flags.Flags.enablePointerChoreographer()) {
+ throw new IllegalStateException(
+ "This must not be used when PointerChoreographer is enabled");
+ }
synchronized (mAdditionalDisplayInputPropertiesLock) {
return mOverriddenPointerDisplayId;
}
@@ -3332,8 +3340,8 @@
}
@Override
- public PointF getCursorPosition() {
- final float[] p = mNative.getMouseCursorPosition();
+ public PointF getCursorPosition(int displayId) {
+ final float[] p = mNative.getMouseCursorPosition(displayId);
if (p == null || p.length != 2) {
throw new IllegalStateException("Failed to get mouse cursor position");
}
diff --git a/services/core/java/com/android/server/input/NativeInputManagerService.java b/services/core/java/com/android/server/input/NativeInputManagerService.java
index a79a135..bc82078 100644
--- a/services/core/java/com/android/server/input/NativeInputManagerService.java
+++ b/services/core/java/com/android/server/input/NativeInputManagerService.java
@@ -233,14 +233,15 @@
void setStylusButtonMotionEventsEnabled(boolean enabled);
/**
- * Get the current position of the mouse cursor.
+ * Get the current position of the mouse cursor on the given display.
*
- * If the mouse cursor is not currently shown, the coordinate values will be NaN-s.
+ * If the mouse cursor is not currently shown, the coordinate values will be NaN-s. Use
+ * {@link android.view.Display#INVALID_DISPLAY} to get the position of the default mouse cursor.
*
* NOTE: This will grab the PointerController's lock, so we must be careful about calling this
* from the InputReader or Display threads, which may result in a deadlock.
*/
- float[] getMouseCursorPosition();
+ float[] getMouseCursorPosition(int displayId);
/** Set whether showing a pointer icon for styluses is enabled. */
void setStylusPointerIconEnabled(boolean enabled);
@@ -519,7 +520,7 @@
public native void setStylusButtonMotionEventsEnabled(boolean enabled);
@Override
- public native float[] getMouseCursorPosition();
+ public native float[] getMouseCursorPosition(int displayId);
@Override
public native void setStylusPointerIconEnabled(boolean enabled);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index d72ca7d..4767ebd 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -115,7 +115,6 @@
import android.util.Printer;
import android.util.Slog;
import android.util.SparseArray;
-import android.util.SparseBooleanArray;
import android.util.proto.ProtoOutputStream;
import android.view.InputChannel;
import android.view.InputDevice;
@@ -281,8 +280,6 @@
@NonNull
private InputMethodSettings mSettings;
final SettingsObserver mSettingsObserver;
- private final SparseBooleanArray mLoggedDeniedGetInputMethodWindowVisibleHeightForUid =
- new SparseBooleanArray(0);
final WindowManagerInternal mWindowManagerInternal;
private final ActivityManagerInternal mActivityManagerInternal;
final PackageManagerInternal mPackageManagerInternal;
@@ -1354,13 +1351,6 @@
clearPackageChangeState();
}
- @Override
- public void onUidRemoved(int uid) {
- synchronized (ImfLock.class) {
- mLoggedDeniedGetInputMethodWindowVisibleHeightForUid.delete(uid);
- }
- }
-
private void clearPackageChangeState() {
// No need to lock them because we access these fields only on getRegisteredHandler().
mChangedPackages.clear();
@@ -2399,16 +2389,6 @@
@StartInputReason int startInputReason,
int unverifiedTargetSdkVersion,
@NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
- String selectedMethodId = getSelectedMethodIdLocked();
-
- if (!mSystemReady) {
- // If the system is not yet ready, we shouldn't be running third
- // party code.
- return new InputBindResult(
- InputBindResult.ResultCode.ERROR_SYSTEM_NOT_READY,
- null, null, null, selectedMethodId, getSequenceNumberLocked(), false);
- }
-
if (!InputMethodUtils.checkIfPackageBelongsToUid(mPackageManagerInternal, cs.mUid,
editorInfo.packageName)) {
Slog.e(TAG, "Rejecting this client as it reported an invalid package name."
@@ -2429,6 +2409,7 @@
// Potentially override the selected input method if the new display belongs to a virtual
// device with a custom IME.
+ String selectedMethodId = getSelectedMethodIdLocked();
if (oldDisplayIdToShowIme != mDisplayIdToShowIme) {
final String deviceMethodId = computeCurrentDeviceMethodIdLocked(selectedMethodId);
if (deviceMethodId == null) {
@@ -3673,7 +3654,6 @@
+ "specified for cross-user startInputOrWindowGainedFocus()");
}
}
-
if (windowToken == null) {
Slog.e(TAG, "windowToken cannot be null.");
return InputBindResult.NULL;
@@ -3685,6 +3665,14 @@
"InputMethodManagerService#startInputOrWindowGainedFocus");
final InputBindResult result;
synchronized (ImfLock.class) {
+ if (!mSystemReady) {
+ // If the system is not yet ready, we shouldn't be running third arty code.
+ return new InputBindResult(
+ InputBindResult.ResultCode.ERROR_SYSTEM_NOT_READY,
+ null /* method */, null /* accessibilitySessions */, null /* channel */,
+ getSelectedMethodIdLocked(), getSequenceNumberLocked(),
+ false /* isInputMethodSuppressingSpellChecker */);
+ }
final long ident = Binder.clearCallingIdentity();
try {
result = startInputOrWindowGainedFocusInternalLocked(startInputReason,
@@ -4276,10 +4264,6 @@
synchronized (ImfLock.class) {
if (!canInteractWithImeLocked(callingUid, client,
"getInputMethodWindowVisibleHeight", null /* statsToken */)) {
- if (!mLoggedDeniedGetInputMethodWindowVisibleHeightForUid.get(callingUid)) {
- EventLog.writeEvent(0x534e4554, "204906124", callingUid, "");
- mLoggedDeniedGetInputMethodWindowVisibleHeightForUid.put(callingUid, true);
- }
return 0;
}
// This should probably use the caller's display id, but because this is unsupported
diff --git a/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java b/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java
index 5e38bca..2522f7b 100644
--- a/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java
@@ -27,6 +27,7 @@
import android.annotation.Nullable;
import android.location.Location;
+import android.location.LocationRequest;
import android.location.LocationResult;
import android.location.provider.ProviderRequest;
import android.os.SystemClock;
@@ -179,6 +180,7 @@
private void onThrottlingChangedLocked(boolean deliverImmediate) {
long throttlingIntervalMs = INTERVAL_DISABLED;
if (mDeviceStationary && mDeviceIdle && !mIncomingRequest.isLocationSettingsIgnored()
+ && mIncomingRequest.getQuality() != LocationRequest.QUALITY_HIGH_ACCURACY
&& mLastLocation != null
&& mLastLocation.getElapsedRealtimeAgeMillis(mDeviceStationaryRealtimeMs)
<= MAX_STATIONARY_LOCATION_AGE_MS) {
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 2cd3ab1..1d516e2 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -287,10 +287,14 @@
}
user.mPriorityStack.onSessionActiveStateChanged(record);
}
- setForegroundServiceAllowance(
- record,
- /* allowRunningInForeground= */ record.isActive()
- && (playbackState == null || playbackState.isActive()));
+ boolean allowRunningInForeground = record.isActive()
+ && (playbackState == null || playbackState.isActive());
+
+ Log.d(TAG, "onSessionActiveStateChanged: "
+ + "record=" + record
+ + "playbackState=" + playbackState
+ + "allowRunningInForeground=" + allowRunningInForeground);
+ setForegroundServiceAllowance(record, allowRunningInForeground);
mHandler.postSessionsChanged(record);
}
}
@@ -388,10 +392,12 @@
}
user.mPriorityStack.onPlaybackStateChanged(record, shouldUpdatePriority);
if (playbackState != null) {
- setForegroundServiceAllowance(
- record,
- /* allowRunningInForeground= */ playbackState.isActive()
- && record.isActive());
+ boolean allowRunningInForeground = playbackState.isActive() && record.isActive();
+ Log.d(TAG, "onSessionPlaybackStateChanged: "
+ + "record=" + record
+ + "playbackState=" + playbackState
+ + "allowRunningInForeground=" + allowRunningInForeground);
+ setForegroundServiceAllowance(record, allowRunningInForeground);
}
}
}
@@ -556,6 +562,8 @@
}
session.close();
+
+ Log.d(TAG, "destroySessionLocked: record=" + session);
setForegroundServiceAllowance(session, /* allowRunningInForeground= */ false);
mHandler.postSessionsChanged(session);
}
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 550aed5..978f468 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
@@ -214,6 +214,11 @@
}
@Override
+ public void onProcessStarted(int pid, int processUid, int packageUid,
+ String packageName, String processName) {
+ }
+
+ @Override
public void onForegroundServicesChanged(int pid, int uid, int serviceTypes) {
MediaProjectionManagerService.this.handleForegroundServicesChanged(pid, uid,
serviceTypes);
diff --git a/services/core/java/com/android/server/pm/BACKGROUND_INSTALL_OWNERS b/services/core/java/com/android/server/pm/BACKGROUND_INSTALL_OWNERS
new file mode 100644
index 0000000..baa41a5
--- /dev/null
+++ b/services/core/java/com/android/server/pm/BACKGROUND_INSTALL_OWNERS
@@ -0,0 +1,2 @@
+georgechan@google.com
+wenhaowang@google.com
\ No newline at end of file
diff --git a/services/core/java/com/android/server/pm/OWNERS b/services/core/java/com/android/server/pm/OWNERS
index 84324f2..c8bc56c 100644
--- a/services/core/java/com/android/server/pm/OWNERS
+++ b/services/core/java/com/android/server/pm/OWNERS
@@ -51,3 +51,5 @@
per-file ShortcutService.java = omakoto@google.com, yamasani@google.com, sunnygoyal@google.com, mett@google.com, pinyaoting@google.com
per-file ShortcutUser.java = omakoto@google.com, yamasani@google.com, sunnygoyal@google.com, mett@google.com, pinyaoting@google.com
+# background install control service
+per-file BackgroundInstall* = file:BACKGROUND_INSTALL_OWNERS
\ No newline at end of file
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index a97652c..7e3254d 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -3437,7 +3437,7 @@
}
str.close();
- } catch (IOException | XmlPullParserException e) {
+ } catch (IOException | XmlPullParserException | ArrayIndexOutOfBoundsException e) {
// Remove corrupted file and retry.
atomicFile.failRead(str, e);
diff --git a/services/core/java/com/android/server/pm/VerifyingSession.java b/services/core/java/com/android/server/pm/VerifyingSession.java
index f0ff85d..dd2b409 100644
--- a/services/core/java/com/android/server/pm/VerifyingSession.java
+++ b/services/core/java/com/android/server/pm/VerifyingSession.java
@@ -357,7 +357,8 @@
verifierUser = UserHandle.of(mPm.mUserManager.getCurrentUserId());
}
// TODO(b/300965895): Remove when inconsistencies loading classpaths from apex for
- // user > 1 are fixed.
+ // user > 1 are fixed. Tests should cover verifiers from apex classpaths run on
+ // primary user, secondary user and work profile.
if (pkgLite.isSdkLibrary) {
verifierUser = UserHandle.SYSTEM;
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 0abf304..1fdcc64 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -2185,6 +2185,10 @@
TalkbackShortcutController getTalkbackShortcutController() {
return new TalkbackShortcutController(mContext);
}
+
+ WindowWakeUpPolicy getWindowWakeUpPolicy() {
+ return new WindowWakeUpPolicy(mContext);
+ }
}
/** {@inheritDoc} */
@@ -2433,7 +2437,7 @@
com.android.internal.R.integer.config_keyguardDrawnTimeout);
mKeyguardDelegate = injector.getKeyguardServiceDelegate();
mTalkbackShortcutController = injector.getTalkbackShortcutController();
- mWindowWakeUpPolicy = new WindowWakeUpPolicy(mContext);
+ mWindowWakeUpPolicy = injector.getWindowWakeUpPolicy();
initKeyCombinationRules();
initSingleKeyGestureRules(injector.getLooper());
mButtonOverridePermissionChecker = injector.getButtonOverridePermissionChecker();
diff --git a/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java b/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
index d7b8495..b6d0ca1 100644
--- a/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
+++ b/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
@@ -2390,6 +2390,33 @@
}
@Override
+ public void sendCertificate(IBinder sessionToken, String host, int port,
+ Bundle certBundle, int userId) {
+ if (DEBUG) {
+ Slogf.d(TAG, "sendCertificate(host=%s port=%d cert=%s)", host, port,
+ certBundle);
+ }
+ final int callingUid = Binder.getCallingUid();
+ final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+ userId, "sendCertificate");
+ SessionState sessionState = null;
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ try {
+ sessionState = getSessionStateLocked(sessionToken, callingUid,
+ resolvedUserId);
+ getSessionLocked(sessionState).sendCertificate(host, port, certBundle);
+ } catch (RemoteException | SessionNotFoundException e) {
+ Slogf.e(TAG, "error in sendCertificate", e);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public void notifyError(IBinder sessionToken, String errMsg, Bundle params, int userId) {
if (DEBUG) {
Slogf.d(TAG, "notifyError(errMsg=%s)", errMsg);
@@ -4125,6 +4152,24 @@
}
@Override
+ public void onRequestCertificate(String host, int port) {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slogf.d(TAG, "onRequestCertificate");
+ }
+ if (mSessionState.mSession == null || mSessionState.mClient == null) {
+ return;
+ }
+ try {
+ mSessionState.mClient.onRequestCertificate(host, port, mSessionState.mSeq);
+ } catch (RemoteException e) {
+ Slogf.e(TAG, "error in onRequestCertificate", e);
+ }
+ }
+ }
+
+
+ @Override
public void onAdRequest(AdRequest request) {
synchronized (mLock) {
if (DEBUG) {
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 2d584c4..f2d9bf8 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -462,17 +462,16 @@
}
}
- // TODO(b/318327737): Remove parameter 't' when removing flag DRAW_IN_WM_LOCK.
- void drawMagnifiedRegionBorderIfNeeded(int displayId, SurfaceControl.Transaction t) {
+ void drawMagnifiedRegionBorderIfNeeded(int displayId) {
if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
mAccessibilityTracing.logTrace(
TAG + ".drawMagnifiedRegionBorderIfNeeded",
FLAGS_MAGNIFICATION_CALLBACK,
- "displayId=" + displayId + "; transaction={" + t + "}");
+ "displayId=" + displayId);
}
final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
if (displayMagnifier != null) {
- displayMagnifier.drawMagnifiedRegionBorderIfNeeded(t);
+ displayMagnifier.drawMagnifiedRegionBorderIfNeeded();
}
// Not relevant for the window observer.
}
@@ -870,12 +869,12 @@
.sendToTarget();
}
- void drawMagnifiedRegionBorderIfNeeded(SurfaceControl.Transaction t) {
+ void drawMagnifiedRegionBorderIfNeeded() {
if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
mAccessibilityTracing.logTrace(LOG_TAG + ".drawMagnifiedRegionBorderIfNeeded",
- FLAGS_MAGNIFICATION_CALLBACK, "transition={" + t + "}");
+ FLAGS_MAGNIFICATION_CALLBACK);
}
- mMagnifedViewport.drawWindowIfNeeded(t);
+ mMagnifedViewport.drawWindowIfNeeded();
}
void dump(PrintWriter pw, String prefix) {
@@ -1121,14 +1120,6 @@
}
void setMagnifiedRegionBorderShown(boolean shown, boolean animate) {
- if (ViewportWindow.DRAW_IN_WM_LOCK) {
- if (shown) {
- mFullRedrawNeeded = true;
- mOldMagnificationRegion.set(0, 0, 0, 0);
- }
- mWindow.setShown(shown, animate);
- return;
- }
if (mWindow.setShown(shown, animate)) {
mFullRedrawNeeded = true;
// Clear the old region, so recomputeBounds will refresh the current region.
@@ -1151,12 +1142,8 @@
return mMagnificationSpec;
}
- void drawWindowIfNeeded(SurfaceControl.Transaction t) {
+ void drawWindowIfNeeded() {
recomputeBounds();
- if (ViewportWindow.DRAW_IN_WM_LOCK) {
- mWindow.drawOrRemoveIfNeeded(t);
- return;
- }
mWindow.postDrawIfNeeded();
}
@@ -1187,8 +1174,6 @@
private final class ViewportWindow implements Runnable {
private static final String SURFACE_TITLE = "Magnification Overlay";
- // TODO(b/318327737): Remove if it is stable.
- static final boolean DRAW_IN_WM_LOCK = !Flags.drawMagnifierBorderOutsideWmlock();
private final Region mBounds = new Region();
private final Rect mDirtyRect = new Rect();
@@ -1328,14 +1313,14 @@
@Override
public void run() {
- drawOrRemoveIfNeeded(mTransaction);
+ drawOrRemoveIfNeeded();
}
/**
* This method must only be called by animation handler directly to make sure
* thread safe and there is no lock held outside.
*/
- private void drawOrRemoveIfNeeded(SurfaceControl.Transaction t) {
+ private void drawOrRemoveIfNeeded() {
// Drawing variables (alpha, dirty rect, and bounds) access is synchronized
// using WindowManagerGlobalLock. Grab copies of these values before
// drawing on the canvas so that drawing can be performed outside of the lock.
@@ -1343,7 +1328,7 @@
Rect drawingRect = null;
Region drawingBounds = null;
synchronized (mService.mGlobalLock) {
- if (!DRAW_IN_WM_LOCK && mBlastBufferQueue.mNativeObject == 0) {
+ if (mBlastBufferQueue.mNativeObject == 0) {
// Complete removal since releaseSurface has been called.
if (mSurface.isValid()) {
mTransaction.remove(mSurfaceControl).apply();
@@ -1388,16 +1373,8 @@
mPaint.setAlpha(alpha);
canvas.drawPath(drawingBounds.getBoundaryPath(), mPaint);
mSurface.unlockCanvasAndPost(canvas);
- if (DRAW_IN_WM_LOCK) {
- t.show(mSurfaceControl);
- return;
- }
showSurface = true;
} else {
- if (DRAW_IN_WM_LOCK) {
- t.hide(mSurfaceControl);
- return;
- }
showSurface = false;
}
@@ -1413,11 +1390,6 @@
@GuardedBy("mService.mGlobalLock")
void releaseSurface() {
mBlastBufferQueue.destroy();
- if (DRAW_IN_WM_LOCK) {
- mService.mTransactionFactory.get().remove(mSurfaceControl).apply();
- mSurface.release();
- return;
- }
// Post to perform cleanup on the thread which handles mSurface.
mService.mAnimationHandler.post(this);
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index d9fa01e..03d55d9 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -53,6 +53,7 @@
import static android.app.admin.DevicePolicyResources.Drawables.Source.PROFILE_SWITCH_ANIMATION;
import static android.app.admin.DevicePolicyResources.Drawables.Style.OUTLINE;
import static android.app.admin.DevicePolicyResources.Drawables.WORK_PROFILE_ICON;
+import static android.content.Context.CONTEXT_RESTRICTED;
import static android.content.Intent.ACTION_MAIN;
import static android.content.Intent.CATEGORY_HOME;
import static android.content.Intent.CATEGORY_LAUNCHER;
@@ -2231,7 +2232,17 @@
mOptInOnBackInvoked = WindowOnBackInvokedDispatcher
.isOnBackInvokedCallbackEnabled(info, info.applicationInfo,
- () -> ent != null ? ent.array : null, false);
+ () -> {
+ Context appContext = null;
+ try {
+ appContext = mAtmService.mContext.createPackageContextAsUser(
+ info.packageName, CONTEXT_RESTRICTED,
+ UserHandle.of(mUserId));
+ appContext.setTheme(theme);
+ } catch (PackageManager.NameNotFoundException ignore) {
+ }
+ return appContext;
+ });
}
/**
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index 9ac4a5c..4681396 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -717,31 +717,6 @@
boolean callerCanAllow = resultForCaller.allows() && !state.callerExplicitOptOut();
boolean realCallerCanAllow = resultForRealCaller.allows()
&& !state.realCallerExplicitOptOut();
- if (callerCanAllow && realCallerCanAllow) {
- // Both caller and real caller allow with system defined behavior
- if (state.mBalAllowedByPiCreatorWithHardening.allowsBackgroundActivityStarts()) {
- // Will be allowed even with BAL hardening.
- if (DEBUG_ACTIVITY_STARTS) {
- Slog.d(TAG, "Activity start allowed by caller. "
- + state.dump());
- }
- return allowBasedOnCaller(state);
- }
- if (state.mBalAllowedByPiCreator.allowsBackgroundActivityStarts()) {
- Slog.wtf(TAG,
- "With Android 15 BAL hardening this activity start may be blocked"
- + " if the PI creator upgrades target_sdk to 35+"
- + " AND the PI sender upgrades target_sdk to 34+! "
- + state.dump());
- showBalRiskToast();
- return allowBasedOnCaller(state);
- }
- Slog.wtf(TAG,
- "Without Android 15 BAL hardening this activity start would be allowed"
- + " (missing opt in by PI creator or sender)! "
- + state.dump());
- return abortLaunch(state);
- }
if (callerCanAllow) {
// Allowed before V by creator
if (state.mBalAllowedByPiCreatorWithHardening.allowsBackgroundActivityStarts()) {
@@ -753,35 +728,29 @@
return allowBasedOnCaller(state);
}
if (state.mBalAllowedByPiCreator.allowsBackgroundActivityStarts()) {
- Slog.wtf(TAG,
- "With Android 15 BAL hardening this activity start may be blocked"
+ Slog.wtf(TAG, "With Android 15 BAL hardening this activity start may be blocked"
+ " if the PI creator upgrades target_sdk to 35+! "
+ " (missing opt in by PI creator)! "
+ state.dump());
showBalRiskToast();
return allowBasedOnCaller(state);
}
- Slog.wtf(TAG,
- "Without Android 15 BAL hardening this activity start would be allowed"
- + " (missing opt in by PI creator)! "
- + state.dump());
- return abortLaunch(state);
}
if (realCallerCanAllow) {
// Allowed before U by sender
if (state.mBalAllowedByPiSender.allowsBackgroundActivityStarts()) {
- Slog.wtf(TAG,
- "With Android 14 BAL hardening this activity start will be blocked"
+ Slog.wtf(TAG, "With Android 14 BAL hardening this activity start will be blocked"
+ " if the PI sender upgrades target_sdk to 34+! "
+ " (missing opt in by PI sender)! "
+ state.dump());
showBalRiskToast();
return allowBasedOnRealCaller(state);
}
- Slog.wtf(TAG, "Without Android 14 BAL hardening this activity start would be allowed"
- + " (missing opt in by PI sender)! "
- + state.dump());
- return abortLaunch(state);
+ }
+ // caller or real caller could start the activity, but would need to explicitly opt in
+ if (callerCanAllow || realCallerCanAllow) {
+ Slog.wtf(TAG, "Without BAL hardening this activity start would be allowed "
+ + state.dump());
}
// neither the caller not the realCaller can allow or have explicitly opted out
return abortLaunch(state);
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index 10405ec..e027eb6 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -1135,16 +1135,17 @@
if (!mFreezeTaskListReordering) {
// Simple case: this is not an affiliated task, so we just move it to the
// front unless overridden by the provided activity options
+ int indexToAdd = findIndexToAdd(task);
mTasks.remove(taskIndex);
- mTasks.add(0, task);
+ mTasks.add(indexToAdd, task);
if (taskIndex != 0) {
// Only notify when position changes
mTaskNotificationController.notifyTaskListUpdated();
}
if (DEBUG_RECENTS) {
- Slog.d(TAG_RECENTS, "addRecent: moving to top " + task
- + " from " + taskIndex);
+ Slog.d(TAG_RECENTS, "addRecent: moving " + task + " to index "
+ + indexToAdd + " from " + taskIndex);
}
}
notifyTaskPersisterLocked(task, false);
@@ -1231,6 +1232,37 @@
notifyTaskPersisterLocked(task, false /* flush */);
}
+ // Looks for a new index to move the recent Task. Note that the recent Task should not be
+ // placed higher than another recent Task that has higher hierarchical z-ordering.
+ private int findIndexToAdd(Task task) {
+ int indexToAdd = 0;
+ for (int i = 0; i < mTasks.size(); i++) {
+ final Task otherTask = mTasks.get(i);
+ if (task == otherTask) {
+ break;
+ }
+
+ if (!otherTask.isAttached()) {
+ // Stop searching if not attached.
+ break;
+ }
+
+ if (otherTask.inPinnedWindowingMode()) {
+ // Skip pip task without increasing index since pip is always on screen.
+ continue;
+ }
+
+ // Stop searching if the task has higher z-ordering, or increase the index and
+ // continue the search.
+ if (task.compareTo(otherTask) > 0) {
+ break;
+ }
+
+ indexToAdd = i + 1;
+ }
+ return indexToAdd;
+ }
+
/**
* Add the task to the bottom if possible.
*/
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 20622ab..b43a454 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -150,8 +150,7 @@
dc.checkAppWindowsReadyToShow();
}
if (accessibilityController.hasCallbacks()) {
- accessibilityController.drawMagnifiedRegionBorderIfNeeded(dc.mDisplayId,
- mTransaction);
+ accessibilityController.drawMagnifiedRegionBorderIfNeeded(dc.mDisplayId);
}
if (dc.isAnimating(animationFlags, ANIMATION_TYPE_ALL)) {
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 82ff36a..cbc301b 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -306,7 +306,7 @@
void setMotionClassifierEnabled(bool enabled);
std::optional<std::string> getBluetoothAddress(int32_t deviceId);
void setStylusButtonMotionEventsEnabled(bool enabled);
- FloatPoint getMouseCursorPosition();
+ FloatPoint getMouseCursorPosition(int32_t displayId);
void setStylusPointerIconEnabled(bool enabled);
/* --- InputReaderPolicyInterface implementation --- */
@@ -1784,10 +1784,12 @@
InputReaderConfiguration::Change::STYLUS_BUTTON_REPORTING);
}
-FloatPoint NativeInputManager::getMouseCursorPosition() {
+FloatPoint NativeInputManager::getMouseCursorPosition(int32_t displayId) {
if (ENABLE_POINTER_CHOREOGRAPHER) {
- return mInputManager->getChoreographer().getMouseCursorPosition(ADISPLAY_ID_NONE);
+ return mInputManager->getChoreographer().getMouseCursorPosition(displayId);
}
+ // To maintain the status-quo, the displayId parameter (used when PointerChoreographer is
+ // enabled) is ignored in the old pipeline.
std::scoped_lock _l(mLock);
const auto pc = mLocked.legacyPointerController.lock();
if (!pc) return {AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION};
@@ -2751,9 +2753,10 @@
im->setStylusButtonMotionEventsEnabled(enabled);
}
-static jfloatArray nativeGetMouseCursorPosition(JNIEnv* env, jobject nativeImplObj) {
+static jfloatArray nativeGetMouseCursorPosition(JNIEnv* env, jobject nativeImplObj,
+ jint displayId) {
NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
- const auto p = im->getMouseCursorPosition();
+ const auto p = im->getMouseCursorPosition(displayId);
const std::array<float, 2> arr = {{p.x, p.y}};
jfloatArray outArr = env->NewFloatArray(2);
env->SetFloatArrayRegion(outArr, 0, arr.size(), arr.data());
@@ -2892,7 +2895,7 @@
{"getBluetoothAddress", "(I)Ljava/lang/String;", (void*)nativeGetBluetoothAddress},
{"setStylusButtonMotionEventsEnabled", "(Z)V",
(void*)nativeSetStylusButtonMotionEventsEnabled},
- {"getMouseCursorPosition", "()[F", (void*)nativeGetMouseCursorPosition},
+ {"getMouseCursorPosition", "(I)[F", (void*)nativeGetMouseCursorPosition},
{"setStylusPointerIconEnabled", "(Z)V", (void*)nativeSetStylusPointerIconEnabled},
{"setAccessibilityBounceKeysThreshold", "(I)V",
(void*)nativeSetAccessibilityBounceKeysThreshold},
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index e3f2c53..2b8bcc7 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -434,6 +434,9 @@
private static final String ROLE_SERVICE_CLASS = "com.android.role.RoleService";
private static final String GAME_MANAGER_SERVICE_CLASS =
"com.android.server.app.GameManagerService$Lifecycle";
+ private static final String ENHANCED_CONFIRMATION_SERVICE_CLASS =
+ "com.android.ecm.EnhancedConfirmationService";
+
private static final String UWB_APEX_SERVICE_JAR_PATH =
"/apex/com.android.uwb/javalib/service-uwb.jar";
private static final String UWB_SERVICE_CLASS = "com.android.server.uwb.UwbService";
@@ -1593,6 +1596,12 @@
mSystemServiceManager.startService(DropBoxManagerService.class);
t.traceEnd();
+ if (android.permission.flags.Flags.enhancedConfirmationModeApisEnabled()) {
+ t.traceBegin("StartEnhancedConfirmationService");
+ mSystemServiceManager.startService(ENHANCED_CONFIRMATION_SERVICE_CLASS);
+ t.traceEnd();
+ }
+
// Grants default permissions and defines roles
t.traceBegin("StartRoleManagerService");
LocalManagerRegistry.addManager(RoleServicePlatformHelper.class,
diff --git a/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt
index 24d4952..3284cf1 100644
--- a/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt
@@ -129,7 +129,10 @@
val packageState = newState.externalState.packageStates[packageName] ?: return
val androidPackage = packageState.androidPackage ?: return
val appId = packageState.appId
- val appIdPermissionFlags = newState.userStates[userId]!!.appIdDevicePermissionFlags
+ // The user may happen removed due to DeletePackageHelper.removeUnusedPackagesLPw() calling
+ // deletePackageX() asynchronously.
+ val userState = newState.userStates[userId] ?: return
+ val devicePermissionFlags = userState.appIdDevicePermissionFlags[appId] ?: return
androidPackage.requestedPermissions.forEach { permissionName ->
val isRequestedByOtherPackages =
anyPackageInAppId(appId) {
@@ -139,7 +142,7 @@
if (isRequestedByOtherPackages) {
return@forEach
}
- appIdPermissionFlags[appId]?.forEachIndexed { _, deviceId, _ ->
+ devicePermissionFlags.forEachIndexed { _, deviceId, _ ->
setPermissionFlags(appId, deviceId, userId, permissionName, 0)
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ProcessObserverTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ProcessObserverTest.java
new file mode 100644
index 0000000..fcf761f
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ProcessObserverTest.java
@@ -0,0 +1,275 @@
+/*
+ * 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.am;
+
+import static android.os.Process.myPid;
+import static android.os.Process.myUid;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.app.ActivityManagerInternal;
+import android.app.IApplicationThread;
+import android.app.IProcessObserver;
+import android.app.usage.UsageStatsManagerInternal;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManagerInternal;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.util.Log;
+
+import androidx.test.filters.MediumTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.server.DropBoxManagerInternal;
+import com.android.server.LocalServices;
+import com.android.server.am.ActivityManagerService.Injector;
+import com.android.server.appop.AppOpsService;
+import com.android.server.wm.ActivityTaskManagerInternal;
+import com.android.server.wm.ActivityTaskManagerService;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.File;
+import java.util.Arrays;
+
+
+/**
+ * Tests to verify that process events are dispatched to process observers.
+ */
+@MediumTest
+@SuppressWarnings("GuardedBy")
+public class ProcessObserverTest {
+ private static final String TAG = "ProcessObserverTest";
+
+ private static final String PACKAGE = "com.foo";
+
+ @Rule
+ public final ApplicationExitInfoTest.ServiceThreadRule
+ mServiceThreadRule = new ApplicationExitInfoTest.ServiceThreadRule();
+
+ private Context mContext;
+ private HandlerThread mHandlerThread;
+
+ @Mock
+ private AppOpsService mAppOpsService;
+ @Mock
+ private DropBoxManagerInternal mDropBoxManagerInt;
+ @Mock
+ private PackageManagerInternal mPackageManagerInt;
+ @Mock
+ private UsageStatsManagerInternal mUsageStatsManagerInt;
+ @Mock
+ private ActivityManagerInternal mActivityManagerInt;
+ @Mock
+ private ActivityTaskManagerInternal mActivityTaskManagerInt;
+ @Mock
+ private BatteryStatsService mBatteryStatsService;
+
+ private ActivityManagerService mRealAms;
+ private ActivityManagerService mAms;
+
+ private ProcessList mRealProcessList = new ProcessList();
+ private ProcessList mProcessList;
+
+ final IProcessObserver mProcessObserver = mock(IProcessObserver.Stub.class);
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+
+ mHandlerThread = new HandlerThread(TAG);
+ mHandlerThread.start();
+
+ LocalServices.removeServiceForTest(DropBoxManagerInternal.class);
+ LocalServices.addService(DropBoxManagerInternal.class, mDropBoxManagerInt);
+
+ LocalServices.removeServiceForTest(PackageManagerInternal.class);
+ LocalServices.addService(PackageManagerInternal.class, mPackageManagerInt);
+
+ LocalServices.removeServiceForTest(ActivityManagerInternal.class);
+ LocalServices.addService(ActivityManagerInternal.class, mActivityManagerInt);
+
+ LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class);
+ LocalServices.addService(ActivityTaskManagerInternal.class, mActivityTaskManagerInt);
+
+ doReturn(new ComponentName("", "")).when(mPackageManagerInt).getSystemUiServiceComponent();
+ doReturn(true).when(mActivityTaskManagerInt).attachApplication(any());
+ doNothing().when(mActivityTaskManagerInt).onProcessMapped(anyInt(), any());
+
+ mRealAms = new ActivityManagerService(
+ new TestInjector(mContext), mServiceThreadRule.getThread());
+ mRealAms.mConstants.loadDeviceConfigConstants();
+ mRealAms.mActivityTaskManager = new ActivityTaskManagerService(mContext);
+ mRealAms.mActivityTaskManager.initialize(null, null, mContext.getMainLooper());
+ mRealAms.mAtmInternal = mActivityTaskManagerInt;
+ mRealAms.mPackageManagerInt = mPackageManagerInt;
+ mRealAms.mUsageStatsService = mUsageStatsManagerInt;
+ mRealAms.mProcessesReady = true;
+ mAms = spy(mRealAms);
+ mRealProcessList.mService = mAms;
+ mProcessList = spy(mRealProcessList);
+
+ doReturn(mProcessObserver).when(mProcessObserver).asBinder();
+ mProcessList.registerProcessObserver(mProcessObserver);
+
+ doAnswer((invocation) -> {
+ Log.v(TAG, "Intercepting isProcStartValidLocked() for "
+ + Arrays.toString(invocation.getArguments()));
+ return null;
+ }).when(mProcessList).isProcStartValidLocked(any(), anyLong());
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mHandlerThread.quit();
+ }
+
+ private class TestInjector extends Injector {
+ TestInjector(Context context) {
+ super(context);
+ }
+
+ @Override
+ public AppOpsService getAppOpsService(File recentAccessesFile, File storageFile,
+ Handler handler) {
+ return mAppOpsService;
+ }
+
+ @Override
+ public Handler getUiHandler(ActivityManagerService service) {
+ return mHandlerThread.getThreadHandler();
+ }
+
+ @Override
+ public ProcessList getProcessList(ActivityManagerService service) {
+ return mRealProcessList;
+ }
+
+ @Override
+ public BatteryStatsService getBatteryStatsService() {
+ return mBatteryStatsService;
+ }
+ }
+
+ private ProcessRecord makeActiveProcessRecord(String packageName)
+ throws Exception {
+ final ApplicationInfo ai = makeApplicationInfo(packageName);
+ return makeActiveProcessRecord(ai);
+ }
+
+ private ProcessRecord makeActiveProcessRecord(ApplicationInfo ai)
+ throws Exception {
+ final IApplicationThread thread = mock(IApplicationThread.class);
+ final IBinder threadBinder = new Binder();
+ doReturn(threadBinder).when(thread).asBinder();
+ doAnswer((invocation) -> {
+ Log.v(TAG, "Intercepting bindApplication() for "
+ + Arrays.toString(invocation.getArguments()));
+ if (mRealAms.mConstants.mEnableWaitForFinishAttachApplication) {
+ mRealAms.finishAttachApplication(0);
+ }
+ return null;
+ }).when(thread).bindApplication(
+ any(), any(),
+ any(), any(), anyBoolean(),
+ any(), any(),
+ any(), any(),
+ any(),
+ any(), anyInt(),
+ anyBoolean(), anyBoolean(),
+ anyBoolean(), anyBoolean(), any(),
+ any(), any(), any(),
+ any(), any(),
+ any(), any(),
+ any(),
+ anyLong(), anyLong());
+ final ProcessRecord r = spy(new ProcessRecord(mAms, ai, ai.processName, ai.uid));
+ r.setPid(myPid());
+ r.setStartUid(myUid());
+ r.setHostingRecord(new HostingRecord(HostingRecord.HOSTING_TYPE_BROADCAST));
+ r.makeActive(thread, mAms.mProcessStats);
+ doNothing().when(r).killLocked(any(), any(), anyInt(), anyInt(), anyBoolean(),
+ anyBoolean());
+ return r;
+ }
+
+ static ApplicationInfo makeApplicationInfo(String packageName) {
+ final ApplicationInfo ai = new ApplicationInfo();
+ ai.packageName = packageName;
+ ai.processName = packageName;
+ ai.uid = myUid();
+ return ai;
+ }
+
+ /**
+ * Verify that a process start event is dispatched to process observers.
+ */
+ @Test
+ public void testNormal() throws Exception {
+ ProcessRecord app = startProcess();
+ verify(mProcessObserver).onProcessStarted(
+ app.getPid(), app.uid, app.info.uid, PACKAGE, PACKAGE);
+ }
+
+ private ProcessRecord startProcess() throws Exception {
+ final ProcessRecord app = makeActiveProcessRecord(PACKAGE);
+ final ApplicationInfo appInfo = makeApplicationInfo(PACKAGE);
+ mProcessList.handleProcessStartedLocked(app, app.getPid(), /* usingWrapper */ false,
+ /* expectedStartSeq */ 0, /* procAttached */ false);
+ app.getThread().bindApplication(PACKAGE, appInfo,
+ null, null, false,
+ null,
+ null,
+ null, null,
+ null,
+ null, 0,
+ false, false,
+ true, false,
+ null,
+ null, null,
+ null,
+ null, null, null,
+ null, null,
+ 0, 0);
+ return app;
+ }
+
+ // TODO: [b/302724778] Remove manual JNI load
+ static {
+ System.loadLibrary("mockingservicestestjni");
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/StationaryThrottlingLocationProviderTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/StationaryThrottlingLocationProviderTest.java
index 4eba219..efab19c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/StationaryThrottlingLocationProviderTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/StationaryThrottlingLocationProviderTest.java
@@ -29,6 +29,7 @@
import android.content.Context;
import android.location.Location;
+import android.location.LocationRequest;
import android.location.LocationResult;
import android.location.provider.ProviderRequest;
import android.platform.test.annotations.Presubmit;
@@ -218,4 +219,22 @@
verify(mDelegate, never()).onSetRequest(ProviderRequest.EMPTY_REQUEST);
verify(mListener, after(75).times(1)).onReportLocation(any(LocationResult.class));
}
+
+ @Test
+ public void testNoThrottle_highAccuracy() {
+ ProviderRequest request = new ProviderRequest.Builder().setIntervalMillis(
+ 50).setQuality(LocationRequest.QUALITY_HIGH_ACCURACY).build();
+
+ mProvider.getController().setRequest(request);
+ verify(mDelegate).onSetRequest(request);
+
+ LocationResult loc = createLocationResult("test_provider", mRandom);
+ mDelegateProvider.reportLocation(loc);
+ verify(mListener, times(1)).onReportLocation(loc);
+
+ mInjector.getDeviceStationaryHelper().setStationary(true);
+ mInjector.getDeviceIdleHelper().setIdle(true);
+ verify(mDelegate, never()).onSetRequest(ProviderRequest.EMPTY_REQUEST);
+ verify(mListener, after(75).times(1)).onReportLocation(any(LocationResult.class));
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java
index ccbbaa5..5943832 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java
@@ -33,19 +33,21 @@
import android.os.Handler;
import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
-import android.view.Display;
import android.view.DisplayInfo;
import android.view.WindowManager;
import androidx.test.InstrumentationRegistry;
+import com.android.input.flags.Flags;
import com.android.server.LocalServices;
import com.android.server.input.InputManagerInternal;
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -58,6 +60,9 @@
private static final String LANGUAGE_TAG = "en-US";
private static final String LAYOUT_TYPE = "qwerty";
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
@Mock
private InputManagerInternal mInputManagerInternalMock;
@Mock
@@ -72,11 +77,12 @@
@Before
public void setUp() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_POINTER_CHOREOGRAPHER);
+
MockitoAnnotations.initMocks(this);
mInputManagerMockHelper = new InputManagerMockHelper(
TestableLooper.get(this), mNativeWrapperMock, mIInputManagerMock);
- doReturn(true).when(mInputManagerInternalMock).setVirtualMousePointerDisplayId(anyInt());
LocalServices.removeServiceForTest(InputManagerInternal.class);
LocalServices.addService(InputManagerInternal.class, mInputManagerInternalMock);
@@ -129,11 +135,7 @@
mInputController.createMouse("name", /*vendorId= */ 1, /*productId= */ 1, deviceToken,
/* displayId= */ 1);
verify(mNativeWrapperMock).openUinputMouse(eq("name"), eq(1), eq(1), anyString());
- verify(mInputManagerInternalMock).setVirtualMousePointerDisplayId(eq(1));
- doReturn(1).when(mInputManagerInternalMock).getVirtualMousePointerDisplayId();
mInputController.unregisterInputDevice(deviceToken);
- verify(mInputManagerInternalMock).setVirtualMousePointerDisplayId(
- eq(Display.INVALID_DISPLAY));
}
@Test
@@ -143,14 +145,11 @@
mInputController.createMouse("mouse1", /*vendorId= */ 1, /*productId= */ 1, deviceToken,
/* displayId= */ 1);
verify(mNativeWrapperMock).openUinputMouse(eq("mouse1"), eq(1), eq(1), anyString());
- verify(mInputManagerInternalMock).setVirtualMousePointerDisplayId(eq(1));
final IBinder deviceToken2 = new Binder();
mInputController.createMouse("mouse2", /*vendorId= */ 1, /*productId= */ 1, deviceToken2,
/* displayId= */ 2);
verify(mNativeWrapperMock).openUinputMouse(eq("mouse2"), eq(1), eq(1), anyString());
- verify(mInputManagerInternalMock).setVirtualMousePointerDisplayId(eq(2));
mInputController.unregisterInputDevice(deviceToken);
- verify(mInputManagerInternalMock).setVirtualMousePointerDisplayId(eq(1));
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index 9ff29d2..5442af8 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -339,8 +339,8 @@
LocalServices.addService(DisplayManagerInternal.class, mDisplayManagerInternalMock);
mSetFlagsRule.initAllFlagsToReleaseConfigDefault();
+ mSetFlagsRule.enableFlags(com.android.input.flags.Flags.FLAG_ENABLE_POINTER_CHOREOGRAPHER);
- doReturn(true).when(mInputManagerInternalMock).setVirtualMousePointerDisplayId(anyInt());
doNothing().when(mInputManagerInternalMock)
.setMousePointerAccelerationEnabled(anyBoolean(), anyInt());
doNothing().when(mInputManagerInternalMock).setPointerIconVisible(anyBoolean(), anyInt());
@@ -1333,7 +1333,6 @@
mInputController.addDeviceForTesting(BINDER, fd,
InputController.InputDeviceDescriptor.TYPE_MOUSE, DISPLAY_ID_1, PHYS,
DEVICE_NAME_1, INPUT_DEVICE_ID);
- doReturn(DISPLAY_ID_1).when(mInputManagerInternalMock).getVirtualMousePointerDisplayId();
assertThat(mDeviceImpl.sendButtonEvent(BINDER,
new VirtualMouseButtonEvent.Builder()
.setButtonCode(buttonCode)
@@ -1363,7 +1362,6 @@
mInputController.addDeviceForTesting(BINDER, fd,
InputController.InputDeviceDescriptor.TYPE_MOUSE, DISPLAY_ID_1, PHYS, DEVICE_NAME_1,
INPUT_DEVICE_ID);
- doReturn(DISPLAY_ID_1).when(mInputManagerInternalMock).getVirtualMousePointerDisplayId();
assertThat(mDeviceImpl.sendRelativeEvent(BINDER,
new VirtualMouseRelativeEvent.Builder()
.setRelativeX(x)
@@ -1394,7 +1392,6 @@
mInputController.addDeviceForTesting(BINDER, fd,
InputController.InputDeviceDescriptor.TYPE_MOUSE, DISPLAY_ID_1, PHYS, DEVICE_NAME_1,
INPUT_DEVICE_ID);
- doReturn(DISPLAY_ID_1).when(mInputManagerInternalMock).getVirtualMousePointerDisplayId();
assertThat(mDeviceImpl.sendScrollEvent(BINDER,
new VirtualMouseScrollEvent.Builder()
.setXAxisMovement(x)
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java
index 3e4f1df..81981e6 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java
@@ -183,9 +183,8 @@
private VirtualCameraConfig createVirtualCameraConfig(
int width, int height, int format, int maximumFramesPerSecond,
String name, int sensorOrientation, int lensFacing) {
- return new VirtualCameraConfig.Builder()
+ return new VirtualCameraConfig.Builder(name)
.addStreamConfig(width, height, format, maximumFramesPerSecond)
- .setName(name)
.setVirtualCameraCallback(mCallbackHandler, mVirtualCameraCallbackMock)
.setSensorOrientation(sensorOrientation)
.setLensFacing(lensFacing)
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 7c2f7ee..c8abd8d 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -166,6 +166,7 @@
@Mock
private PhoneWindowManager.ButtonOverridePermissionChecker mButtonOverridePermissionChecker;
+ @Mock private WindowWakeUpPolicy mWindowWakeUpPolicy;
@Mock private IBinder mInputToken;
@Mock private IBinder mImeTargetWindowToken;
@@ -230,6 +231,10 @@
TalkbackShortcutController getTalkbackShortcutController() {
return new TestTalkbackShortcutController(mContext);
}
+
+ WindowWakeUpPolicy getWindowWakeUpPolicy() {
+ return mWindowWakeUpPolicy;
+ }
}
TestPhoneWindowManager(Context context, boolean supportSettingsUpdate) {
@@ -620,7 +625,8 @@
void assertPowerWakeUp() {
mTestLooper.dispatchAll();
- verify(mPowerManager).wakeUp(anyLong(), anyInt(), anyString());
+ verify(mWindowWakeUpPolicy)
+ .wakeUpFromKey(anyLong(), eq(KeyEvent.KEYCODE_POWER), anyBoolean());
}
void assertNoPowerSleep() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index fe9d837..06afa38 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -1068,7 +1068,7 @@
invocationOnMock.callRealMethod();
return null;
}).when(surface).lockCanvas(any());
- mWm.mAccessibilityController.drawMagnifiedRegionBorderIfNeeded(displayId, mTransaction);
+ mWm.mAccessibilityController.drawMagnifiedRegionBorderIfNeeded(displayId);
waitUntilHandlersIdle();
try {
verify(surface).lockCanvas(any());
@@ -1076,14 +1076,9 @@
clearInvocations(surface);
// Invalidate and redraw.
mWm.mAccessibilityController.onDisplaySizeChanged(mDisplayContent);
- mWm.mAccessibilityController.drawMagnifiedRegionBorderIfNeeded(displayId, mTransaction);
+ mWm.mAccessibilityController.drawMagnifiedRegionBorderIfNeeded(displayId);
// Turn off magnification to release surface.
mWm.mAccessibilityController.setMagnificationCallbacks(displayId, null);
- if (!com.android.window.flags.Flags.drawMagnifierBorderOutsideWmlock()) {
- verify(surface).release();
- assertTrue(lockCanvasInWmLock[0]);
- return;
- }
waitUntilHandlersIdle();
// lockCanvas must not be called after releasing.
verify(surface, never()).lockCanvas(any());
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index e81f482..2c6e1e4 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -1360,7 +1360,7 @@
/**
* Returns a list of {@link PhoneAccountHandle}s which can be used to make and receive phone
* calls. The returned list includes those accounts which have been explicitly enabled by
- * the user or other users visible to the user.
+ * the user or enabled by other users but visible to the user.
*
* @see #EXTRA_PHONE_ACCOUNT_HANDLE
* @return A list of {@code PhoneAccountHandle} objects.
@@ -1486,7 +1486,7 @@
return service.getCallCapablePhoneAccounts(includeDisabledAccounts,
mContext.getOpPackageName(), mContext.getAttributionTag(), true).getList();
} catch (RemoteException e) {
- throw e.rethrowAsRuntimeException();
+ throw e.rethrowFromSystemServer();
}
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index cbd5524..c1ceaef 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -25,6 +25,7 @@
import android.Manifest;
import android.annotation.BytesLong;
import android.annotation.CallbackExecutor;
+import android.annotation.CurrentTimeMillisLong;
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.LongDef;
@@ -18714,51 +18715,93 @@
* call diagnostic data
* @hide
*/
- public static class EmergencyCallDiagnosticParams {
+ @SystemApi
+ @FlaggedApi(com.android.server.telecom.flags.Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES)
+ public static final class EmergencyCallDiagnosticParams {
+ public static final class Builder {
+ private boolean mCollectTelecomDumpSys;
+ private boolean mCollectTelephonyDumpsys;
- private boolean mCollectTelecomDumpSys;
- private boolean mCollectTelephonyDumpsys;
- private boolean mCollectLogcat;
+ // If this is set to a value other than -1L, then the logcat collection is enabled.
+ // Logcat lines with this time or greater are collected how much is collected is
+ // dependent on internal implementation. Time represented as milliseconds since boot.
+ private long mLogcatStartTimeMillis = sUnsetLogcatStartTime;
- //logcat lines with this time or greater are collected
- //how much is collected is dependent on internal implementation.
- //Time represented as milliseconds since January 1, 1970 UTC
+ /**
+ * Allows enabling of telecom dumpsys collection.
+ * @param collectTelecomDumpsys Determines whether telecom dumpsys should be collected.
+ * @return Builder instance corresponding to the configured call diagnostic params.
+ */
+ public @NonNull Builder setTelecomDumpSysCollectionEnabled(
+ boolean collectTelecomDumpsys) {
+ mCollectTelecomDumpSys = collectTelecomDumpsys;
+ return this;
+ }
+
+ /**
+ * Allows enabling of telephony dumpsys collection.
+ * @param collectTelephonyDumpsys Determines if telephony dumpsys should be collected.
+ * @return Builder instance corresponding to the configured call diagnostic params.
+ */
+ public @NonNull Builder setTelephonyDumpSysCollectionEnabled(
+ boolean collectTelephonyDumpsys) {
+ mCollectTelephonyDumpsys = collectTelephonyDumpsys;
+ return this;
+ }
+
+ /**
+ * Allows enabling of logcat (system,radio) collection.
+ * @param startTimeMillis Enables logcat collection as of the indicated timestamp.
+ * @return Builder instance corresponding to the configured call diagnostic params.
+ */
+ public @NonNull Builder setLogcatCollectionStartTimeMillis(
+ @CurrentTimeMillisLong long startTimeMillis) {
+ mLogcatStartTimeMillis = startTimeMillis;
+ return this;
+ }
+
+ /**
+ * Build the EmergencyCallDiagnosticParams from the provided Builder config.
+ * @return {@link EmergencyCallDiagnosticParams} instance from provided builder.
+ */
+ public @NonNull EmergencyCallDiagnosticParams build() {
+ return new EmergencyCallDiagnosticParams(mCollectTelecomDumpSys,
+ mCollectTelephonyDumpsys, mLogcatStartTimeMillis);
+ }
+ }
+
+ private boolean mCollectTelecomDumpSys;
+ private boolean mCollectTelephonyDumpsys;
+ private boolean mCollectLogcat;
private long mLogcatStartTimeMillis;
+ private static long sUnsetLogcatStartTime = -1L;
+
+ private EmergencyCallDiagnosticParams(boolean collectTelecomDumpSys,
+ boolean collectTelephonyDumpsys, long logcatStartTimeMillis) {
+ mCollectTelecomDumpSys = collectTelecomDumpSys;
+ mCollectTelephonyDumpsys = collectTelephonyDumpsys;
+ mLogcatStartTimeMillis = logcatStartTimeMillis;
+ mCollectLogcat = logcatStartTimeMillis != sUnsetLogcatStartTime;
+ }
public boolean isTelecomDumpSysCollectionEnabled() {
return mCollectTelecomDumpSys;
}
- public void setTelecomDumpSysCollection(boolean collectTelecomDumpSys) {
- mCollectTelecomDumpSys = collectTelecomDumpSys;
- }
-
public boolean isTelephonyDumpSysCollectionEnabled() {
return mCollectTelephonyDumpsys;
}
- public void setTelephonyDumpSysCollection(boolean collectTelephonyDumpsys) {
- mCollectTelephonyDumpsys = collectTelephonyDumpsys;
- }
-
public boolean isLogcatCollectionEnabled() {
return mCollectLogcat;
}
- public long getLogcatStartTime()
+ public long getLogcatCollectionStartTimeMillis()
{
return mLogcatStartTimeMillis;
}
- public void setLogcatCollection(boolean collectLogcat, long startTimeMillis) {
- mCollectLogcat = collectLogcat;
- if(mCollectLogcat)
- {
- mLogcatStartTimeMillis = startTimeMillis;
- }
- }
-
@Override
public String toString() {
return "EmergencyCallDiagnosticParams{" +
@@ -18774,11 +18817,12 @@
* Request telephony to persist state for debugging emergency call failures.
*
* @param dropboxTag Tag to use when persisting data to dropbox service.
- *
- * @see params Parameters controlling what is collected
+ * @param params Parameters controlling what is collected.
*
* @hide
*/
+ @SystemApi
+ @FlaggedApi(com.android.server.telecom.flags.Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES)
@RequiresPermission(android.Manifest.permission.DUMP)
public void persistEmergencyCallDiagnosticData(@NonNull String dropboxTag,
@NonNull EmergencyCallDiagnosticParams params) {
@@ -18791,7 +18835,7 @@
if (telephony != null) {
telephony.persistEmergencyCallDiagnosticData(dropboxTag,
params.isLogcatCollectionEnabled(),
- params.getLogcatStartTime(),
+ params.getLogcatCollectionStartTimeMillis(),
params.isTelecomDumpSysCollectionEnabled(),
params.isTelephonyDumpSysCollectionEnabled());
}
diff --git a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
index 256a469..566e51a 100644
--- a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
+++ b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
@@ -24,6 +24,7 @@
import android.hardware.input.InputManagerGlobal
import android.os.test.TestLooper
import android.platform.test.annotations.Presubmit
+import android.platform.test.flag.junit.SetFlagsRule
import android.provider.Settings
import android.test.mock.MockContentResolver
import android.view.Display
@@ -72,6 +73,9 @@
@get:Rule
val fakeSettingsProviderRule = FakeSettingsProvider.rule()!!
+ @get:Rule
+ val setFlagsRule = SetFlagsRule()
+
@Mock
private lateinit var native: NativeInputManagerService
@@ -170,6 +174,8 @@
@Test
fun testSetVirtualMousePointerDisplayId() {
+ setFlagsRule.disableFlags(com.android.input.flags.Flags.FLAG_ENABLE_POINTER_CHOREOGRAPHER)
+
// Set the virtual mouse pointer displayId, and ensure that the calling thread is blocked
// until the native callback happens.
var countDownLatch = CountDownLatch(1)
@@ -221,6 +227,8 @@
@Test
fun testSetVirtualMousePointerDisplayId_unsuccessfulUpdate() {
+ setFlagsRule.disableFlags(com.android.input.flags.Flags.FLAG_ENABLE_POINTER_CHOREOGRAPHER)
+
// Set the virtual mouse pointer displayId, and ensure that the calling thread is blocked
// until the native callback happens.
val countDownLatch = CountDownLatch(1)
@@ -246,6 +254,8 @@
@Test
fun testSetVirtualMousePointerDisplayId_competingRequests() {
+ setFlagsRule.disableFlags(com.android.input.flags.Flags.FLAG_ENABLE_POINTER_CHOREOGRAPHER)
+
val firstRequestSyncLatch = CountDownLatch(1)
doAnswer {
firstRequestSyncLatch.countDown()
@@ -289,6 +299,8 @@
@Test
fun onDisplayRemoved_resetAllAdditionalInputProperties() {
+ setFlagsRule.disableFlags(com.android.input.flags.Flags.FLAG_ENABLE_POINTER_CHOREOGRAPHER)
+
setVirtualMousePointerDisplayIdAndVerify(10)
localService.setPointerIconVisible(false, 10)
@@ -313,6 +325,8 @@
@Test
fun updateAdditionalInputPropertiesForOverrideDisplay() {
+ setFlagsRule.disableFlags(com.android.input.flags.Flags.FLAG_ENABLE_POINTER_CHOREOGRAPHER)
+
setVirtualMousePointerDisplayIdAndVerify(10)
localService.setPointerIconVisible(false, 10)
@@ -341,6 +355,8 @@
@Test
fun setAdditionalInputPropertiesBeforeOverride() {
+ setFlagsRule.disableFlags(com.android.input.flags.Flags.FLAG_ENABLE_POINTER_CHOREOGRAPHER)
+
localService.setPointerIconVisible(false, 10)
localService.setMousePointerAccelerationEnabled(false, 10)