Merge "[automerger skipped] Merge "Rescind BAL privilege when ShortcutService sends the callback PI" into udc-dev am: 4c452c359c am: c176df687a -s ours" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 5cfdeb9..6b5554b 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -18,8 +18,11 @@
// Add java_aconfig_libraries to here to add them to the core framework
srcs: [
+ ":android.app.usage.flags-aconfig-java{.generated_srcjars}",
":android.os.flags-aconfig-java{.generated_srcjars}",
+ ":android.os.vibrator.flags-aconfig-java{.generated_srcjars}",
":android.security.flags-aconfig-java{.generated_srcjars}",
+ ":android.media.flags-aconfig-java{.generated_srcjars}",
":camera_platform_flags_core_java_lib{.generated_srcjars}",
":com.android.window.flags.window-aconfig-java{.generated_srcjars}",
":com.android.text.flags-aconfig-java{.generated_srcjars}",
@@ -79,6 +82,11 @@
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+cc_aconfig_library {
+ name: "aconfig_text_flags_c_lib",
+ aconfig_declarations: "com.android.text.flags-aconfig",
+}
+
// Security
aconfig_declarations {
name: "android.security.flags-aconfig",
@@ -100,6 +108,19 @@
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+// UsageStats
+aconfig_declarations {
+ name: "android.app.usage.flags-aconfig",
+ package: "android.app.usage",
+ srcs: ["core/java/android/app/usage/*.aconfig"],
+}
+
+java_aconfig_library {
+ name: "android.app.usage.flags-aconfig-java",
+ aconfig_declarations: "android.app.usage.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
// OS
aconfig_declarations {
name: "android.os.flags-aconfig",
@@ -138,3 +159,29 @@
aconfig_declarations: "android.view.inputmethod.flags-aconfig",
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+
+// Vibrator
+aconfig_declarations {
+ name: "android.os.vibrator.flags-aconfig",
+ package: "android.os.vibrator",
+ srcs: ["core/java/android/os/vibrator/*.aconfig"],
+}
+
+java_aconfig_library {
+ name: "android.os.vibrator.flags-aconfig-java",
+ aconfig_declarations: "android.os.vibrator.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
+// Media
+aconfig_declarations {
+ name: "android.media.flags-aconfig",
+ package: "android.media",
+ srcs: ["media/java/android/media/*.aconfig"],
+}
+
+java_aconfig_library {
+ name: "android.media.flags-aconfig-java",
+ aconfig_declarations: "android.media.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
diff --git a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
index 17076bc..66c1efc 100644
--- a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
+++ b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
@@ -419,6 +419,14 @@
*/
public static final int REASON_SYSTEM_EXEMPT_APP_OP = 327;
+ /**
+ * Granted by {@link com.android.server.pm.PackageArchiverService} to the installer responsible
+ * for unarchiving an app.
+ *
+ * @hide
+ */
+ public static final int REASON_PACKAGE_UNARCHIVE = 328;
+
/** @hide The app requests out-out. */
public static final int REASON_OPT_OUT_REQUESTED = 1000;
@@ -502,6 +510,7 @@
REASON_ACTIVE_DEVICE_ADMIN,
REASON_MEDIA_NOTIFICATION_TRANSFER,
REASON_PACKAGE_INSTALLER,
+ REASON_PACKAGE_UNARCHIVE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ReasonCode {}
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 2fa31ed..0c61981 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -3213,6 +3213,7 @@
method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void registerIntentInterceptor(@NonNull android.content.IntentFilter, @NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.VirtualDeviceManager.IntentInterceptorCallback);
method public void removeActivityListener(@NonNull android.companion.virtual.VirtualDeviceManager.ActivityListener);
method public void removeSoundEffectListener(@NonNull android.companion.virtual.VirtualDeviceManager.SoundEffectListener);
+ method @FlaggedApi(Flags.FLAG_DYNAMIC_POLICY) @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void setDevicePolicy(int, int);
method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void setShowPointerIcon(boolean);
method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void unregisterIntentInterceptor(@NonNull android.companion.virtual.VirtualDeviceManager.IntentInterceptorCallback);
}
@@ -3546,6 +3547,7 @@
field public static final String ACTION_SHOW_SUSPENDED_APP_DETAILS = "android.intent.action.SHOW_SUSPENDED_APP_DETAILS";
field @Deprecated public static final String ACTION_SIM_STATE_CHANGED = "android.intent.action.SIM_STATE_CHANGED";
field public static final String ACTION_SPLIT_CONFIGURATION_CHANGED = "android.intent.action.SPLIT_CONFIGURATION_CHANGED";
+ field public static final String ACTION_UNARCHIVE_PACKAGE = "android.intent.action.UNARCHIVE_PACKAGE";
field public static final String ACTION_UPGRADE_SETUP = "android.intent.action.UPGRADE_SETUP";
field public static final String ACTION_USER_ADDED = "android.intent.action.USER_ADDED";
field public static final String ACTION_USER_REMOVED = "android.intent.action.USER_REMOVED";
@@ -3795,6 +3797,9 @@
public class PackageArchiver {
method @RequiresPermission(anyOf={android.Manifest.permission.DELETE_PACKAGES, android.Manifest.permission.REQUEST_DELETE_PACKAGES}) public void requestArchive(@NonNull String, @NonNull android.content.IntentSender) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @RequiresPermission(anyOf={android.Manifest.permission.INSTALL_PACKAGES, android.Manifest.permission.REQUEST_INSTALL_PACKAGES}) public void requestUnarchive(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
+ field public static final String EXTRA_UNARCHIVE_ALL_USERS = "android.content.pm.extra.UNARCHIVE_ALL_USERS";
+ field public static final String EXTRA_UNARCHIVE_PACKAGE_NAME = "android.content.pm.extra.UNARCHIVE_PACKAGE_NAME";
}
public class PackageInfo implements android.os.Parcelable {
diff --git a/core/java/android/app/IUriGrantsManager.aidl b/core/java/android/app/IUriGrantsManager.aidl
index 9e7f2fe..b630d03 100644
--- a/core/java/android/app/IUriGrantsManager.aidl
+++ b/core/java/android/app/IUriGrantsManager.aidl
@@ -39,4 +39,7 @@
void clearGrantedUriPermissions(in String packageName, int userId);
ParceledListSlice getUriPermissions(in String packageName, boolean incoming,
boolean persistedOnly);
+
+ int checkGrantUriPermission_ignoreNonSystem(
+ int sourceUid, String targetPkg, in Uri uri, int modeFlags, int userId);
}
diff --git a/core/java/android/app/usage/flags.aconfig b/core/java/android/app/usage/flags.aconfig
new file mode 100644
index 0000000..afe87de
--- /dev/null
+++ b/core/java/android/app/usage/flags.aconfig
@@ -0,0 +1,9 @@
+package: "android.app.usage"
+
+flag {
+ name: "user_interaction_type_api"
+ namespace: "power_optimization"
+ description: "Feature flag for user interaction event report/query API"
+ bug: "296061232"
+}
+
diff --git a/core/java/android/companion/virtual/IVirtualDevice.aidl b/core/java/android/companion/virtual/IVirtualDevice.aidl
index 4801d15..be699f4 100644
--- a/core/java/android/companion/virtual/IVirtualDevice.aidl
+++ b/core/java/android/companion/virtual/IVirtualDevice.aidl
@@ -70,6 +70,12 @@
void close();
/**
+ * Specifies a policy for this virtual device.
+ */
+ @EnforcePermission("CREATE_VIRTUAL_DEVICE")
+ void setDevicePolicy(int policyType, int devicePolicy);
+
+ /**
* Notifies that an audio session being started.
*/
@EnforcePermission("CREATE_VIRTUAL_DEVICE")
diff --git a/core/java/android/companion/virtual/VirtualDeviceInternal.java b/core/java/android/companion/virtual/VirtualDeviceInternal.java
index d13bfd4..2e5c0f7 100644
--- a/core/java/android/companion/virtual/VirtualDeviceInternal.java
+++ b/core/java/android/companion/virtual/VirtualDeviceInternal.java
@@ -238,6 +238,15 @@
}
}
+ void setDevicePolicy(@VirtualDeviceParams.DynamicPolicyType int policyType,
+ @VirtualDeviceParams.DevicePolicy int devicePolicy) {
+ try {
+ mVirtualDevice.setDevicePolicy(policyType, devicePolicy);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
@NonNull
VirtualDpad createVirtualDpad(@NonNull VirtualDpadConfig config) {
try {
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index af41370..923e689 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -19,6 +19,7 @@
import static android.media.AudioManager.AUDIO_SESSION_ID_GENERATE;
import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -59,6 +60,8 @@
import android.util.Log;
import android.view.Surface;
+import com.android.internal.util.AnnotationValidations;
+
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -513,6 +516,28 @@
}
/**
+ * Specifies a policy for this virtual device.
+ *
+ * <p>Policies define the system behavior that may be specific for this virtual device. The
+ * given policy must be able to be changed dynamically during the lifetime of the device.
+ *
+ * @param policyType the type of policy, i.e. which behavior to specify a policy for.
+ * @param devicePolicy the value of the policy, i.e. how to interpret the device behavior.
+ *
+ * @see VirtualDeviceParams#POLICY_TYPE_RECENTS
+ */
+ @FlaggedApi(Flags.FLAG_DYNAMIC_POLICY)
+ @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+ public void setDevicePolicy(@VirtualDeviceParams.DynamicPolicyType int policyType,
+ @VirtualDeviceParams.DevicePolicy int devicePolicy) {
+ AnnotationValidations.validate(
+ VirtualDeviceParams.DynamicPolicyType.class, null, policyType);
+ AnnotationValidations.validate(
+ VirtualDeviceParams.DevicePolicy.class, null, devicePolicy);
+ mVirtualDeviceInternal.setDevicePolicy(policyType, devicePolicy);
+ }
+
+ /**
* Creates a virtual dpad.
*
* @param config the configurations of the virtual dpad.
diff --git a/core/java/android/companion/virtual/VirtualDeviceParams.java b/core/java/android/companion/virtual/VirtualDeviceParams.java
index b6d8375..037e814a 100644
--- a/core/java/android/companion/virtual/VirtualDeviceParams.java
+++ b/core/java/android/companion/virtual/VirtualDeviceParams.java
@@ -150,6 +150,17 @@
public @interface PolicyType {}
/**
+ * Policy types that can be dynamically changed during the virtual device's lifetime.
+ *
+ * @see VirtualDeviceManager.VirtualDevice#setDevicePolicy
+ * @hide
+ */
+ @IntDef(prefix = "POLICY_TYPE_", value = {POLICY_TYPE_RECENTS})
+ @Retention(RetentionPolicy.SOURCE)
+ @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
+ public @interface DynamicPolicyType {}
+
+ /**
* Tells the sensor framework how to handle sensor requests from contexts associated with this
* virtual device, namely the sensors returned by
* {@link android.hardware.SensorManager#getSensorList}:
@@ -375,6 +386,14 @@
}
/**
+ * Returns all device policies.
+ * @hide
+ */
+ public @NonNull SparseIntArray getDevicePolicies() {
+ return mDevicePolicies;
+ }
+
+ /**
* Returns the configurations for all sensors that should be created for this device.
*
* @see Builder#addVirtualSensorConfig
diff --git a/core/java/android/companion/virtual/flags.aconfig b/core/java/android/companion/virtual/flags.aconfig
index 39b99c6..057b856 100644
--- a/core/java/android/companion/virtual/flags.aconfig
+++ b/core/java/android/companion/virtual/flags.aconfig
@@ -6,3 +6,10 @@
description: "More logs to test flags with"
bug: "291725823"
}
+
+flag {
+ name: "dynamic_policy"
+ namespace: "virtual_devices"
+ description: "Enable dynamic policy API"
+ bug: "298401780"
+}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 31f6418..fe7d1e6 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -5270,6 +5270,16 @@
public static final String ACTION_SHOW_FOREGROUND_SERVICE_MANAGER =
"android.intent.action.SHOW_FOREGROUND_SERVICE_MANAGER";
+ /**
+ * Broadcast Action: Sent to the responsible installer of an archived package when unarchival
+ * is requested.
+ *
+ * @see android.content.pm.PackageArchiver
+ * @hide
+ */
+ @SystemApi
+ public static final String ACTION_UNARCHIVE_PACKAGE = "android.intent.action.UNARCHIVE_PACKAGE";
+
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// Standard intent categories (see addCategory()).
diff --git a/core/java/android/content/pm/IPackageArchiverService.aidl b/core/java/android/content/pm/IPackageArchiverService.aidl
index fc471c4..dc6491d 100644
--- a/core/java/android/content/pm/IPackageArchiverService.aidl
+++ b/core/java/android/content/pm/IPackageArchiverService.aidl
@@ -23,4 +23,7 @@
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(anyOf={android.Manifest.permission.DELETE_PACKAGES,android.Manifest.permission.REQUEST_DELETE_PACKAGES})")
void requestArchive(String packageName, String callerPackageName, in IntentSender statusReceiver, in UserHandle userHandle);
+
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(anyOf={android.Manifest.permission.INSTALL_PACKAGES,android.Manifest.permission.REQUEST_INSTALL_PACKAGES})")
+ void requestUnarchive(String packageName, String callerPackageName, in UserHandle userHandle);
}
\ No newline at end of file
diff --git a/core/java/android/content/pm/PackageArchiver.java b/core/java/android/content/pm/PackageArchiver.java
index d739d50..b065231 100644
--- a/core/java/android/content/pm/PackageArchiver.java
+++ b/core/java/android/content/pm/PackageArchiver.java
@@ -42,6 +42,26 @@
@SystemApi
public class PackageArchiver {
+ /**
+ * Extra field for the package name of a package that is requested to be unarchived. Sent as
+ * part of the {@link android.content.Intent#ACTION_UNARCHIVE_PACKAGE} intent.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_UNARCHIVE_PACKAGE_NAME =
+ "android.content.pm.extra.UNARCHIVE_PACKAGE_NAME";
+
+ /**
+ * If true, the requestor of the unarchival has specified that the app should be unarchived
+ * for {@link android.os.UserHandle#ALL}.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_UNARCHIVE_ALL_USERS =
+ "android.content.pm.extra.UNARCHIVE_ALL_USERS";
+
private final Context mContext;
private final IPackageArchiverService mService;
@@ -58,7 +78,7 @@
*
* @param statusReceiver Callback used to notify when the operation is completed.
* @throws NameNotFoundException If {@code packageName} isn't found or not available to the
- * caller.
+ * caller or isn't archived.
* @hide
*/
@RequiresPermission(anyOf = {
@@ -76,4 +96,34 @@
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Requests to unarchive a currently archived package.
+ *
+ * <p> Sends a request to unarchive an app to the responsible installer. The installer is
+ * determined by {@link InstallSourceInfo#getUpdateOwnerPackageName()}, or
+ * {@link InstallSourceInfo#getInstallingPackageName()} if the former value is null.
+ *
+ * <p> The installation will happen asynchronously and can be observed through
+ * {@link android.content.Intent#ACTION_PACKAGE_ADDED}.
+ *
+ * @throws NameNotFoundException If {@code packageName} isn't found or not visible to the
+ * caller or if the package has no installer on the device
+ * anymore to unarchive it.
+ * @hide
+ */
+ @RequiresPermission(anyOf = {
+ Manifest.permission.INSTALL_PACKAGES,
+ Manifest.permission.REQUEST_INSTALL_PACKAGES})
+ @SystemApi
+ public void requestUnarchive(@NonNull String packageName)
+ throws NameNotFoundException {
+ try {
+ mService.requestUnarchive(packageName, mContext.getPackageName(), mContext.getUser());
+ } catch (ParcelableException e) {
+ e.maybeRethrow(NameNotFoundException.class);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/inputmethodservice/NavigationBarController.java b/core/java/android/inputmethodservice/NavigationBarController.java
index c01664e..8be4c58 100644
--- a/core/java/android/inputmethodservice/NavigationBarController.java
+++ b/core/java/android/inputmethodservice/NavigationBarController.java
@@ -237,7 +237,7 @@
mNavigationBarFrame.setOnApplyWindowInsetsListener((view, insets) -> {
if (mNavigationBarFrame != null) {
boolean visible = insets.isVisible(captionBar());
- mNavigationBarFrame.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
+ mNavigationBarFrame.setVisibility(visible ? View.VISIBLE : View.GONE);
}
return view.onApplyWindowInsets(insets);
});
diff --git a/core/java/android/os/IVibratorManagerService.aidl b/core/java/android/os/IVibratorManagerService.aidl
index 6275352..f30dd20 100644
--- a/core/java/android/os/IVibratorManagerService.aidl
+++ b/core/java/android/os/IVibratorManagerService.aidl
@@ -36,4 +36,10 @@
void vibrate(int uid, int displayId, String opPkg, in CombinedVibration vibration,
in VibrationAttributes attributes, String reason, IBinder token);
void cancelVibrate(int usageFilter, IBinder token);
+
+ // Async oneway APIs.
+ // There is no order guarantee with respect to the two-way APIs above like
+ // vibrate/isVibrating/cancel.
+ oneway void performHapticFeedback(int uid, int displayId, String opPkg, int constant,
+ boolean always, String reason, IBinder token);
}
diff --git a/core/java/android/os/SystemVibrator.java b/core/java/android/os/SystemVibrator.java
index 1cd0f3b..04c257b 100644
--- a/core/java/android/os/SystemVibrator.java
+++ b/core/java/android/os/SystemVibrator.java
@@ -206,6 +206,15 @@
}
@Override
+ public void performHapticFeedback(int constant, boolean always, String reason) {
+ if (mVibratorManager == null) {
+ Log.w(TAG, "Failed to perform haptic feedback; no vibrator manager.");
+ return;
+ }
+ mVibratorManager.performHapticFeedback(constant, always, reason);
+ }
+
+ @Override
public void cancel() {
if (mVibratorManager == null) {
Log.w(TAG, "Failed to cancel vibrate; no vibrator manager.");
diff --git a/core/java/android/os/SystemVibratorManager.java b/core/java/android/os/SystemVibratorManager.java
index 284b246..ee90834 100644
--- a/core/java/android/os/SystemVibratorManager.java
+++ b/core/java/android/os/SystemVibratorManager.java
@@ -145,6 +145,21 @@
}
@Override
+ public void performHapticFeedback(int constant, boolean always, String reason) {
+ if (mService == null) {
+ Log.w(TAG, "Failed to perform haptic feedback; no vibrator manager service.");
+ return;
+ }
+ try {
+ mService.performHapticFeedback(
+ Process.myUid(), mContext.getAssociatedDisplayId(), mPackageName, constant,
+ always, reason, mToken);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to perform haptic feedback.", e);
+ }
+ }
+
+ @Override
public void cancel() {
cancelVibration(VibrationAttributes.USAGE_FILTER_MATCH_ALL);
}
@@ -228,6 +243,11 @@
}
@Override
+ public void performHapticFeedback(int effectId, boolean always, String reason) {
+ SystemVibratorManager.this.performHapticFeedback(effectId, always, reason);
+ }
+
+ @Override
public void cancel() {
SystemVibratorManager.this.cancel();
}
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index aafa501..99c9925 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -510,6 +510,28 @@
String reason, @NonNull VibrationAttributes attributes);
/**
+ * Performs a haptic feedback.
+ *
+ * <p>A haptic feedback is a short vibration feedback. The type of feedback is identified via
+ * the {@code constant}, which should be one of the effect constants provided in
+ * {@link HapticFeedbackConstants}. The haptic feedback provided for a given effect ID is
+ * consistent across all usages on the same device.
+ *
+ * @param constant the ID for the haptic feedback. This should be one of the constants defined
+ * in {@link HapticFeedbackConstants}.
+ * @param always {@code true} if the haptic feedback should be played regardless of the user
+ * vibration intensity settings applicable to the corresponding vibration.
+ * {@code false} if the vibration for the haptic feedback should respect the applicable
+ * vibration intensity settings.
+ * @param reason the reason for this haptic feedback.
+ *
+ * @hide
+ */
+ public void performHapticFeedback(int constant, boolean always, String reason) {
+ Log.w(TAG, "performHapticFeedback is not supported");
+ }
+
+ /**
* Query whether the vibrator natively supports the given effects.
*
* <p>If an effect is not supported, the system may still automatically fall back to playing
diff --git a/core/java/android/os/VibratorManager.java b/core/java/android/os/VibratorManager.java
index f506ef8..e0b6a9f 100644
--- a/core/java/android/os/VibratorManager.java
+++ b/core/java/android/os/VibratorManager.java
@@ -35,7 +35,8 @@
public abstract class VibratorManager {
private static final String TAG = "VibratorManager";
- private final String mPackageName;
+ /** @hide */
+ protected final String mPackageName;
/**
* @hide to prevent subclassing from outside of the framework
@@ -137,6 +138,21 @@
String reason, @Nullable VibrationAttributes attributes);
/**
+ * Performs a haptic feedback.
+ *
+ * @param constant the ID of the requested haptic feedback. Should be one of the constants
+ * defined in {@link HapticFeedbackConstants}.
+ * @param always {@code true} if the haptic feedback should be played regardless of the user
+ * vibration intensity settings applicable to the corresponding vibration.
+ * {@code false} otherwise.
+ * @param reason the reason for this haptic feedback.
+ * @hide
+ */
+ public void performHapticFeedback(int constant, boolean always, String reason) {
+ Log.w(TAG, "performHapticFeedback is not supported");
+ }
+
+ /**
* Turn all the vibrators off.
*/
@RequiresPermission(android.Manifest.permission.VIBRATE)
diff --git a/core/java/android/os/vibrator/flags.aconfig b/core/java/android/os/vibrator/flags.aconfig
new file mode 100644
index 0000000..361e244
--- /dev/null
+++ b/core/java/android/os/vibrator/flags.aconfig
@@ -0,0 +1,8 @@
+package: "android.os.vibrator"
+
+flag {
+ namespace: "vibrator"
+ name: "use_vibrator_haptic_feedback"
+ description: "Enables performHapticFeedback to directly use the vibrator service instead of going through the window session"
+ bug: "295459081"
+}
\ No newline at end of file
diff --git a/core/java/android/text/flags/phrase_strict_fallback.aconfig b/core/java/android/text/flags/phrase_strict_fallback.aconfig
new file mode 100644
index 0000000..c67a21b
--- /dev/null
+++ b/core/java/android/text/flags/phrase_strict_fallback.aconfig
@@ -0,0 +1,8 @@
+package: "com.android.text.flags"
+
+flag {
+ name: "phrase_strict_fallback"
+ namespace: "text"
+ description: "Feature flag for automatic fallback from phrase based line break to strict line break."
+ bug: "281970875"
+}
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 5fe2aa1..12527e9 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -254,7 +254,6 @@
DEFAULT_FLAGS.put(SETTINGS_ENABLE_LOCKSCREEN_TRANSFER_API, "true");
DEFAULT_FLAGS.put(SETTINGS_REMOTE_DEVICE_CREDENTIAL_VALIDATION, "true");
DEFAULT_FLAGS.put(SETTINGS_BIOMETRICS2_FINGERPRINT_SETTINGS, "false");
- DEFAULT_FLAGS.put("settings_press_hold_nav_handle_to_search", "false");
// TODO: b/298454866 Replace with Trunk Stable Feature Flag
DEFAULT_FLAGS.put(SETTINGS_REMOTEAUTH_ENROLLMENT_SETTINGS, "false");
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 9a4cb72..30fd2cf 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -105,6 +105,8 @@
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.Trace;
+import android.os.Vibrator;
+import android.os.vibrator.Flags;
import android.sysprop.DisplayProperties;
import android.text.InputType;
import android.text.TextUtils;
@@ -5411,6 +5413,9 @@
*/
private PointerIcon mMousePointerIcon;
+ /** Vibrator for haptic feedback. */
+ private Vibrator mVibrator;
+
/**
* @hide
*/
@@ -27758,8 +27763,24 @@
&& !isHapticFeedbackEnabled()) {
return false;
}
- return mAttachInfo.mRootCallbacks.performHapticFeedback(feedbackConstant,
- (flags & HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING) != 0);
+
+ final boolean always = (flags & HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING) != 0;
+ if (Flags.useVibratorHapticFeedback()) {
+ if (!mAttachInfo.canPerformHapticFeedback()) {
+ return false;
+ }
+ getSystemVibrator().performHapticFeedback(
+ feedbackConstant, always, "View#performHapticFeedback");
+ return true;
+ }
+ return mAttachInfo.mRootCallbacks.performHapticFeedback(feedbackConstant, always);
+ }
+
+ private Vibrator getSystemVibrator() {
+ if (mVibrator != null) {
+ return mVibrator;
+ }
+ return mVibrator = mContext.getSystemService(Vibrator.class);
}
/**
@@ -31239,6 +31260,11 @@
return events;
}
+ private boolean canPerformHapticFeedback() {
+ return mSession != null
+ && (mDisplay.getFlags() & Display.FLAG_TOUCH_FEEDBACK_DISABLED) == 0;
+ }
+
@Nullable
ScrollCaptureInternal getScrollCaptureInternal() {
if (mScrollCaptureInternal != null) {
diff --git a/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java b/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
index f67a61b..61470f2 100644
--- a/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
+++ b/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
@@ -28,7 +28,13 @@
import android.annotation.AnyThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UiThread;
+import android.app.UriGrantsManager;
+import android.content.ContentProvider;
+import android.content.Intent;
import android.graphics.RectF;
+import android.net.Uri;
+import android.os.Binder;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.CancellationSignalBeamer;
@@ -37,6 +43,7 @@
import android.os.Looper;
import android.os.ResultReceiver;
import android.os.Trace;
+import android.os.UserHandle;
import android.util.Log;
import android.util.proto.ProtoOutputStream;
import android.view.KeyEvent;
@@ -1143,7 +1150,22 @@
public void commitContent(InputConnectionCommandHeader header,
InputContentInfo inputContentInfo, int flags, Bundle opts,
AndroidFuture future /* T=Boolean */) {
+ final int imeUid = Binder.getCallingUid();
dispatchWithTracing("commitContent", future, () -> {
+ // Check if the originator IME has the right permissions
+ try {
+ final int contentUriOwnerUserId = ContentProvider.getUserIdFromUri(
+ inputContentInfo.getContentUri(), UserHandle.getUserId(imeUid));
+ final Uri contentUriWithoutUserId = ContentProvider.getUriWithoutUserId(
+ inputContentInfo.getContentUri());
+ UriGrantsManager.getService().checkGrantUriPermission_ignoreNonSystem(imeUid, null,
+ contentUriWithoutUserId, Intent.FLAG_GRANT_READ_URI_PERMISSION,
+ contentUriOwnerUserId);
+ } catch (Exception e) {
+ Log.w(TAG, "commitContent with invalid Uri permission from IME:", e);
+ return false;
+ }
+
if (header.mSessionId != mCurrentSessionId.get()) {
return false; // cancelled
}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 6523fff..f5b81b0 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -50,6 +50,7 @@
import android.view.DragEvent;
import android.view.KeyEvent;
import android.view.MotionEvent;
+import android.view.PointerIcon;
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewGroup;
@@ -3139,4 +3140,15 @@
if (result == null) return super.onApplyWindowInsets(insets);
return result;
}
+
+ @Override
+ @Nullable
+ public PointerIcon onResolvePointerIcon(@NonNull MotionEvent event, int pointerIndex) {
+ PointerIcon icon =
+ mProvider.getViewDelegate().onResolvePointerIcon(event, pointerIndex);
+ if (icon != null) {
+ return icon;
+ }
+ return super.onResolvePointerIcon(event, pointerIndex);
+ }
}
diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java
index 26579c5d..ca423e0 100644
--- a/core/java/android/webkit/WebViewProvider.java
+++ b/core/java/android/webkit/WebViewProvider.java
@@ -39,6 +39,7 @@
import android.view.DragEvent;
import android.view.KeyEvent;
import android.view.MotionEvent;
+import android.view.PointerIcon;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.view.WindowInsets;
@@ -496,6 +497,15 @@
default WindowInsets onApplyWindowInsets(@Nullable WindowInsets insets) {
return null;
}
+
+ /**
+ * @hide Only used by WebView.
+ */
+ @SuppressWarnings("unused")
+ @Nullable
+ default PointerIcon onResolvePointerIcon(@NonNull MotionEvent event, int pointerIndex) {
+ return null;
+ }
}
interface ScrollDelegate {
diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig
index 560e41b..b8d251f 100644
--- a/core/java/android/window/flags/windowing_sdk.aconfig
+++ b/core/java/android/window/flags/windowing_sdk.aconfig
@@ -8,3 +8,10 @@
description: "Whether the feature to sync different window-related config updates is enabled"
bug: "260873529"
}
+
+flag {
+ namespace: "windowing_sdk"
+ name: "activity_embedding_overlay_presentation_flag"
+ description: "Whether the overlay presentation feature is enabled"
+ bug: "243518738"
+}
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 03480e4..302c7fa 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -5480,6 +5480,13 @@
of known compatibility issues. -->
<string-array name="config_highRefreshRateBlacklist"></string-array>
+ <!-- The list of packages to automatically opt in to refresh rate suppressing by small area
+ detection. Format of this array should be packageName:threshold and threshold value should
+ be between 0 to 1-->
+ <string-array name="config_smallAreaDetectionAllowlist" translatable="false">
+ <!-- Add packages:threshold here -->
+ </string-array>
+
<!-- The list of packages to force slowJpegMode for Apps using Camera API1 -->
<string-array name="config_forceSlowJpegModeList" translatable="false">
<!-- Add packages here -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 7ea5974..02209a7 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4288,6 +4288,8 @@
<java-symbol type="array" name="config_highRefreshRateBlacklist" />
<java-symbol type="array" name="config_forceSlowJpegModeList" />
+ <java-symbol type="array" name="config_smallAreaDetectionAllowlist" />
+
<java-symbol type="layout" name="chooser_dialog" />
<java-symbol type="layout" name="chooser_dialog_item" />
<java-symbol type="drawable" name="chooser_dialog_background" />
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index a41fb64..0778311 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -67,7 +67,6 @@
import android.window.WindowContextInfo;
import android.window.WindowTokenClientController;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.MediumTest;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.rule.ActivityTestRule;
@@ -254,15 +253,9 @@
// Execute a local relaunch item with current scaled config (e.g. simulate recreate),
// the config should not be scaled again.
- final Configuration currentConfig = activity.getResources().getConfiguration();
- final ClientTransaction localTransaction =
- newTransaction(activityThread, activity.getActivityToken());
- localTransaction.addCallback(ActivityRelaunchItem.obtain(
- null /* pendingResults */, null /* pendingIntents */, 0 /* configChanges */,
- new MergedConfiguration(currentConfig, currentConfig),
- true /* preserveWindow */));
InstrumentationRegistry.getInstrumentation().runOnMainSync(
- () -> activityThread.executeTransaction(localTransaction));
+ () -> activityThread.executeTransaction(
+ newRelaunchResumeTransaction(activity)));
assertScreenScale(scale, activity, originalActivityConfig, originalActivityMetrics);
} finally {
@@ -630,7 +623,6 @@
});
}
- @FlakyTest(bugId = 298331121)
@Test
public void testHandleConfigurationChanged_DoesntOverrideActivityConfig() {
final TestActivity activity = mActivityTestRule.launchActivity(new Intent());
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index 9aac694..0479576 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -103,7 +103,7 @@
default void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {}
/** Whether this task listener supports compat UI. */
default boolean supportCompatUI() {
- // All TaskListeners should support compat UI except PIP.
+ // All TaskListeners should support compat UI except PIP and StageCoordinator.
return true;
}
/** Attaches a child window surface to the task surface. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java
index 72702e7..b828aac 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java
@@ -18,6 +18,7 @@
import android.annotation.Nullable;
import android.os.RemoteException;
+import android.os.Trace;
import android.util.Slog;
import android.view.IDisplayChangeWindowCallback;
import android.view.IDisplayChangeWindowController;
@@ -40,6 +41,7 @@
*/
public class DisplayChangeController {
private static final String TAG = DisplayChangeController.class.getSimpleName();
+ private static final String HANDLE_DISPLAY_CHANGE_TRACE_TAG = "HandleRemoteDisplayChange";
private final ShellExecutor mMainExecutor;
private final IWindowManager mWmService;
@@ -81,9 +83,15 @@
/** Query all listeners for changes that should happen on display change. */
void dispatchOnDisplayChange(WindowContainerTransaction outWct, int displayId,
int fromRotation, int toRotation, DisplayAreaInfo newDisplayAreaInfo) {
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) {
+ Trace.beginSection("dispatchOnDisplayChange");
+ }
for (OnDisplayChangingListener c : mDisplayChangeListener) {
c.onDisplayChange(displayId, fromRotation, toRotation, newDisplayAreaInfo, outWct);
}
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) {
+ Trace.endSection();
+ }
}
private void onDisplayChange(int displayId, int fromRotation, int toRotation,
@@ -94,6 +102,10 @@
callback.continueDisplayChange(t);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to continue handling display change", e);
+ } finally {
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) {
+ Trace.endAsyncSection(HANDLE_DISPLAY_CHANGE_TRACE_TAG, callback.hashCode());
+ }
}
}
@@ -103,6 +115,9 @@
@Override
public void onDisplayChange(int displayId, int fromRotation, int toRotation,
DisplayAreaInfo newDisplayAreaInfo, IDisplayChangeWindowCallback callback) {
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) {
+ Trace.beginAsyncSection(HANDLE_DISPLAY_CHANGE_TRACE_TAG, callback.hashCode());
+ }
mMainExecutor.execute(() -> DisplayChangeController.this
.onDisplayChange(displayId, fromRotation, toRotation,
newDisplayAreaInfo, callback));
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java
index cbff464..77aefc8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java
@@ -208,7 +208,7 @@
}
private boolean getHasUserAspectRatioSettingsButton(@NonNull TaskInfo taskInfo) {
- return taskInfo.topActivityEligibleForUserAspectRatioButton
+ return taskInfo.topActivityEligibleForUserAspectRatioButton
&& (taskInfo.topActivityBoundsLetterboxed
|| taskInfo.isUserFullscreenOverrideEnabled);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 842b1bf..94fa485 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -228,6 +228,15 @@
private final Toast mSplitUnsupportedToast;
private SplitRequest mSplitRequest;
+ /**
+ * Since StageCoordinator only coordinates MainStage and SideStage, it shouldn't support
+ * CompatUI layouts. CompatUI is handled separately by MainStage and SideStage.
+ */
+ @Override
+ public boolean supportCompatUI() {
+ return false;
+ }
+
class SplitRequest {
@SplitPosition
int mActivatePosition;
diff --git a/libs/WindowManager/Shell/tests/flicker/Android.bp b/libs/WindowManager/Shell/tests/flicker/Android.bp
index 434b008..eb650ca 100644
--- a/libs/WindowManager/Shell/tests/flicker/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/Android.bp
@@ -126,6 +126,7 @@
"flickertestapplib",
"flickerlib",
"flickerlib-helpers",
+ "flickerlib-trace_processor_shell",
"platform-test-annotations",
"wm-flicker-common-app-helpers",
"wm-flicker-common-assertions",
diff --git a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifest.xml b/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifest.xml
index 6a87de4..ae130b8 100644
--- a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifest.xml
@@ -45,9 +45,13 @@
<uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS" />
<!-- Enable bubble notification-->
<uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
+ <!-- Allow the test to connect to perfetto trace processor -->
+ <uses-permission android:name="android.permission.INTERNET"/>
- <!-- Allow the test to write directly to /sdcard/ -->
- <application android:requestLegacyExternalStorage="true" android:largeHeap="true">
+ <!-- Allow the test to write directly to /sdcard/ and connect to trace processor -->
+ <application android:requestLegacyExternalStorage="true"
+ android:networkSecurityConfig="@xml/network_security_config"
+ android:largeHeap="true">
<uses-library android:name="android.test.runner"/>
<service android:name=".NotificationListener"
diff --git a/libs/WindowManager/Shell/tests/flicker/res/xml/network_security_config.xml b/libs/WindowManager/Shell/tests/flicker/res/xml/network_security_config.xml
new file mode 100644
index 0000000..4bd9ca0
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/res/xml/network_security_config.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<network-security-config>
+ <domain-config cleartextTrafficPermitted="true">
+ <domain includeSubdomains="true">localhost</domain>
+ </domain-config>
+</network-security-config>
diff --git a/media/java/android/media/flags.aconfig b/media/java/android/media/flags.aconfig
new file mode 100644
index 0000000..8567a3b
--- /dev/null
+++ b/media/java/android/media/flags.aconfig
@@ -0,0 +1,8 @@
+package: "android.media"
+
+flag {
+ name: "haptics_customization_enabled"
+ namespace: "media"
+ description: "Enables the haptics customization feature"
+ bug: "241918098"
+}
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index 3d35bad..5dcb9d2 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -8,7 +8,6 @@
}
android_library {
-
name: "SettingsLib",
static_libs: [
@@ -26,44 +25,20 @@
"iconloader",
"WifiTrackerLibRes",
- "SettingsLibHelpUtils",
- "SettingsLibRestrictedLockUtils",
- "SettingsLibActionBarShadow",
- "SettingsLibAppPreference",
- "SettingsLibSearchWidget",
- "SettingsLibSettingsSpinner",
- "SettingsLibIllustrationPreference",
- "SettingsLibLayoutPreference",
- "SettingsLibMainSwitchPreference",
- "SettingsLibActionButtonsPreference",
- "SettingsLibEntityHeaderWidgets",
- "SettingsLibBarChartPreference",
- "SettingsLibProgressBar",
- "SettingsLibAdaptiveIcon",
- "SettingsLibRadioButtonPreference",
- "SettingsLibSelectorWithWidgetPreference",
- "SettingsLibDisplayUtils",
- "SettingsLibUtils",
- "SettingsLibEmergencyNumber",
- "SettingsLibTopIntroPreference",
- "SettingsLibBannerMessagePreference",
- "SettingsLibFooterPreference",
- "SettingsLibUsageProgressBarPreference",
- "SettingsLibCollapsingToolbarBaseActivity",
- "SettingsLibTwoTargetPreference",
- "SettingsLibSettingsTransition",
- "SettingsLibButtonPreference",
"SettingsLibDeviceStateRotationLock",
- "SettingsLibProfileSelector",
+ "SettingsLibDisplayUtils",
+ "SettingsLibEmergencyNumber",
+ "SettingsLibSearchWidget",
+ "SettingsLibUtils",
+ "SettingsLibWidget",
"setupdesign",
"zxing-core-1.7",
"androidx.room_room-runtime",
"settingslib_flags_lib",
-
],
plugins: ["androidx.room_room-compiler-plugin"],
-
+ use_resource_processor: true,
resource_dirs: ["res"],
srcs: [
@@ -72,6 +47,43 @@
],
}
+// Group all the libraries with namespace "com.android.settingslib.widget", to allow SettingsLib to
+// set use_resource_processor = true.
+// We can remove SettingsLibWidget when all these libraries have its own namespace.
+android_library {
+ name: "SettingsLibWidget",
+ visibility: ["//visibility:private"],
+ manifest: "AndroidManifest-SettingsLibWidget.xml",
+ static_libs: [
+ "SettingsLibActionBarShadow",
+ "SettingsLibActionButtonsPreference",
+ "SettingsLibAdaptiveIcon",
+ "SettingsLibAppPreference",
+ "SettingsLibBannerMessagePreference",
+ "SettingsLibBarChartPreference",
+ "SettingsLibButtonPreference",
+ "SettingsLibCollapsingToolbarBaseActivity",
+ "SettingsLibEntityHeaderWidgets",
+ "SettingsLibFooterPreference",
+ "SettingsLibHelpUtils",
+ "SettingsLibIllustrationPreference",
+ "SettingsLibLayoutPreference",
+ "SettingsLibMainSwitchPreference",
+ "SettingsLibProfileSelector",
+ "SettingsLibProgressBar",
+ "SettingsLibRadioButtonPreference",
+ "SettingsLibRestrictedLockUtils",
+ "SettingsLibSelectorWithWidgetPreference",
+ "SettingsLibSettingsSpinner",
+ "SettingsLibSettingsTransition",
+ "SettingsLibTopIntroPreference",
+ "SettingsLibTwoTargetPreference",
+ "SettingsLibUsageProgressBarPreference",
+ ],
+
+ resource_dirs: [],
+}
+
// NOTE: Keep this module in sync with ./common.mk
java_defaults {
name: "SettingsLibDefaults",
diff --git a/packages/SettingsLib/AndroidManifest-SettingsLibWidget.xml b/packages/SettingsLib/AndroidManifest-SettingsLibWidget.xml
new file mode 100644
index 0000000..38a7d6a
--- /dev/null
+++ b/packages/SettingsLib/AndroidManifest-SettingsLibWidget.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest package="com.android.settingslib.widget" />
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt
index 90a723f..b77368a 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt
@@ -253,6 +253,7 @@
hideTitleSemantics = false,
navigationIcon = navigationIcon,
actions = actionsRow,
+ titleScaleDisabled = false,
)
}
}
@@ -426,6 +427,7 @@
* accessibility services at the same time, when animating between collapsed / expanded states.
* @param navigationIcon a navigation icon [Composable]
* @param actions actions [Composable]
+ * @param titleScaleDisabled whether the title font scaling is disabled. Default is disabled.
*/
@Composable
private fun TopAppBarLayout(
@@ -443,6 +445,7 @@
hideTitleSemantics: Boolean,
navigationIcon: @Composable () -> Unit,
actions: @Composable () -> Unit,
+ titleScaleDisabled: Boolean = true,
) {
Layout(
{
@@ -466,9 +469,12 @@
ProvideTextStyle(value = titleTextStyle) {
CompositionLocalProvider(
LocalContentColor provides titleContentColor,
- // Disable the title font scaling by only passing the density but not the
- // font scale.
- LocalDensity provides Density(density = LocalDensity.current.density),
+ LocalDensity provides with(LocalDensity.current) {
+ Density(
+ density = density,
+ fontScale = if (titleScaleDisabled) 1f else fontScale,
+ )
+ },
content = title
)
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SearchScaffold.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SearchScaffold.kt
index d437e35..696e877 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SearchScaffold.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SearchScaffold.kt
@@ -18,6 +18,7 @@
import androidx.activity.compose.BackHandler
import androidx.appcompat.R
+import androidx.compose.foundation.focusable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.RowScope
@@ -96,7 +97,8 @@
Modifier
.padding(paddingValues.horizontalValues())
.padding(top = paddingValues.calculateTopPadding())
- .fillMaxSize(),
+ .focusable()
+ .fillMaxSize()
) {
content(
paddingValues.calculateBottomPadding(),
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
index af06d73..b1d1ea5e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
@@ -126,10 +126,10 @@
switchSummary = isChecked()
? getUpdatableEnterpriseString(
getContext(), ENABLED_BY_ADMIN_SWITCH_SUMMARY,
- R.string.enabled_by_admin)
+ com.android.settingslib.widget.R.string.enabled_by_admin)
: getUpdatableEnterpriseString(
getContext(), DISABLED_BY_ADMIN_SWITCH_SUMMARY,
- R.string.disabled_by_admin);
+ com.android.settingslib.widget.R.string.disabled_by_admin);
} else {
switchSummary = mRestrictedSwitchSummary;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
index 6eb2f38..96bb4b5 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
@@ -1776,7 +1776,8 @@
final int userId = UserHandle.getUserId(this.info.uid);
if (UserManager.get(context).isManagedProfile(userId)) {
this.labelDescription = context.getString(
- com.android.settingslib.R.string.accessibility_work_profile_app_description,
+ com.android.settingslib.utils.R
+ .string.accessibility_work_profile_app_description,
this.label);
} else {
this.labelDescription = this.label;
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BroadcastDialog.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BroadcastDialog.java
index cb4eba4..f5257b0 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BroadcastDialog.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BroadcastDialog.java
@@ -46,7 +46,8 @@
View layout = View.inflate(mContext, R.layout.broadcast_dialog, null);
final Window window = getWindow();
window.setContentView(layout);
- window.setWindowAnimations(R.style.Theme_AlertDialog_SettingsLib);
+ window.setWindowAnimations(
+ com.android.settingslib.widget.R.style.Theme_AlertDialog_SettingsLib);
TextView title = layout.findViewById(R.id.dialog_title);
TextView subTitle = layout.findViewById(R.id.dialog_subtitle);
diff --git a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java
index 1f4cafce..d53c3a7 100644
--- a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java
@@ -153,7 +153,7 @@
}
final ImageView icon = holder.itemView.findViewById(android.R.id.icon);
final int iconSize = getContext().getResources().getDimensionPixelSize(
- R.dimen.secondary_app_icon_size);
+ com.android.settingslib.widget.R.dimen.secondary_app_icon_size);
if (icon != null && iconSize > 0) {
ViewGroup.LayoutParams params = icon.getLayoutParams();
params.height = iconSize;
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/ConversationIconFactory.java b/packages/SettingsLib/src/com/android/settingslib/notification/ConversationIconFactory.java
index ebdfbea..251cd36 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/ConversationIconFactory.java
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/ConversationIconFactory.java
@@ -32,7 +32,6 @@
import android.util.Log;
import com.android.launcher3.icons.BaseIconFactory;
-import com.android.settingslib.R;
import com.android.settingslib.Utils;
/**
@@ -81,7 +80,7 @@
mPackageManager = pm;
mIconDrawableFactory = iconDrawableFactory;
mImportantConversationColor = context.getResources().getColor(
- R.color.important_conversation, null);
+ com.android.launcher3.icons.R.color.important_conversation, null);
}
/**
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
index 21eb35a..2d6f058 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
@@ -167,7 +167,8 @@
ImageView frictionImageView = (ImageView) view.findViewById(R.id.friction_icon);
bindFrictionImage(frictionImageView);
- final View divider = view.findViewById(R.id.two_target_divider);
+ final View divider =
+ view.findViewById(com.android.settingslib.widget.R.id.two_target_divider);
divider.setVisibility(shouldShowDivider() ? View.VISIBLE : View.INVISIBLE);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
index c45d774..015356e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
@@ -331,7 +331,8 @@
return;
} else if (!isDefaultNetwork && mDefaultNetworkCapabilities != null
&& mDefaultNetworkCapabilities.hasTransport(TRANSPORT_CELLULAR)) {
- statusLabel = mContext.getString(R.string.wifi_connected_low_quality);
+ statusLabel = mContext.getString(
+ com.android.wifitrackerlib.R.string.wifi_connected_low_quality);
return;
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/PrimarySwitchPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/PrimarySwitchPreferenceTest.java
index 74c2fc8..32a16716 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/PrimarySwitchPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/PrimarySwitchPreferenceTest.java
@@ -54,7 +54,7 @@
mPreference = new PrimarySwitchPreference(mContext);
LayoutInflater inflater = LayoutInflater.from(mContext);
mHolder = PreferenceViewHolder.createInstanceForTests(inflater.inflate(
- com.android.settingslib.R.layout.preference_two_target, null));
+ com.android.settingslib.widget.R.layout.preference_two_target, null));
mWidgetView = mHolder.itemView.findViewById(android.R.id.widget_frame);
inflater.inflate(R.layout.preference_widget_primary_switch, mWidgetView, true);
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayoutTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayoutTest.java
index 06343f5..ff84dc9 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayoutTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayoutTest.java
@@ -55,7 +55,8 @@
@Test
public void onCreate_userAddedChildViewsBeMovedToContentFrame() {
CollapsingCoordinatorLayout layout = mActivity.getCollapsingCoordinatorLayout();
- View contentFrameView = layout.findViewById(R.id.content_frame);
+ View contentFrameView =
+ layout.findViewById(com.android.settingslib.widget.R.id.content_frame);
TextView textView = contentFrameView.findViewById(R.id.text_hello_world);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/HideNonSystemOverlayMixinTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/HideNonSystemOverlayMixinTest.java
index 471dac0..8246aff 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/HideNonSystemOverlayMixinTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/HideNonSystemOverlayMixinTest.java
@@ -29,8 +29,6 @@
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
-import com.android.settingslib.R;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -95,7 +93,7 @@
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setTheme(R.style.Theme_AppCompat);
+ setTheme(androidx.appcompat.R.style.Theme_AppCompat);
getLifecycle().addObserver(new HideNonSystemOverlayMixin(this));
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/FinancedDeviceActionDisabledByAdminControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/FinancedDeviceActionDisabledByAdminControllerTest.java
index 7b08fee..2f8967e 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/FinancedDeviceActionDisabledByAdminControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/FinancedDeviceActionDisabledByAdminControllerTest.java
@@ -30,8 +30,6 @@
import androidx.test.core.app.ApplicationProvider;
-import com.android.settingslib.R;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -51,7 +49,7 @@
@Before
public void setUp() {
- mActivity.setTheme(R.style.Theme_AppCompat_DayNight);
+ mActivity.setTheme(androidx.appcompat.R.style.Theme_AppCompat_DayNight);
mController.initialize(mTestUtils.createLearnMoreButtonLauncher());
mController.updateEnforcedAdmin(ENFORCED_ADMIN, ENFORCEMENT_ADMIN_USER_ID);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/ManagedDeviceActionDisabledByAdminControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/ManagedDeviceActionDisabledByAdminControllerTest.java
index 7b88566..f168122 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/ManagedDeviceActionDisabledByAdminControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/ManagedDeviceActionDisabledByAdminControllerTest.java
@@ -33,8 +33,6 @@
import androidx.test.core.app.ApplicationProvider;
-import com.android.settingslib.R;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -57,7 +55,7 @@
@Before
public void setUp() {
- mActivity.setTheme(R.style.Theme_AppCompat_DayNight);
+ mActivity.setTheme(androidx.appcompat.R.style.Theme_AppCompat_DayNight);
}
@Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/CreateUserDialogControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/CreateUserDialogControllerTest.java
index 66a2ea6..6831222 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/CreateUserDialogControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/CreateUserDialogControllerTest.java
@@ -62,7 +62,7 @@
public void setup() {
MockitoAnnotations.initMocks(this);
mActivity = spy(ActivityController.of(new FragmentActivity()).get());
- mActivity.setTheme(R.style.Theme_AppCompat_DayNight);
+ mActivity.setTheme(androidx.appcompat.R.style.Theme_AppCompat_DayNight);
mUnderTest = new TestCreateUserDialogController();
mPhotoRestrictedByBase = false;
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/EditUserInfoControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/EditUserInfoControllerTest.java
index f595cd3..a95257a 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/EditUserInfoControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/EditUserInfoControllerTest.java
@@ -99,7 +99,7 @@
public void setup() {
MockitoAnnotations.initMocks(this);
mActivity = spy(ActivityController.of(new FragmentActivity()).get());
- mActivity.setTheme(R.style.Theme_AppCompat_DayNight);
+ mActivity.setTheme(androidx.appcompat.R.style.Theme_AppCompat_DayNight);
mController = new TestEditUserInfoController();
mPhotoRestrictedByBase = false;
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveIconTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveIconTest.java
index 6cbae05..10862403 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveIconTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveIconTest.java
@@ -105,15 +105,15 @@
icon.setBackgroundColor(mContext, tile);
- assertThat(icon.mBackgroundColor)
- .isEqualTo(mContext.getColor(R.color.homepage_generic_icon_background));
+ assertThat(icon.mBackgroundColor).isEqualTo(mContext.getColor(
+ com.android.settingslib.widget.R.color.homepage_generic_icon_background));
}
@Test
public void onBindTile_externalTileWithBackgroundColorHint_shouldUpdateIcon() {
final Tile tile = spy(new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE));
mActivityInfo.metaData.putInt(META_DATA_PREFERENCE_ICON_BACKGROUND_HINT,
- R.color.bt_outline_color);
+ com.android.settingslib.widget.R.color.bt_outline_color);
doReturn(Icon.createWithResource(mContext, R.drawable.ic_system_update))
.when(tile).getIcon(mContext);
@@ -121,8 +121,8 @@
new AdaptiveIcon(mContext, new ColorDrawable(Color.BLACK));
icon.setBackgroundColor(mContext, tile);
- assertThat(icon.mBackgroundColor)
- .isEqualTo(mContext.getColor(R.color.bt_outline_color));
+ assertThat(icon.mBackgroundColor).isEqualTo(mContext.getColor(
+ com.android.settingslib.widget.R.color.bt_outline_color));
}
@Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveOutlineDrawableTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveOutlineDrawableTest.java
index 71d55bc..b2bc53d 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveOutlineDrawableTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveOutlineDrawableTest.java
@@ -21,8 +21,6 @@
import android.content.res.Resources;
import android.graphics.Paint;
-import com.android.settingslib.R;
-
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java
index 049c90e..a26f200 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java
@@ -58,14 +58,15 @@
@Test
public void setLearnMoreText_shouldSetAsTextInLearnMore() {
final PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(
- LayoutInflater.from(mContext).inflate(R.layout.preference_footer, null));
+ LayoutInflater.from(mContext)
+ .inflate(com.android.settingslib.widget.R.layout.preference_footer, null));
mFooterPreference.setLearnMoreText("Custom learn more");
mFooterPreference.setLearnMoreAction(view -> { /* do nothing */ } /* listener */);
mFooterPreference.onBindViewHolder(holder);
assertThat(((TextView) holder.findViewById(
- R.id.settingslib_learn_more)).getText().toString())
+ com.android.settingslib.widget.R.id.settingslib_learn_more)).getText().toString())
.isEqualTo("Custom learn more");
}
@@ -94,8 +95,9 @@
@Test
public void onBindViewHolder_whenTitleIsNull_shouldNotRaiseNpe() {
PreferenceViewHolder viewHolder = spy(PreferenceViewHolder.createInstanceForTests(
- LayoutInflater.from(mContext).inflate(R.layout.preference_footer, null)));
- when(viewHolder.findViewById(R.id.title)).thenReturn(null);
+ LayoutInflater.from(mContext)
+ .inflate(com.android.settingslib.widget.R.layout.preference_footer, null)));
+ when(viewHolder.findViewById(androidx.core.R.id.title)).thenReturn(null);
Throwable actualThrowable = null;
try {
@@ -110,8 +112,10 @@
@Test
public void onBindViewHolder_whenLearnMoreIsNull_shouldNotRaiseNpe() {
PreferenceViewHolder viewHolder = spy(PreferenceViewHolder.createInstanceForTests(
- LayoutInflater.from(mContext).inflate(R.layout.preference_footer, null)));
- when(viewHolder.findViewById(R.id.settingslib_learn_more)).thenReturn(null);
+ LayoutInflater.from(mContext)
+ .inflate(com.android.settingslib.widget.R.layout.preference_footer, null)));
+ when(viewHolder.findViewById(com.android.settingslib.widget.R.id.settingslib_learn_more))
+ .thenReturn(null);
Throwable actualThrowable = null;
try {
@@ -126,7 +130,8 @@
@Test
public void onBindViewHolder_whenIconFrameIsNull_shouldNotRaiseNpe() {
PreferenceViewHolder viewHolder = spy(PreferenceViewHolder.createInstanceForTests(
- LayoutInflater.from(mContext).inflate(R.layout.preference_footer, null)));
+ LayoutInflater.from(mContext)
+ .inflate(com.android.settingslib.widget.R.layout.preference_footer, null)));
when(viewHolder.findViewById(R.id.icon_frame)).thenReturn(null);
Throwable actualThrowable = null;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/TwoTargetPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/TwoTargetPreferenceTest.java
index aaec909..23b4c2a 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/TwoTargetPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/TwoTargetPreferenceTest.java
@@ -32,8 +32,6 @@
import androidx.preference.PreferenceViewHolder;
-import com.android.settingslib.R;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
index 0d7d9cc..017ac60 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
@@ -446,7 +446,9 @@
for (key in listOf(".blue600", ".blue400")) {
addValueCallback(KeyPath(key, "**"), LottieProperty.COLOR_FILTER) {
PorterDuffColorFilter(
- context.getColor(com.android.settingslib.R.color.settingslib_color_blue400),
+ context.getColor(
+ com.android.settingslib.color.R.color.settingslib_color_blue400
+ ),
PorterDuff.Mode.SRC_ATOP
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprint.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprint.kt
index 970b475..3ff1f09 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprint.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprint.kt
@@ -31,7 +31,7 @@
defaultCommunalWidgetSection: DefaultCommunalWidgetSection,
) : KeyguardBlueprint {
override val id: String = COMMUNAL
- override val sections: Array<KeyguardSection> = arrayOf(defaultCommunalWidgetSection)
+ override val sections: Set<KeyguardSection> = setOf(defaultCommunalWidgetSection)
companion object {
const val COMMUNAL = "communal"
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/DefaultCommunalWidgetSection.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/DefaultCommunalWidgetSection.kt
index 4fb9384..8640c97 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/DefaultCommunalWidgetSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/DefaultCommunalWidgetSection.kt
@@ -42,9 +42,12 @@
private val communalWidgetViewModel: CommunalWidgetViewModel,
private val communalWidgetViewAdapter: CommunalWidgetViewAdapter,
private val keyguardBlueprintInteractor: Lazy<KeyguardBlueprintInteractor>,
-) : KeyguardSection {
+) : KeyguardSection() {
private val widgetAreaViewId = R.id.communal_widget_wrapper
- override fun addViews(constraintLayout: ConstraintLayout) {
+
+ override fun addViews(constraintLayout: ConstraintLayout) {}
+
+ override fun bindData(constraintLayout: ConstraintLayout) {
if (!featureFlags.isEnabled(Flags.WIDGET_ON_KEYGUARD)) {
return
}
@@ -65,4 +68,6 @@
connect(widgetAreaViewId, END, PARENT_ID, END)
}
}
+
+ override fun removeViews(constraintLayout: ConstraintLayout) {}
}
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
index f68078a..82b0324 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
@@ -29,6 +29,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.util.Compile
import com.android.systemui.util.traceSection
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
@@ -239,7 +240,7 @@
private companion object {
const val TAG = "DisplayRepository"
- val DEBUG = Log.isLoggable(TAG, Log.DEBUG)
+ val DEBUG = Log.isLoggable(TAG, Log.DEBUG) || Compile.IS_DEBUG
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index d8b31a2..ea2568d 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -636,7 +636,13 @@
val CLIPBOARD_SHARED_TRANSITIONS =
unreleasedFlag("clipboard_shared_transitions", teamfood = true)
+ /**
+ * Whether the scene container (Flexiglass) is enabled. Note that [SCENE_CONTAINER] should be
+ * checked and toggled together with [SCENE_CONTAINER_ENABLED] so that ProGuard can remove
+ * unused code from our APK at compile time.
+ */
// TODO(b/283300105): Tracking Bug
+ @JvmField val SCENE_CONTAINER_ENABLED = false
@JvmField val SCENE_CONTAINER = unreleasedFlag("scene_container")
// 1900
@@ -674,6 +680,10 @@
// TODO:(b/283203305): Tracking bug
@JvmField val TRIM_FONT_CACHES_AT_UNLOCK = unreleasedFlag("trim_font_caches_on_unlock")
+ // TODO(b/298380520): Tracking Bug
+ @JvmField
+ val USER_TRACKER_BACKGROUND_CALLBACKS = unreleasedFlag("user_tracker_background_callbacks")
+
// 2700 - unfold transitions
// TODO(b/265764985): Tracking Bug
@Keep
@@ -764,7 +774,7 @@
/** Enable the Compose implementation of the PeopleSpaceActivity. */
@JvmField
- val COMPOSE_PEOPLE_SPACE = unreleasedFlag("compose_people_space")
+ val COMPOSE_PEOPLE_SPACE = releasedFlag("compose_people_space")
/** Enable the Compose implementation of the Quick Settings footer actions. */
@JvmField
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBlueprint.kt
index 659c5f3..35a9aae 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBlueprint.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBlueprint.kt
@@ -22,17 +22,34 @@
/** Determines the constraints for the ConstraintSet in the lockscreen root view. */
interface KeyguardBlueprint {
val id: String
- val sections: Array<KeyguardSection>
+ val sections: Set<KeyguardSection>
- fun addViews(constraintLayout: ConstraintLayout) {
- sections.forEach { it.addViews(constraintLayout) }
+ /**
+ * Add views to new blueprint.
+ *
+ * Finds sections that did not exist in the previous blueprint and add the corresponding views.
+ *
+ * @param previousBluePrint: KeyguardBlueprint the blueprint we are transitioning from.
+ */
+ fun addViews(previousBlueprint: KeyguardBlueprint?, constraintLayout: ConstraintLayout) {
+ sections.subtract((previousBlueprint?.sections ?: setOf()).toSet()).forEach {
+ it.addViews(constraintLayout)
+ it.bindData(constraintLayout)
+ }
+ }
+
+ /**
+ * Remove views of old blueprint.
+ *
+ * Finds sections that are no longer in the next blueprint and remove the corresponding views.
+ *
+ * @param nextBluePrint: KeyguardBlueprint the blueprint we will transition to.
+ */
+ fun removeViews(nextBlueprint: KeyguardBlueprint, constraintLayout: ConstraintLayout) {
+ sections.subtract(nextBlueprint.sections).forEach { it.removeViews(constraintLayout) }
}
fun applyConstraints(constraintSet: ConstraintSet) {
sections.forEach { it.applyConstraints(constraintSet) }
}
-
- fun onDestroy() {
- sections.forEach { it.onDestroy() }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardSection.kt
index 19f50de..48a2146 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardSection.kt
@@ -23,8 +23,34 @@
* Lower level modules that determine constraints for a particular section in the lockscreen root
* view.
*/
-interface KeyguardSection {
- fun addViews(constraintLayout: ConstraintLayout)
- fun applyConstraints(constraintSet: ConstraintSet)
- fun onDestroy() {}
+abstract class KeyguardSection {
+ /** Adds the views to the root view. */
+ abstract fun addViews(constraintLayout: ConstraintLayout)
+ /** Binds the views to data. */
+ abstract fun bindData(constraintLayout: ConstraintLayout)
+ /** Applies layout constraints to the view in respect to the root view. */
+ abstract fun applyConstraints(constraintSet: ConstraintSet)
+ /** Removes views and does any data binding destruction. */
+ abstract fun removeViews(constraintLayout: ConstraintLayout)
+
+ /**
+ * Defines equality as same class.
+ *
+ * This is to enable set operations to be done as an optimization to blueprint transitions.
+ */
+ override fun equals(other: Any?): Boolean {
+ other?.let { other ->
+ return this::class == other::class
+ }
+ return false
+ }
+
+ /**
+ * Defines hashcode as class.
+ *
+ * This is to enable set operations to be done as an optimization to blueprint transitions.
+ */
+ override fun hashCode(): Int {
+ return this::class.hashCode()
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
index c340e5d..78b72a9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
@@ -21,7 +21,6 @@
import android.util.Log
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
-import androidx.core.view.children
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBlueprintViewModel
@@ -37,26 +36,20 @@
repeatOnLifecycle(Lifecycle.State.CREATED) {
launch {
viewModel.blueprint.collect { blueprint ->
+ val prevBluePrint = viewModel.currentBluePrint
Trace.beginSection("KeyguardBlueprint#applyBlueprint")
Log.d(TAG, "applying blueprint: $blueprint")
- if (blueprint != viewModel.currentBluePrint) {
- viewModel.currentBluePrint?.onDestroy()
+ // Add and remove views of sections that are not contained by the other.
+ prevBluePrint?.removeViews(blueprint, constraintLayout)
+ blueprint.addViews(prevBluePrint, constraintLayout)
+
+ ConstraintSet().apply {
+ clone(constraintLayout)
+ val emptyLayout = ConstraintSet.Layout()
+ knownIds.forEach { getConstraint(it).layout.copyFrom(emptyLayout) }
+ blueprint.applyConstraints(this)
+ applyTo(constraintLayout)
}
- val constraintSet =
- ConstraintSet().apply {
- clone(constraintLayout)
- val emptyLayout = ConstraintSet.Layout()
- knownIds.forEach {
- getConstraint(it).layout.copyFrom(emptyLayout)
- }
- blueprint.addViews(constraintLayout)
- blueprint.applyConstraints(this)
- applyTo(constraintLayout)
- }
- // Remove all unconstrained views.
- constraintLayout.children
- .filterNot { constraintSet.knownIds.contains(it.id) }
- .forEach { constraintLayout.removeView(it) }
viewModel.currentBluePrint = blueprint
Trace.endSection()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt
index 5a15fc2..85b2b82 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt
@@ -55,7 +55,7 @@
override val id: String = DEFAULT
override val sections =
- arrayOf(
+ setOf(
defaultIndicationAreaSection,
defaultLockIconSection,
defaultShortcutsSection,
@@ -66,9 +66,12 @@
splitShadeGuidelines,
)
- override fun addViews(constraintLayout: ConstraintLayout) {
+ override fun addViews(
+ previousBlueprint: KeyguardBlueprint?,
+ constraintLayout: ConstraintLayout
+ ) {
if (featureFlags.isEnabled(Flags.LAZY_INFLATE_KEYGUARD)) {
- super.addViews(constraintLayout)
+ super.addViews(previousBlueprint, constraintLayout)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt
index 5ef625e..bb3af6c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt
@@ -46,7 +46,7 @@
override val id: String = SHORTCUTS_BESIDE_UDFPS
override val sections =
- arrayOf(
+ setOf(
defaultIndicationAreaSection,
defaultLockIconSection,
defaultAmbientIndicationAreaSection,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt
index 587c6b7..79b7157 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt
@@ -18,6 +18,8 @@
package com.android.systemui.keyguard.ui.view.layout.sections
import android.content.res.Resources
+import android.view.View
+import android.widget.ImageView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
@@ -25,7 +27,9 @@
import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
import androidx.constraintlayout.widget.ConstraintSet.RIGHT
import androidx.constraintlayout.widget.ConstraintSet.TOP
+import androidx.core.content.res.ResourcesCompat
import com.android.systemui.R
+import com.android.systemui.animation.view.LaunchableImageView
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
@@ -49,12 +53,19 @@
private val falsingManager: FalsingManager,
private val indicationController: KeyguardIndicationController,
private val vibratorHelper: VibratorHelper,
-) : BaseShortcutsSection(), KeyguardSection {
+) : KeyguardSection() {
+ private var leftShortcutHandle: KeyguardQuickAffordanceViewBinder.Binding? = null
+ private var rightShortcutHandle: KeyguardQuickAffordanceViewBinder.Binding? = null
override fun addViews(constraintLayout: ConstraintLayout) {
if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
addLeftShortcut(constraintLayout)
addRightShortcut(constraintLayout)
+ }
+ }
+
+ override fun bindData(constraintLayout: ConstraintLayout) {
+ if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
leftShortcutHandle =
KeyguardQuickAffordanceViewBinder.bind(
constraintLayout.requireViewById(R.id.start_button),
@@ -98,4 +109,67 @@
connect(R.id.end_button, BOTTOM, R.id.lock_icon_view, BOTTOM)
}
}
+
+ override fun removeViews(constraintLayout: ConstraintLayout) {
+ leftShortcutHandle?.destroy()
+ rightShortcutHandle?.destroy()
+ constraintLayout.removeView(R.id.start_button)
+ constraintLayout.removeView(R.id.end_button)
+ }
+
+ private fun addLeftShortcut(constraintLayout: ConstraintLayout) {
+ val padding =
+ constraintLayout.resources.getDimensionPixelSize(
+ R.dimen.keyguard_affordance_fixed_padding
+ )
+ val view =
+ LaunchableImageView(constraintLayout.context, null).apply {
+ id = R.id.start_button
+ scaleType = ImageView.ScaleType.FIT_CENTER
+ background =
+ ResourcesCompat.getDrawable(
+ context.resources,
+ R.drawable.keyguard_bottom_affordance_bg,
+ context.theme
+ )
+ foreground =
+ ResourcesCompat.getDrawable(
+ context.resources,
+ R.drawable.keyguard_bottom_affordance_selected_border,
+ context.theme
+ )
+ visibility = View.INVISIBLE
+ setPadding(padding, padding, padding, padding)
+ }
+ constraintLayout.addView(view)
+ }
+
+ private fun addRightShortcut(constraintLayout: ConstraintLayout) {
+ if (constraintLayout.findViewById<View>(R.id.end_button) != null) return
+
+ val padding =
+ constraintLayout.resources.getDimensionPixelSize(
+ R.dimen.keyguard_affordance_fixed_padding
+ )
+ val view =
+ LaunchableImageView(constraintLayout.context, null).apply {
+ id = R.id.end_button
+ scaleType = ImageView.ScaleType.FIT_CENTER
+ background =
+ ResourcesCompat.getDrawable(
+ context.resources,
+ R.drawable.keyguard_bottom_affordance_bg,
+ context.theme
+ )
+ foreground =
+ ResourcesCompat.getDrawable(
+ context.resources,
+ R.drawable.keyguard_bottom_affordance_selected_border,
+ context.theme
+ )
+ visibility = View.INVISIBLE
+ setPadding(padding, padding, padding, padding)
+ }
+ constraintLayout.addView(view)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/BaseShortcutsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/BaseShortcutsSection.kt
deleted file mode 100644
index db0cf5a..0000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/BaseShortcutsSection.kt
+++ /dev/null
@@ -1,101 +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.
- *
- */
-
-package com.android.systemui.keyguard.ui.view.layout.sections
-
-import android.view.View
-import android.widget.ImageView
-import androidx.constraintlayout.widget.ConstraintLayout
-import androidx.constraintlayout.widget.ConstraintSet
-import androidx.core.content.res.ResourcesCompat
-import com.android.systemui.R
-import com.android.systemui.animation.view.LaunchableImageView
-import com.android.systemui.keyguard.shared.model.KeyguardSection
-import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder
-
-/** Base class for sections that add lockscreen shortcuts. */
-abstract class BaseShortcutsSection : KeyguardSection {
- protected open var leftShortcutHandle: KeyguardQuickAffordanceViewBinder.Binding? = null
- protected open var rightShortcutHandle: KeyguardQuickAffordanceViewBinder.Binding? = null
-
- override fun addViews(constraintLayout: ConstraintLayout) {}
-
- override fun applyConstraints(constraintSet: ConstraintSet) {}
-
- override fun onDestroy() {
- leftShortcutHandle?.destroy()
- rightShortcutHandle?.destroy()
- }
-
- protected open fun addLeftShortcut(constraintLayout: ConstraintLayout) {
- if (constraintLayout.findViewById<View>(R.id.start_button) != null) return
-
- val padding =
- constraintLayout.resources.getDimensionPixelSize(
- R.dimen.keyguard_affordance_fixed_padding
- )
- val view =
- LaunchableImageView(constraintLayout.context, null).apply {
- id = R.id.start_button
- scaleType = ImageView.ScaleType.FIT_CENTER
- background =
- ResourcesCompat.getDrawable(
- context.resources,
- R.drawable.keyguard_bottom_affordance_bg,
- context.theme
- )
- foreground =
- ResourcesCompat.getDrawable(
- context.resources,
- R.drawable.keyguard_bottom_affordance_selected_border,
- context.theme
- )
- visibility = View.INVISIBLE
- setPadding(padding, padding, padding, padding)
- }
- constraintLayout.addView(view)
- }
-
- protected open fun addRightShortcut(constraintLayout: ConstraintLayout) {
- if (constraintLayout.findViewById<View>(R.id.end_button) != null) return
-
- val padding =
- constraintLayout.resources.getDimensionPixelSize(
- R.dimen.keyguard_affordance_fixed_padding
- )
- val view =
- LaunchableImageView(constraintLayout.context, null).apply {
- id = R.id.end_button
- scaleType = ImageView.ScaleType.FIT_CENTER
- background =
- ResourcesCompat.getDrawable(
- context.resources,
- R.drawable.keyguard_bottom_affordance_bg,
- context.theme
- )
- foreground =
- ResourcesCompat.getDrawable(
- context.resources,
- R.drawable.keyguard_bottom_affordance_selected_border,
- context.theme
- )
- visibility = View.INVISIBLE
- setPadding(padding, padding, padding, padding)
- }
- constraintLayout.addView(view)
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultAmbientIndicationAreaSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultAmbientIndicationAreaSection.kt
index f8455c5..ce86e97 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultAmbientIndicationAreaSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultAmbientIndicationAreaSection.kt
@@ -18,7 +18,6 @@
package com.android.systemui.keyguard.ui.view.layout.sections
import android.view.LayoutInflater
-import android.view.View
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
@@ -46,19 +45,21 @@
private val featureFlags: FeatureFlags,
private val keyguardAmbientIndicationViewModel: KeyguardAmbientIndicationViewModel,
private val keyguardRootViewModel: KeyguardRootViewModel,
-) : KeyguardSection {
+) : KeyguardSection() {
private var ambientIndicationAreaHandle: KeyguardAmbientIndicationAreaViewBinder.Binding? = null
override fun addViews(constraintLayout: ConstraintLayout) {
if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
- if (constraintLayout.findViewById<View>(R.id.ambient_indication_container) == null) {
- val view =
- LayoutInflater.from(constraintLayout.context)
- .inflate(R.layout.ambient_indication, constraintLayout, false)
+ val view =
+ LayoutInflater.from(constraintLayout.context)
+ .inflate(R.layout.ambient_indication, constraintLayout, false)
- constraintLayout.addView(view)
- }
+ constraintLayout.addView(view)
+ }
+ }
+ override fun bindData(constraintLayout: ConstraintLayout) {
+ if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
ambientIndicationAreaHandle =
KeyguardAmbientIndicationAreaViewBinder.bind(
constraintLayout,
@@ -94,7 +95,9 @@
}
}
- override fun onDestroy() {
+ override fun removeViews(constraintLayout: ConstraintLayout) {
ambientIndicationAreaHandle?.destroy()
+
+ constraintLayout.removeView(R.id.ambient_indication_container)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt
index f04bfc6..a45223c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt
@@ -18,7 +18,6 @@
package com.android.systemui.keyguard.ui.view.layout.sections
import android.content.Context
-import android.view.View
import android.view.ViewGroup
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
@@ -42,17 +41,19 @@
private val keyguardRootViewModel: KeyguardRootViewModel,
private val indicationController: KeyguardIndicationController,
private val featureFlags: FeatureFlags,
-) : KeyguardSection {
+) : KeyguardSection() {
private val indicationAreaViewId = R.id.keyguard_indication_area
private var indicationAreaHandle: DisposableHandle? = null
override fun addViews(constraintLayout: ConstraintLayout) {
if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
- if (constraintLayout.findViewById<View>(indicationAreaViewId) == null) {
- val view = KeyguardIndicationArea(context, null)
- constraintLayout.addView(view)
- }
+ val view = KeyguardIndicationArea(context, null)
+ constraintLayout.addView(view)
+ }
+ }
+ override fun bindData(constraintLayout: ConstraintLayout) {
+ if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
indicationAreaHandle =
KeyguardIndicationAreaBinder.bind(
constraintLayout,
@@ -90,7 +91,8 @@
}
}
- override fun onDestroy() {
+ override fun removeViews(constraintLayout: ConstraintLayout) {
indicationAreaHandle?.dispose()
+ constraintLayout.removeView(indicationAreaViewId)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSection.kt
index 3d62f3f..3e91d93 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSection.kt
@@ -47,19 +47,23 @@
private val notificationPanelView: NotificationPanelView,
private val featureFlags: FeatureFlags,
private val lockIconViewController: LockIconViewController,
-) : KeyguardSection {
+) : KeyguardSection() {
private val lockIconViewId = R.id.lock_icon_view
override fun addViews(constraintLayout: ConstraintLayout) {
- if (featureFlags.isEnabled(Flags.MIGRATE_LOCK_ICON)) {
- notificationPanelView.findViewById<View>(R.id.lock_icon_view).let {
- notificationPanelView.removeView(it)
- }
- if (constraintLayout.findViewById<View>(R.id.lock_icon_view) == null) {
- val view = LockIconView(context, null).apply { id = R.id.lock_icon_view }
- constraintLayout.addView(view)
- lockIconViewController.setLockIconView(view)
- }
+ if (!featureFlags.isEnabled(Flags.MIGRATE_LOCK_ICON)) {
+ return
+ }
+ notificationPanelView.findViewById<View>(R.id.lock_icon_view).let {
+ notificationPanelView.removeView(it)
+ }
+ val view = LockIconView(context, null).apply { id = R.id.lock_icon_view }
+ constraintLayout.addView(view)
+ }
+
+ override fun bindData(constraintLayout: ConstraintLayout) {
+ constraintLayout.findViewById<LockIconView?>(R.id.lock_icon_view)?.let {
+ lockIconViewController.setLockIconView(it)
}
}
@@ -92,6 +96,10 @@
}
}
+ override fun removeViews(constraintLayout: ConstraintLayout) {
+ constraintLayout.removeView(R.id.lock_icon_view)
+ }
+
@VisibleForTesting
internal fun centerLockIcon(center: Point, radius: Float, constraintSet: ConstraintSet) {
val sensorRect =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt
index a203e41d..59c5d78 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt
@@ -40,22 +40,30 @@
private val sharedNotificationContainer: SharedNotificationContainer,
private val sharedNotificationContainerViewModel: SharedNotificationContainerViewModel,
private val controller: NotificationStackScrollLayoutController,
-) : KeyguardSection {
+) : KeyguardSection() {
override fun addViews(constraintLayout: ConstraintLayout) {
+ if (!featureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ return
+ }
+ // This moves the existing NSSL view to a different parent, as the controller is a
+ // singleton and recreating it has other bad side effects
+ notificationPanelView.findViewById<View?>(R.id.notification_stack_scroller)?.let {
+ (it.parent as ViewGroup).removeView(it)
+ sharedNotificationContainer.addNotificationStackScrollLayout(it)
+ }
+ }
+
+ override fun bindData(constraintLayout: ConstraintLayout) {
if (featureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
- // This moves the existing NSSL view to a different parent, as the controller is a
- // singleton and recreating it has other bad side effects
- notificationPanelView.findViewById<View?>(R.id.notification_stack_scroller)?.let {
- (it.parent as ViewGroup).removeView(it)
- sharedNotificationContainer.addNotificationStackScrollLayout(it)
- SharedNotificationContainerBinder.bind(
- sharedNotificationContainer,
- sharedNotificationContainerViewModel,
- controller,
- )
- }
+ SharedNotificationContainerBinder.bind(
+ sharedNotificationContainer,
+ sharedNotificationContainerViewModel,
+ controller,
+ )
}
}
override fun applyConstraints(constraintSet: ConstraintSet) {}
+
+ override fun removeViews(constraintLayout: ConstraintLayout) {}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultSettingsPopupMenuSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultSettingsPopupMenuSection.kt
index 660cc96..b25f9af 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultSettingsPopupMenuSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultSettingsPopupMenuSection.kt
@@ -49,23 +49,26 @@
private val keyguardSettingsMenuViewModel: KeyguardSettingsMenuViewModel,
private val vibratorHelper: VibratorHelper,
private val activityStarter: ActivityStarter,
-) : KeyguardSection {
+) : KeyguardSection() {
private var settingsPopupMenuHandle: DisposableHandle? = null
override fun addViews(constraintLayout: ConstraintLayout) {
- if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
- if (constraintLayout.findViewById<View?>(R.id.keyguard_settings_button) == null) {
- val view =
- LayoutInflater.from(constraintLayout.context)
- .inflate(R.layout.keyguard_settings_popup_menu, constraintLayout, false)
- .apply {
- id = R.id.keyguard_settings_button
- isVisible = false
- alpha = 0f
- } as LaunchableLinearLayout
- constraintLayout.addView(view)
- }
+ if (!featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
+ return
+ }
+ val view =
+ LayoutInflater.from(constraintLayout.context)
+ .inflate(R.layout.keyguard_settings_popup_menu, constraintLayout, false)
+ .apply {
+ id = R.id.keyguard_settings_button
+ isVisible = false
+ alpha = 0f
+ } as LaunchableLinearLayout
+ constraintLayout.addView(view)
+ }
+ override fun bindData(constraintLayout: ConstraintLayout) {
+ if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
settingsPopupMenuHandle =
KeyguardSettingsViewBinder.bind(
constraintLayout.requireViewById<View>(R.id.keyguard_settings_button),
@@ -100,7 +103,8 @@
}
}
- override fun onDestroy() {
+ override fun removeViews(constraintLayout: ConstraintLayout) {
settingsPopupMenuHandle?.dispose()
+ constraintLayout.removeView(R.id.keyguard_settings_button)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt
index 965910a..c498055 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt
@@ -18,13 +18,17 @@
package com.android.systemui.keyguard.ui.view.layout.sections
import android.content.res.Resources
+import android.view.View
+import android.widget.ImageView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
import androidx.constraintlayout.widget.ConstraintSet.LEFT
import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
import androidx.constraintlayout.widget.ConstraintSet.RIGHT
+import androidx.core.content.res.ResourcesCompat
import com.android.systemui.R
+import com.android.systemui.animation.view.LaunchableImageView
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
@@ -48,12 +52,19 @@
private val falsingManager: FalsingManager,
private val indicationController: KeyguardIndicationController,
private val vibratorHelper: VibratorHelper,
-) : BaseShortcutsSection(), KeyguardSection {
+) : KeyguardSection() {
+ private var leftShortcutHandle: KeyguardQuickAffordanceViewBinder.Binding? = null
+ private var rightShortcutHandle: KeyguardQuickAffordanceViewBinder.Binding? = null
override fun addViews(constraintLayout: ConstraintLayout) {
if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
addLeftShortcut(constraintLayout)
addRightShortcut(constraintLayout)
+ }
+ }
+
+ override fun bindData(constraintLayout: ConstraintLayout) {
+ if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
leftShortcutHandle =
KeyguardQuickAffordanceViewBinder.bind(
constraintLayout.requireViewById(R.id.start_button),
@@ -98,6 +109,69 @@
}
}
+ override fun removeViews(constraintLayout: ConstraintLayout) {
+ leftShortcutHandle?.destroy()
+ rightShortcutHandle?.destroy()
+ constraintLayout.removeView(R.id.start_button)
+ constraintLayout.removeView(R.id.end_button)
+ }
+
+ private fun addLeftShortcut(constraintLayout: ConstraintLayout) {
+ val padding =
+ constraintLayout.resources.getDimensionPixelSize(
+ R.dimen.keyguard_affordance_fixed_padding
+ )
+ val view =
+ LaunchableImageView(constraintLayout.context, null).apply {
+ id = R.id.start_button
+ scaleType = ImageView.ScaleType.FIT_CENTER
+ background =
+ ResourcesCompat.getDrawable(
+ context.resources,
+ R.drawable.keyguard_bottom_affordance_bg,
+ context.theme
+ )
+ foreground =
+ ResourcesCompat.getDrawable(
+ context.resources,
+ R.drawable.keyguard_bottom_affordance_selected_border,
+ context.theme
+ )
+ visibility = View.INVISIBLE
+ setPadding(padding, padding, padding, padding)
+ }
+ constraintLayout.addView(view)
+ }
+
+ private fun addRightShortcut(constraintLayout: ConstraintLayout) {
+ if (constraintLayout.findViewById<View>(R.id.end_button) != null) return
+
+ val padding =
+ constraintLayout.resources.getDimensionPixelSize(
+ R.dimen.keyguard_affordance_fixed_padding
+ )
+ val view =
+ LaunchableImageView(constraintLayout.context, null).apply {
+ id = R.id.end_button
+ scaleType = ImageView.ScaleType.FIT_CENTER
+ background =
+ ResourcesCompat.getDrawable(
+ context.resources,
+ R.drawable.keyguard_bottom_affordance_bg,
+ context.theme
+ )
+ foreground =
+ ResourcesCompat.getDrawable(
+ context.resources,
+ R.drawable.keyguard_bottom_affordance_selected_border,
+ context.theme
+ )
+ visibility = View.INVISIBLE
+ setPadding(padding, padding, padding, padding)
+ }
+ constraintLayout.addView(view)
+ }
+
/** Method to add shortcuts without applying any data binding. */
fun addShortcutViews(constraintLayout: ConstraintLayout) {
addLeftShortcut(constraintLayout)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt
index 321d7a7..b144f7a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt
@@ -55,31 +55,34 @@
private val keyguardViewConfigurator: Lazy<KeyguardViewConfigurator>,
private val notificationPanelViewController: Lazy<NotificationPanelViewController>,
private val keyguardMediaController: KeyguardMediaController,
-) : KeyguardSection {
+) : KeyguardSection() {
private val statusViewId = R.id.keyguard_status_view
- @OptIn(ExperimentalCoroutinesApi::class)
override fun addViews(constraintLayout: ConstraintLayout) {
+ if (!featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) {
+ return
+ }
// At startup, 2 views with the ID `R.id.keyguard_status_view` will be available.
// Disable one of them
- if (featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) {
- notificationPanelView.findViewById<View>(statusViewId)?.let {
- notificationPanelView.removeView(it)
- }
- if (constraintLayout.findViewById<View>(statusViewId) == null) {
- val keyguardStatusView =
- (LayoutInflater.from(context)
- .inflate(R.layout.keyguard_status_view, constraintLayout, false)
- as KeyguardStatusView)
- .apply { clipChildren = false }
+ notificationPanelView.findViewById<View>(statusViewId)?.let {
+ notificationPanelView.removeView(it)
+ }
+ val keyguardStatusView =
+ (LayoutInflater.from(context)
+ .inflate(R.layout.keyguard_status_view, constraintLayout, false)
+ as KeyguardStatusView)
+ .apply { clipChildren = false }
+ constraintLayout.addView(keyguardStatusView)
+ }
- val statusViewComponent =
- keyguardStatusViewComponentFactory.build(keyguardStatusView)
+ override fun bindData(constraintLayout: ConstraintLayout) {
+ if (featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) {
+ constraintLayout.findViewById<KeyguardStatusView?>(R.id.keyguard_status_view)?.let {
+ val statusViewComponent = keyguardStatusViewComponentFactory.build(it)
val controller = statusViewComponent.keyguardStatusViewController
controller.init()
- constraintLayout.addView(keyguardStatusView)
keyguardMediaController.attachSplitShadeContainer(
- keyguardStatusView.requireViewById<ViewGroup>(R.id.status_view_media_container)
+ it.requireViewById<ViewGroup>(R.id.status_view_media_container)
)
keyguardViewConfigurator.get().keyguardStatusViewController = controller
notificationPanelViewController.get().updateStatusBarViewController()
@@ -107,7 +110,8 @@
}
@OptIn(ExperimentalCoroutinesApi::class)
- override fun onDestroy() {
+ override fun removeViews(constraintLayout: ConstraintLayout) {
+ constraintLayout.removeView(statusViewId)
keyguardViewConfigurator.get().keyguardStatusViewController = null
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/Extensions.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/Extensions.kt
new file mode 100644
index 0000000..94332d2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/Extensions.kt
@@ -0,0 +1,8 @@
+package com.android.systemui.keyguard.ui.view.layout.sections
+
+import android.view.View
+import androidx.constraintlayout.widget.ConstraintLayout
+
+internal fun ConstraintLayout.removeView(viewId: Int) {
+ findViewById<View?>(viewId)?.let { removeView(it) }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeGuidelines.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeGuidelines.kt
index bd629d5..5e3ea05 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeGuidelines.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeGuidelines.kt
@@ -17,7 +17,6 @@
package com.android.systemui.keyguard.ui.view.layout.sections
-import android.content.Context
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.constraintlayout.widget.ConstraintSet.VERTICAL
@@ -25,9 +24,11 @@
import com.android.systemui.keyguard.shared.model.KeyguardSection
import javax.inject.Inject
-class SplitShadeGuidelines @Inject constructor(private val context: Context) : KeyguardSection {
+class SplitShadeGuidelines @Inject constructor() : KeyguardSection() {
override fun addViews(constraintLayout: ConstraintLayout) {}
+ override fun bindData(constraintLayout: ConstraintLayout) {}
+
override fun applyConstraints(constraintSet: ConstraintSet) {
constraintSet.apply {
// For use on large screens, it will provide a guideline vertically in the center to
@@ -36,4 +37,6 @@
setGuidelinePercent(R.id.split_shade_guideline, 0.5f)
}
}
+
+ override fun removeViews(constraintLayout: ConstraintLayout) {}
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 8db7abf..c199904 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -163,6 +163,7 @@
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
+import com.android.systemui.shade.data.repository.ShadeRepository;
import com.android.systemui.shade.transition.ShadeTransitionController;
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
import com.android.systemui.shared.system.QuickStepContract;
@@ -350,6 +351,7 @@
private final Interpolator mBounceInterpolator;
private final NotificationShadeWindowController mNotificationShadeWindowController;
private final ShadeExpansionStateManager mShadeExpansionStateManager;
+ private final ShadeRepository mShadeRepository;
private final FalsingTapListener mFalsingTapListener = this::falsingAdditionalTapRequired;
private final AccessibilityDelegate mAccessibilityDelegate = new ShadeAccessibilityDelegate();
private final NotificationGutsManager mGutsManager;
@@ -710,7 +712,8 @@
VibratorHelper vibratorHelper,
LatencyTracker latencyTracker,
PowerManager powerManager,
- AccessibilityManager accessibilityManager, @DisplayId int displayId,
+ AccessibilityManager accessibilityManager,
+ @DisplayId int displayId,
KeyguardUpdateMonitor keyguardUpdateMonitor,
MetricsLogger metricsLogger,
ShadeLogger shadeLogger,
@@ -746,6 +749,7 @@
ScreenOffAnimationController screenOffAnimationController,
LockscreenGestureLogger lockscreenGestureLogger,
ShadeExpansionStateManager shadeExpansionStateManager,
+ ShadeRepository shadeRepository,
Optional<SysUIUnfoldComponent> unfoldComponent,
SysUiState sysUiState,
Provider<KeyguardBottomAreaViewController> keyguardBottomAreaViewControllerProvider,
@@ -788,6 +792,7 @@
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mLockscreenGestureLogger = lockscreenGestureLogger;
mShadeExpansionStateManager = shadeExpansionStateManager;
+ mShadeRepository = shadeRepository;
mShadeLog = shadeLogger;
mGutsManager = gutsManager;
mDreamingToLockscreenTransitionViewModel = dreamingToLockscreenTransitionViewModel;
@@ -3952,6 +3957,7 @@
}
mExpandedFraction = Math.min(1f,
maxPanelHeight == 0 ? 0 : mExpandedHeight / maxPanelHeight);
+ mShadeRepository.setLegacyShadeExpansion(mExpandedFraction);
mQsController.setShadeExpansion(mExpandedHeight, mExpandedFraction);
mExpansionDragDownAmountPx = h;
mAmbientState.setExpansionFraction(mExpandedFraction);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
index 6585fcb..ed719a65 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
@@ -77,7 +77,9 @@
layoutInsetController: NotificationInsetsController,
): WindowRootView {
return if (
- featureFlags.isEnabled(Flags.SCENE_CONTAINER) && ComposeFacade.isComposeAvailable()
+ Flags.SCENE_CONTAINER_ENABLED &&
+ featureFlags.isEnabled(Flags.SCENE_CONTAINER) &&
+ ComposeFacade.isComposeAvailable()
) {
val sceneWindowRootView =
layoutInflater.inflate(R.layout.scene_window_root, null) as SceneWindowRootView
diff --git a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
index 5a8be1e..509921f 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
@@ -34,21 +34,48 @@
/** ShadeModel information regarding shade expansion events */
val shadeModel: Flow<ShadeModel>
- /** Amount qs has expanded. Quick Settings can be expanded without the full shade expansion. */
+ /**
+ * Amount qs has expanded, [0-1]. 0 means fully collapsed, 1 means fully expanded. Quick
+ * Settings can be expanded without the full shade expansion.
+ */
val qsExpansion: StateFlow<Float>
- /** The amount the shade has expanded */
- val shadeExpansion: StateFlow<Float>
+ /**
+ * The amount the lockscreen shade has dragged down by the user, [0-1]. 0 means fully collapsed,
+ * 1 means fully expanded.
+ */
+ val lockscreenShadeExpansion: StateFlow<Float>
+
+ /**
+ * NotificationPanelViewController.mExpandedFraction as a StateFlow. This nominally represents
+ * the amount the shade has expanded 0-1 like many other flows in this repo, but there are cases
+ * where its value will be 1 and no shade will be rendered, e.g. whenever the keyguard is
+ * visible and when quick settings is expanded. The confusing nature and impending deletion of
+ * this makes it unsuitable for future development, so usage is discouraged.
+ */
+ @Deprecated("Use ShadeInteractor.shadeExpansion instead")
+ val legacyShadeExpansion: StateFlow<Float>
/** Amount shade has expanded with regard to the UDFPS location */
val udfpsTransitionToFullShadeProgress: StateFlow<Float>
/** The amount QS has expanded without notifications */
fun setQsExpansion(qsExpansion: Float)
+
fun setUdfpsTransitionToFullShadeProgress(progress: Float)
- /** The amount the shade has expanded, [0-1]. 0 means fully collapsed, 1 means fully expanded */
- fun setShadeExpansion(expansion: Float)
+ /**
+ * Set the amount the shade has dragged down by the user, [0-1]. 0 means fully collapsed, 1
+ * means fully expanded.
+ */
+ fun setLockscreenShadeExpansion(lockscreenShadeExpansion: Float)
+
+ /**
+ * Set the legacy expansion value. This should only be called whenever the value of
+ * NotificationPanelViewController.mExpandedFraction changes or in tests.
+ */
+ @Deprecated("Should only be called by NPVC and tests")
+ fun setLegacyShadeExpansion(expandedFraction: Float)
}
/** Business logic for shade interactions */
@@ -84,18 +111,29 @@
private val _qsExpansion = MutableStateFlow(0f)
override val qsExpansion: StateFlow<Float> = _qsExpansion.asStateFlow()
- private val _shadeExpansion = MutableStateFlow(0f)
- override val shadeExpansion: StateFlow<Float> = _shadeExpansion.asStateFlow()
+ private val _lockscreenShadeExpansion = MutableStateFlow(0f)
+ override val lockscreenShadeExpansion: StateFlow<Float> =
+ _lockscreenShadeExpansion.asStateFlow()
private var _udfpsTransitionToFullShadeProgress = MutableStateFlow(0f)
override val udfpsTransitionToFullShadeProgress: StateFlow<Float> =
_udfpsTransitionToFullShadeProgress.asStateFlow()
+
+ private val _legacyShadeExpansion = MutableStateFlow(0f)
+ @Deprecated("Use ShadeInteractor.shadeExpansion instead")
+ override val legacyShadeExpansion: StateFlow<Float> = _legacyShadeExpansion.asStateFlow()
+
override fun setQsExpansion(qsExpansion: Float) {
_qsExpansion.value = qsExpansion
}
- override fun setShadeExpansion(expansion: Float) {
- _shadeExpansion.value = expansion
+ @Deprecated("Should only be called by NPVC and tests")
+ override fun setLegacyShadeExpansion(expandedFraction: Float) {
+ _legacyShadeExpansion.value = expandedFraction
+ }
+
+ override fun setLockscreenShadeExpansion(lockscreenShadeExpansion: Float) {
+ _lockscreenShadeExpansion.value = lockscreenShadeExpansion
}
override fun setUdfpsTransitionToFullShadeProgress(progress: Float) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
index 288d32e..fd63b89 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
@@ -22,6 +22,7 @@
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.statusbar.disableflags.data.repository.DisableFlagsRepository
+import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
import com.android.systemui.statusbar.pipeline.mobile.data.repository.UserSetupRepository
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.user.domain.interactor.UserInteractor
@@ -31,6 +32,7 @@
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
@@ -45,6 +47,7 @@
userSetupRepository: UserSetupRepository,
deviceProvisionedController: DeviceProvisionedController,
userInteractor: UserInteractor,
+ sharedNotificationContainerInteractor: SharedNotificationContainerInteractor,
repository: ShadeRepository,
) {
/** Emits true if the shade is currently allowed and false otherwise. */
@@ -53,16 +56,31 @@
.map { it.isShadeEnabled() }
.stateIn(scope, SharingStarted.Eagerly, initialValue = false)
+ /**
+ * Whether split shade, the combined notifications and quick settings shade used for large
+ * screens, is enabled.
+ */
+ val splitShadeEnabled: Flow<Boolean> =
+ sharedNotificationContainerInteractor.configurationBasedDimensions
+ .map { dimens -> dimens.useSplitShade }
+ .distinctUntilChanged()
+
/** The amount [0-1] that the shade has been opened */
val shadeExpansion: Flow<Float> =
- combine(repository.shadeExpansion, keyguardRepository.statusBarState) {
- shadeExpansion,
- statusBarState ->
- // This is required, as shadeExpansion gets reset to 0f even with the shade open
- if (statusBarState == StatusBarState.SHADE_LOCKED) {
- 1f
- } else {
- shadeExpansion
+ combine(
+ repository.lockscreenShadeExpansion,
+ keyguardRepository.statusBarState,
+ repository.legacyShadeExpansion,
+ repository.qsExpansion,
+ splitShadeEnabled
+ ) { dragDownAmount, statusBarState, legacyShadeExpansion, qsExpansion, splitShadeEnabled ->
+ when (statusBarState) {
+ // legacyShadeExpansion is 1 instead of 0 when QS is expanded
+ StatusBarState.SHADE ->
+ if (!splitShadeEnabled && qsExpansion > 0f) 0f else legacyShadeExpansion
+ StatusBarState.KEYGUARD -> dragDownAmount
+ // This is required, as shadeExpansion gets reset to 0f even with the shade open
+ StatusBarState.SHADE_LOCKED -> 1f
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index f004982..73bbbca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -447,7 +447,7 @@
if (!nsslController.isInLockedDownShade() || field == 0f || forceApplyAmount) {
fractionToShade =
MathUtils.saturate(dragDownAmount / notificationShelfTransitionDistance)
- shadeRepository.setShadeExpansion(fractionToShade)
+ shadeRepository.setLockscreenShadeExpansion(fractionToShade)
nsslController.setTransitionToFullShadeAmount(fractionToShade)
qsTransitionController.dragDownAmount = value
@@ -857,12 +857,12 @@
MotionEvent.ACTION_MOVE -> {
val h = y - initialTouchY
// Adjust the touch slop if another gesture may be being performed.
- val touchSlop = if (event.classification
- == MotionEvent.CLASSIFICATION_AMBIGUOUS_GESTURE) {
- touchSlop * slopMultiplier
- } else {
- touchSlop
- }
+ val touchSlop =
+ if (event.classification == MotionEvent.CLASSIFICATION_AMBIGUOUS_GESTURE) {
+ touchSlop * slopMultiplier
+ } else {
+ touchSlop
+ }
if (h > touchSlop && h > Math.abs(x - initialTouchX)) {
isDraggingDown = true
captureStartingChild(initialTouchX, initialTouchY)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt
index be9f6ca..09cb929 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt
@@ -33,7 +33,7 @@
@Test
fun addView() {
val constraintLayout = ConstraintLayout(context, null)
- blueprint.addViews(constraintLayout)
+ blueprint.addViews(null, constraintLayout)
verify(widgetSection).addViews(constraintLayout)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
index 3b4eab2..bf57ecb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
@@ -25,6 +25,7 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
import com.android.systemui.keyguard.ui.view.KeyguardRootView
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultAmbientIndicationAreaSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultIndicationAreaSection
@@ -34,10 +35,12 @@
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultShortcutsSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusViewSection
import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeGuidelines
+import com.android.systemui.util.mockito.whenever
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
+import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@@ -81,7 +84,7 @@
@Test
fun addViews() {
val constraintLayout = ConstraintLayout(context, null)
- underTest.addViews(constraintLayout)
+ underTest.addViews(null, constraintLayout)
underTest.sections.forEach { verify(it, never()).addViews(constraintLayout) }
}
@@ -89,11 +92,35 @@
fun addViews_lazyInflateFlagOn() {
featureFlags.set(Flags.LAZY_INFLATE_KEYGUARD, true)
val constraintLayout = ConstraintLayout(context, null)
- underTest.addViews(constraintLayout)
+ underTest.addViews(null, constraintLayout)
underTest.sections.forEach { verify(it).addViews(constraintLayout) }
}
@Test
+ fun addViews_withPrevBlueprint() {
+ val prevBlueprint = mock(KeyguardBlueprint::class.java)
+ whenever(prevBlueprint.sections)
+ .thenReturn(underTest.sections.minus(defaultLockIconSection))
+ featureFlags.set(Flags.LAZY_INFLATE_KEYGUARD, true)
+ val constraintLayout = ConstraintLayout(context, null)
+ underTest.addViews(prevBlueprint, constraintLayout)
+ underTest.sections.minus(defaultLockIconSection).forEach {
+ verify(it, never()).addViews(constraintLayout)
+ }
+
+ verify(defaultLockIconSection).addViews(constraintLayout)
+ }
+
+ @Test
+ fun addViews_withNextBlueprint() {
+ val nextBlueprint = mock(KeyguardBlueprint::class.java)
+ whenever(nextBlueprint.sections).thenReturn(setOf())
+ val constraintLayout = ConstraintLayout(context, null)
+ underTest.removeViews(nextBlueprint, constraintLayout)
+ underTest.sections.forEach { verify(it).removeViews(constraintLayout) }
+ }
+
+ @Test
fun applyConstraints() {
val cs = ConstraintSet()
underTest.applyConstraints(cs)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt
index 798b23e..4e31af22 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt
@@ -18,14 +18,17 @@
package com.android.systemui.keyguard.ui.view.layout.sections
import android.view.ViewGroup
+import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
import com.android.systemui.statusbar.KeyguardIndicationController
+import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
@@ -58,7 +61,23 @@
}
@Test
- fun apply() {
+ fun addViewsConditionally() {
+ whenever(featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)).thenReturn(true)
+ val constraintLayout = ConstraintLayout(context, null)
+ underTest.addViews(constraintLayout)
+ assertThat(constraintLayout.childCount).isGreaterThan(0)
+ }
+
+ @Test
+ fun addViewsConditionally_migrateFlagOff() {
+ whenever(featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)).thenReturn(false)
+ val constraintLayout = ConstraintLayout(context, null)
+ underTest.addViews(constraintLayout)
+ assertThat(constraintLayout.childCount).isEqualTo(0)
+ }
+
+ @Test
+ fun applyConstraints() {
val cs = ConstraintSet()
underTest.applyConstraints(cs)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSectionTest.kt
index 1192a80..1c3b5e61 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSectionTest.kt
@@ -19,6 +19,7 @@
import android.graphics.Point
import android.view.WindowManager
+import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardUpdateMonitor
@@ -27,7 +28,9 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.AuthController
import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
import com.android.systemui.shade.NotificationPanelView
+import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
@@ -64,7 +67,23 @@
}
@Test
- fun apply() {
+ fun addViewsConditionally() {
+ whenever(featureFlags.isEnabled(Flags.MIGRATE_LOCK_ICON)).thenReturn(true)
+ val constraintLayout = ConstraintLayout(context, null)
+ underTest.addViews(constraintLayout)
+ assertThat(constraintLayout.childCount).isGreaterThan(0)
+ }
+
+ @Test
+ fun addViewsConditionally_migrateFlagOff() {
+ whenever(featureFlags.isEnabled(Flags.MIGRATE_LOCK_ICON)).thenReturn(false)
+ val constraintLayout = ConstraintLayout(context, null)
+ underTest.addViews(constraintLayout)
+ assertThat(constraintLayout.childCount).isEqualTo(0)
+ }
+
+ @Test
+ fun applyConstraints() {
val cs = ConstraintSet()
underTest.applyConstraints(cs)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index c573ac63..1c9ec27 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -626,6 +626,7 @@
mScreenOffAnimationController,
mLockscreenGestureLogger,
mShadeExpansionStateManager,
+ mShadeRepository,
mSysUIUnfoldComponent,
mSysUiState,
() -> mKeyguardBottomAreaViewController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
index ab0ae05..e42a7a6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
@@ -37,6 +37,7 @@
import com.android.keyguard.TestScopeProvider;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.fragments.FragmentHostManager;
@@ -61,6 +62,7 @@
import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFlagsRepository;
import com.android.systemui.statusbar.notification.stack.AmbientState;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
+import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor;
import com.android.systemui.statusbar.phone.KeyguardBottomAreaView;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.KeyguardStatusBarView;
@@ -174,6 +176,9 @@
new FakeUserSetupRepository(),
mDeviceProvisionedController,
mUserInteractor,
+ new SharedNotificationContainerInteractor(
+ new FakeConfigurationRepository(),
+ mContext),
mShadeRepository
);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
index e6e7482..41ea5b7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
@@ -25,7 +25,9 @@
import androidx.test.filters.SmallTest
import com.android.internal.logging.UiEventLogger
import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
@@ -36,6 +38,7 @@
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel
import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFlagsRepository
+import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.telephony.data.repository.FakeTelephonyRepository
@@ -52,6 +55,7 @@
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -71,6 +75,12 @@
private val disableFlagsRepository = FakeDisableFlagsRepository()
private val keyguardRepository = FakeKeyguardRepository()
private val shadeRepository = FakeShadeRepository()
+ private val configurationRepository = FakeConfigurationRepository()
+ private val sharedNotificationContainerInteractor =
+ SharedNotificationContainerInteractor(
+ configurationRepository,
+ mContext,
+ )
@Mock private lateinit var manager: UserManager
@Mock private lateinit var headlessSystemUserMode: HeadlessSystemUserMode
@@ -145,6 +155,7 @@
userSetupRepository,
deviceProvisionedController,
userInteractor,
+ sharedNotificationContainerInteractor,
shadeRepository,
)
}
@@ -363,7 +374,7 @@
val actual by collectLastValue(underTest.shadeExpansion)
keyguardRepository.setStatusBarState(StatusBarState.SHADE_LOCKED)
- shadeRepository.setShadeExpansion(0.5f)
+ shadeRepository.setLockscreenShadeExpansion(0.5f)
assertThat(actual).isEqualTo(1f)
}
@@ -375,10 +386,52 @@
keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
- shadeRepository.setShadeExpansion(0.5f)
+ shadeRepository.setLockscreenShadeExpansion(0.5f)
assertThat(actual).isEqualTo(0.5f)
- shadeRepository.setShadeExpansion(0.8f)
+ shadeRepository.setLockscreenShadeExpansion(0.8f)
assertThat(actual).isEqualTo(0.8f)
}
+
+ fun shadeExpansionWhenInSplitShadeAndQsExpanded() =
+ testScope.runTest {
+ val actual by collectLastValue(underTest.shadeExpansion)
+
+ // WHEN split shade is enabled and QS is expanded
+ keyguardRepository.setStatusBarState(StatusBarState.SHADE)
+ overrideResource(R.bool.config_use_split_notification_shade, true)
+ configurationRepository.onAnyConfigurationChange()
+ runCurrent()
+ shadeRepository.setQsExpansion(.5f)
+ shadeRepository.setLegacyShadeExpansion(.7f)
+
+ // THEN legacy shade expansion is passed through
+ assertThat(actual).isEqualTo(.7f)
+ }
+
+ fun shadeExpansionWhenNotInSplitShadeAndQsExpanded() =
+ testScope.runTest {
+ val actual by collectLastValue(underTest.shadeExpansion)
+
+ // WHEN split shade is not enabled and QS is expanded
+ keyguardRepository.setStatusBarState(StatusBarState.SHADE)
+ shadeRepository.setQsExpansion(.5f)
+ shadeRepository.setLegacyShadeExpansion(1f)
+
+ // THEN shade expansion is zero
+ assertThat(actual).isEqualTo(0f)
+ }
+
+ fun shadeExpansionWhenNotInSplitShadeAndQsCollapsed() =
+ testScope.runTest {
+ val actual by collectLastValue(underTest.shadeExpansion)
+
+ // WHEN split shade is not enabled and QS is expanded
+ keyguardRepository.setStatusBarState(StatusBarState.SHADE)
+ shadeRepository.setQsExpansion(0f)
+ shadeRepository.setLegacyShadeExpansion(.6f)
+
+ // THEN shade expansion is zero
+ assertThat(actual).isEqualTo(.6f)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
index fdaea22..e086712 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
@@ -121,18 +121,33 @@
}
@Test
- fun updateShadeExpansion() =
+ fun updateDragDownAmount() =
testScope.runTest {
- assertThat(underTest.shadeExpansion.value).isEqualTo(0f)
+ assertThat(underTest.lockscreenShadeExpansion.value).isEqualTo(0f)
- underTest.setShadeExpansion(.5f)
- assertThat(underTest.shadeExpansion.value).isEqualTo(.5f)
+ underTest.setLockscreenShadeExpansion(.5f)
+ assertThat(underTest.lockscreenShadeExpansion.value).isEqualTo(.5f)
- underTest.setShadeExpansion(.82f)
- assertThat(underTest.shadeExpansion.value).isEqualTo(.82f)
+ underTest.setLockscreenShadeExpansion(.82f)
+ assertThat(underTest.lockscreenShadeExpansion.value).isEqualTo(.82f)
- underTest.setShadeExpansion(1f)
- assertThat(underTest.shadeExpansion.value).isEqualTo(1f)
+ underTest.setLockscreenShadeExpansion(1f)
+ assertThat(underTest.lockscreenShadeExpansion.value).isEqualTo(1f)
+ }
+
+ @Test
+ fun updateLegacyShadeExpansion() =
+ testScope.runTest {
+ assertThat(underTest.legacyShadeExpansion.value).isEqualTo(0f)
+
+ underTest.setLegacyShadeExpansion(.5f)
+ assertThat(underTest.legacyShadeExpansion.value).isEqualTo(.5f)
+
+ underTest.setLegacyShadeExpansion(.82f)
+ assertThat(underTest.legacyShadeExpansion.value).isEqualTo(.82f)
+
+ underTest.setLegacyShadeExpansion(1f)
+ assertThat(underTest.legacyShadeExpansion.value).isEqualTo(1f)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
index 51e72c6..6f75880 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
@@ -10,6 +10,7 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.classifier.FalsingCollectorFake
+import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
@@ -29,6 +30,7 @@
import com.android.systemui.statusbar.notification.stack.AmbientState
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
+import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.phone.LSShadeTransitionLogger
@@ -102,6 +104,11 @@
@Mock lateinit var transitionControllerCallback: LockscreenShadeTransitionController.Callback
private val disableFlagsRepository = FakeDisableFlagsRepository()
private val keyguardRepository = FakeKeyguardRepository()
+ private val configurationRepository = FakeConfigurationRepository()
+ private val sharedNotificationContainerInteractor = SharedNotificationContainerInteractor(
+ configurationRepository,
+ mContext,
+ )
private val shadeInteractor =
ShadeInteractor(
testScope.backgroundScope,
@@ -110,6 +117,7 @@
userSetupRepository = FakeUserSetupRepository(),
deviceProvisionedController = mock(),
userInteractor = mock(),
+ sharedNotificationContainerInteractor,
repository = FakeShadeRepository(),
)
private val powerInteractor =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
index 55b52dc..75fb22d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
@@ -30,6 +30,7 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.shade.data.repository.FakeShadeRepository
@@ -97,7 +98,11 @@
keyguardTransitionInteractor = it.keyguardTransitionInteractor
keyguardTransitionRepository = it.repository
}
-
+ sharedNotificationContainerInteractor =
+ SharedNotificationContainerInteractor(
+ configurationRepository,
+ mContext,
+ )
shadeInteractor =
ShadeInteractor(
testScope.backgroundScope,
@@ -106,14 +111,9 @@
userSetupRepository,
deviceProvisionedController,
userInteractor,
+ sharedNotificationContainerInteractor,
shadeRepository,
)
-
- sharedNotificationContainerInteractor =
- SharedNotificationContainerInteractor(
- configurationRepository,
- mContext,
- )
underTest =
SharedNotificationContainerViewModel(
sharedNotificationContainerInteractor,
@@ -228,7 +228,7 @@
val isOnLockscreenWithoutShade by collectLastValue(underTest.isOnLockscreenWithoutShade)
// First on AOD
- shadeRepository.setShadeExpansion(0f)
+ shadeRepository.setLockscreenShadeExpansion(0f)
shadeRepository.setQsExpansion(0f)
keyguardTransitionRepository.sendTransitionStep(
TransitionStep(
@@ -242,19 +242,19 @@
showLockscreen()
// While state is LOCKSCREEN, validate variations of both shade and qs expansion
- shadeRepository.setShadeExpansion(0.1f)
+ shadeRepository.setLockscreenShadeExpansion(0.1f)
shadeRepository.setQsExpansion(0f)
assertThat(isOnLockscreenWithoutShade).isFalse()
- shadeRepository.setShadeExpansion(0.1f)
+ shadeRepository.setLockscreenShadeExpansion(0.1f)
shadeRepository.setQsExpansion(0.1f)
assertThat(isOnLockscreenWithoutShade).isFalse()
- shadeRepository.setShadeExpansion(0f)
+ shadeRepository.setLockscreenShadeExpansion(0f)
shadeRepository.setQsExpansion(0.1f)
assertThat(isOnLockscreenWithoutShade).isFalse()
- shadeRepository.setShadeExpansion(0f)
+ shadeRepository.setLockscreenShadeExpansion(0f)
shadeRepository.setQsExpansion(0f)
assertThat(isOnLockscreenWithoutShade).isTrue()
}
@@ -366,8 +366,9 @@
}
private suspend fun showLockscreen() {
- shadeRepository.setShadeExpansion(0f)
+ shadeRepository.setLockscreenShadeExpansion(0f)
shadeRepository.setQsExpansion(0f)
+ keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
keyguardTransitionRepository.sendTransitionStep(
TransitionStep(
to = KeyguardState.LOCKSCREEN,
@@ -377,8 +378,9 @@
}
private suspend fun showLockscreenWithShadeExpanded() {
- shadeRepository.setShadeExpansion(1f)
+ shadeRepository.setLockscreenShadeExpansion(1f)
shadeRepository.setQsExpansion(0f)
+ keyguardRepository.setStatusBarState(StatusBarState.SHADE_LOCKED)
keyguardTransitionRepository.sendTransitionStep(
TransitionStep(
to = KeyguardState.LOCKSCREEN,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
index ccddca2..08152a3 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
@@ -33,8 +33,12 @@
private val _udfpsTransitionToFullShadeProgress = MutableStateFlow(0f)
override val udfpsTransitionToFullShadeProgress = _udfpsTransitionToFullShadeProgress
- private val _shadeExpansion = MutableStateFlow(0f)
- override val shadeExpansion = _shadeExpansion
+ private val _lockscreenShadeExpansion = MutableStateFlow(0f)
+ override val lockscreenShadeExpansion = _lockscreenShadeExpansion
+
+ private val _legacyShadeExpansion = MutableStateFlow(0f)
+ @Deprecated("Use ShadeInteractor instead")
+ override val legacyShadeExpansion = _legacyShadeExpansion
fun setShadeModel(model: ShadeModel) {
_shadeModel.value = model
@@ -48,7 +52,12 @@
_udfpsTransitionToFullShadeProgress.value = progress
}
- override fun setShadeExpansion(expansion: Float) {
- _shadeExpansion.value = expansion
+ override fun setLockscreenShadeExpansion(lockscreenShadeExpansion: Float) {
+ _lockscreenShadeExpansion.value = lockscreenShadeExpansion
+ }
+
+ @Deprecated("Should only be called by NPVC and tests")
+ override fun setLegacyShadeExpansion(expandedFraction: Float) {
+ _legacyShadeExpansion.value = expandedFraction
}
}
diff --git a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
index cf6092e..9e7b897 100644
--- a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
+++ b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
@@ -136,7 +136,8 @@
@Nullable private final SecureWindowCallback mSecureWindowCallback;
@Nullable private final Set<String> mDisplayCategories;
- private final boolean mShowTasksInHostDeviceRecents;
+ @GuardedBy("mGenericWindowPolicyControllerLock")
+ private boolean mShowTasksInHostDeviceRecents;
/**
* Creates a window policy controller that is generic to the different use cases of virtual
@@ -204,6 +205,15 @@
mDisplayId = displayId;
}
+ /**
+ * Set whether to show activities in recents on the host device.
+ */
+ public void setShowInHostDeviceRecents(boolean showInHostDeviceRecents) {
+ synchronized (mGenericWindowPolicyControllerLock) {
+ mShowTasksInHostDeviceRecents = showInHostDeviceRecents;
+ }
+ }
+
/** Register a listener for running applications changes. */
public void registerRunningAppsChangedListener(@NonNull RunningAppsChangedListener listener) {
synchronized (mGenericWindowPolicyControllerLock) {
@@ -347,7 +357,9 @@
@Override
public boolean canShowTasksInHostDeviceRecents() {
- return mShowTasksInHostDeviceRecents;
+ synchronized (mGenericWindowPolicyControllerLock) {
+ return mShowTasksInHostDeviceRecents;
+ }
}
@Override
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index b8b42fba..56afeb1 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -19,6 +19,8 @@
import static android.app.admin.DevicePolicyManager.NEARBY_STREAMING_ENABLED;
import static android.app.admin.DevicePolicyManager.NEARBY_STREAMING_NOT_CONTROLLED_BY_POLICY;
import static android.app.admin.DevicePolicyManager.NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY;
+import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_DEFAULT;
+import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_RECENTS;
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
@@ -42,6 +44,7 @@
import android.companion.virtual.VirtualDeviceParams;
import android.companion.virtual.audio.IAudioConfigChangedCallback;
import android.companion.virtual.audio.IAudioRoutingCallback;
+import android.companion.virtual.flags.Flags;
import android.companion.virtual.sensor.VirtualSensor;
import android.companion.virtual.sensor.VirtualSensorEvent;
import android.content.AttributionSource;
@@ -82,6 +85,7 @@
import android.util.IntArray;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseIntArray;
import android.view.Display;
import android.view.WindowManager;
import android.widget.Toast;
@@ -130,8 +134,8 @@
private final int mOwnerUid;
private final VirtualDeviceLog mVirtualDeviceLog;
private final String mOwnerPackageName;
- private int mDeviceId;
- private @Nullable String mPersistentDeviceId;
+ private final int mDeviceId;
+ private @Nullable final String mPersistentDeviceId;
// Thou shall not hold the mVirtualDeviceLock over the mInputController calls.
// Holding the lock can lead to lock inversion with GlobalWindowManagerLock.
// 1. After display is created the window manager calls into VDM during construction
@@ -147,6 +151,8 @@
private final IBinder mAppToken;
private final VirtualDeviceParams mParams;
@GuardedBy("mVirtualDeviceLock")
+ private final SparseIntArray mDevicePolicies;
+ @GuardedBy("mVirtualDeviceLock")
private final SparseArray<VirtualDisplayWrapper> mVirtualDisplays = new SparseArray<>();
private final IVirtualDeviceActivityListener mActivityListener;
private final IVirtualDeviceSoundEffectListener mSoundEffectListener;
@@ -154,7 +160,7 @@
@GuardedBy("mVirtualDeviceLock")
private final Map<IBinder, IntentFilter> mIntentInterceptors = new ArrayMap<>();
@NonNull
- private Consumer<ArraySet<Integer>> mRunningAppsChangedCallback;
+ private final Consumer<ArraySet<Integer>> mRunningAppsChangedCallback;
// The default setting for showing the pointer on new displays.
@GuardedBy("mVirtualDeviceLock")
private boolean mDefaultShowPointerIcon = true;
@@ -261,6 +267,7 @@
mDeviceId = deviceId;
mAppToken = token;
mParams = params;
+ mDevicePolicies = params.getDevicePolicies();
mDisplayManager = displayManager;
if (inputController == null) {
mInputController = new InputController(
@@ -323,7 +330,13 @@
/** Returns the policy specified for this policy type */
public @VirtualDeviceParams.DevicePolicy int getDevicePolicy(
@VirtualDeviceParams.PolicyType int policyType) {
- return mParams.getDevicePolicy(policyType);
+ if (Flags.dynamicPolicy()) {
+ synchronized (mVirtualDeviceLock) {
+ return mDevicePolicies.get(policyType, DEVICE_POLICY_DEFAULT);
+ }
+ } else {
+ return mParams.getDevicePolicy(policyType);
+ }
}
/** Returns device-specific audio session id for playback. */
@@ -412,15 +425,13 @@
public void close() {
super.close_enforcePermission();
// Remove about-to-be-closed virtual device from the service before butchering it.
- boolean removed = mService.removeVirtualDevice(mDeviceId);
- mVirtualDeviceLog.logClosed(mDeviceId, mOwnerUid);
- mDeviceId = Context.DEVICE_ID_INVALID;
-
- // Device is already closed.
- if (!removed) {
+ if (!mService.removeVirtualDevice(mDeviceId)) {
+ // Device is already closed.
return;
}
+ mVirtualDeviceLog.logClosed(mDeviceId, mOwnerUid);
+
final long ident = Binder.clearCallingIdentity();
try {
VirtualDisplayWrapper[] virtualDisplaysToBeReleased;
@@ -510,6 +521,27 @@
@Override // Binder call
@EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+ public void setDevicePolicy(@VirtualDeviceParams.DynamicPolicyType int policyType,
+ @VirtualDeviceParams.DevicePolicy int devicePolicy) {
+ super.setDevicePolicy_enforcePermission();
+ switch (policyType) {
+ case POLICY_TYPE_RECENTS:
+ synchronized (mVirtualDeviceLock) {
+ mDevicePolicies.put(policyType, devicePolicy);
+ for (int i = 0; i < mVirtualDisplays.size(); i++) {
+ mVirtualDisplays.valueAt(i).getWindowPolicyController()
+ .setShowInHostDeviceRecents(devicePolicy == DEVICE_POLICY_DEFAULT);
+ }
+ }
+ break;
+ default:
+ throw new IllegalArgumentException("Device policy " + policyType
+ + " cannot be changed at runtime. ");
+ }
+ }
+
+ @Override // Binder call
+ @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
public void createVirtualDpad(VirtualDpadConfig config, @NonNull IBinder deviceToken) {
super.createVirtualDpad_enforcePermission();
Objects.requireNonNull(config);
@@ -787,6 +819,7 @@
mParams.dump(fout, " ");
fout.println(" mVirtualDisplayIds: ");
synchronized (mVirtualDeviceLock) {
+ fout.println(" mDevicePolicies: " + mDevicePolicies);
for (int i = 0; i < mVirtualDisplays.size(); i++) {
fout.println(" " + mVirtualDisplays.keyAt(i));
}
@@ -813,9 +846,7 @@
this::onSecureWindowShown,
this::shouldInterceptIntent,
displayCategories,
- mParams.getDevicePolicy(
- VirtualDeviceParams.POLICY_TYPE_RECENTS)
- == VirtualDeviceParams.DEVICE_POLICY_DEFAULT);
+ mParams.getDevicePolicy(POLICY_TYPE_RECENTS) == DEVICE_POLICY_DEFAULT);
gwpc.registerRunningAppsChangedListener(/* listener= */ this);
return gwpc;
}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index b994105..f3ad4b4 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -472,6 +472,8 @@
private SensorManager mSensorManager;
private BrightnessTracker mBrightnessTracker;
+ private SmallAreaDetectionController mSmallAreaDetectionController;
+
// Whether minimal post processing is allowed by the user.
@GuardedBy("mSyncRoot")
@@ -738,6 +740,8 @@
filter.addAction(Intent.ACTION_DOCK_EVENT);
mContext.registerReceiver(mIdleModeReceiver, filter);
+
+ mSmallAreaDetectionController = SmallAreaDetectionController.create(mContext);
}
@VisibleForTesting
@@ -3128,6 +3132,9 @@
pw.println();
mDisplayModeDirector.dump(pw);
mBrightnessSynchronizer.dump(pw);
+ if (mSmallAreaDetectionController != null) {
+ mSmallAreaDetectionController.dump(pw);
+ }
}
private static float[] getFloatArray(TypedArray array) {
diff --git a/services/core/java/com/android/server/display/SmallAreaDetectionController.java b/services/core/java/com/android/server/display/SmallAreaDetectionController.java
new file mode 100644
index 0000000..adaa539
--- /dev/null
+++ b/services/core/java/com/android/server/display/SmallAreaDetectionController.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.pm.PackageManagerInternal;
+import android.provider.DeviceConfig;
+import android.provider.DeviceConfigInterface;
+import android.util.ArrayMap;
+import android.util.SparseArray;
+
+import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.BackgroundThread;
+import com.android.server.LocalServices;
+import com.android.server.pm.UserManagerInternal;
+
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.Map;
+
+final class SmallAreaDetectionController {
+ private static native void nativeUpdateSmallAreaDetection(int[] uids, float[] thresholds);
+ private static native void nativeSetSmallAreaDetectionThreshold(int uid, float threshold);
+
+ // TODO(b/281720315): Move this to DeviceConfig once server side ready.
+ private static final String KEY_SMALL_AREA_DETECTION_ALLOWLIST =
+ "small_area_detection_allowlist";
+
+ private final Object mLock = new Object();
+ private final Context mContext;
+ private final PackageManagerInternal mPackageManager;
+ private final UserManagerInternal mUserManager;
+ @GuardedBy("mLock")
+ private final Map<String, Float> mAllowPkgMap = new ArrayMap<>();
+ // TODO(b/298722189): Update allowlist when user changes
+ @GuardedBy("mLock")
+ private int[] mUserIds;
+
+ static SmallAreaDetectionController create(@NonNull Context context) {
+ final SmallAreaDetectionController controller =
+ new SmallAreaDetectionController(context, DeviceConfigInterface.REAL);
+ final String property = DeviceConfigInterface.REAL.getProperty(
+ DeviceConfig.NAMESPACE_DISPLAY_MANAGER, KEY_SMALL_AREA_DETECTION_ALLOWLIST);
+ controller.updateAllowlist(property);
+ return controller;
+ }
+
+ @VisibleForTesting
+ SmallAreaDetectionController(Context context, DeviceConfigInterface deviceConfig) {
+ mContext = context;
+ mPackageManager = LocalServices.getService(PackageManagerInternal.class);
+ mUserManager = LocalServices.getService(UserManagerInternal.class);
+ deviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ BackgroundThread.getExecutor(),
+ new SmallAreaDetectionController.OnPropertiesChangedListener());
+ mPackageManager.getPackageList(new PackageReceiver());
+ }
+
+ @VisibleForTesting
+ void updateAllowlist(@Nullable String property) {
+ synchronized (mLock) {
+ mAllowPkgMap.clear();
+ if (property != null) {
+ final String[] mapStrings = property.split(",");
+ for (String mapString : mapStrings) putToAllowlist(mapString);
+ } else {
+ final String[] defaultMapStrings = mContext.getResources()
+ .getStringArray(R.array.config_smallAreaDetectionAllowlist);
+ for (String defaultMapString : defaultMapStrings) putToAllowlist(defaultMapString);
+ }
+ updateSmallAreaDetection();
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void putToAllowlist(String rowData) {
+ // Data format: package:threshold - e.g. "com.abc.music:0.05"
+ final String[] items = rowData.split(":");
+ if (items.length == 2) {
+ try {
+ final String pkg = items[0];
+ final float threshold = Float.valueOf(items[1]);
+ mAllowPkgMap.put(pkg, threshold);
+ } catch (Exception e) {
+ // Just skip if items[1] - the threshold is not parsable number
+ }
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void updateUidListForAllUsers(SparseArray<Float> list, String pkg, float threshold) {
+ for (int i = 0; i < mUserIds.length; i++) {
+ final int userId = mUserIds[i];
+ final int uid = mPackageManager.getPackageUid(pkg, 0, userId);
+ if (uid > 0) list.put(uid, threshold);
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void updateSmallAreaDetection() {
+ if (mAllowPkgMap.isEmpty()) return;
+
+ mUserIds = mUserManager.getUserIds();
+
+ final SparseArray<Float> uidThresholdList = new SparseArray<>();
+ for (String pkg : mAllowPkgMap.keySet()) {
+ final float threshold = mAllowPkgMap.get(pkg);
+ updateUidListForAllUsers(uidThresholdList, pkg, threshold);
+ }
+
+ final int[] uids = new int[uidThresholdList.size()];
+ final float[] thresholds = new float[uidThresholdList.size()];
+ for (int i = 0; i < uidThresholdList.size(); i++) {
+ uids[i] = uidThresholdList.keyAt(i);
+ thresholds[i] = uidThresholdList.valueAt(i);
+ }
+ updateSmallAreaDetection(uids, thresholds);
+ }
+
+ @VisibleForTesting
+ void updateSmallAreaDetection(int[] uids, float[] thresholds) {
+ nativeUpdateSmallAreaDetection(uids, thresholds);
+ }
+
+ void setSmallAreaDetectionThreshold(int uid, float threshold) {
+ nativeSetSmallAreaDetectionThreshold(uid, threshold);
+ }
+
+ void dump(PrintWriter pw) {
+ pw.println("Small area detection allowlist");
+ pw.println(" Packages:");
+ synchronized (mLock) {
+ for (String pkg : mAllowPkgMap.keySet()) {
+ pw.println(" " + pkg + " threshold = " + mAllowPkgMap.get(pkg));
+ }
+ pw.println(" mUserIds=" + Arrays.toString(mUserIds));
+ }
+ }
+
+ private class OnPropertiesChangedListener implements DeviceConfig.OnPropertiesChangedListener {
+ public void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) {
+ if (properties.getKeyset().contains(KEY_SMALL_AREA_DETECTION_ALLOWLIST)) {
+ updateAllowlist(
+ properties.getString(KEY_SMALL_AREA_DETECTION_ALLOWLIST, null /*default*/));
+ }
+ }
+ }
+
+ private final class PackageReceiver implements PackageManagerInternal.PackageListObserver {
+ @Override
+ public void onPackageAdded(@NonNull String packageName, int uid) {
+ synchronized (mLock) {
+ if (mAllowPkgMap.containsKey(packageName)) {
+ setSmallAreaDetectionThreshold(uid, mAllowPkgMap.get(packageName));
+ }
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/notification/flags.aconfig b/services/core/java/com/android/server/notification/flags.aconfig
index 1908e4d..dcac8c9 100644
--- a/services/core/java/com/android/server/notification/flags.aconfig
+++ b/services/core/java/com/android/server/notification/flags.aconfig
@@ -13,3 +13,10 @@
description: "This flag controls the polite notification feature"
bug: "270456865"
}
+
+flag {
+ name: "refactor_attention_helper"
+ namespace: "systemui"
+ description: "This flag controls the refactoring of NMS to NotificationAttentionHelper"
+ bug: "291907312"
+}
diff --git a/services/core/java/com/android/server/pm/PackageArchiverService.java b/services/core/java/com/android/server/pm/PackageArchiverService.java
index 9c31dc9..29f49c8 100644
--- a/services/core/java/com/android/server/pm/PackageArchiverService.java
+++ b/services/core/java/com/android/server/pm/PackageArchiverService.java
@@ -17,17 +17,26 @@
package com.android.server.pm;
import static android.content.pm.PackageManager.DELETE_KEEP_DATA;
+import static android.os.PowerExemptionManager.REASON_PACKAGE_UNARCHIVE;
+import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
+import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.app.AppOpsManager;
+import android.app.BroadcastOptions;
import android.content.Context;
+import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.IPackageArchiverService;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.LauncherApps;
+import android.content.pm.PackageArchiver;
import android.content.pm.PackageManager;
import android.content.pm.VersionedPackage;
import android.os.Binder;
+import android.os.Bundle;
import android.os.ParcelableException;
import android.os.UserHandle;
import android.text.TextUtils;
@@ -36,6 +45,7 @@
import com.android.server.pm.pkg.ArchiveState;
import com.android.server.pm.pkg.ArchiveState.ArchiveActivityInfo;
import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.PackageUserStateInternal;
import java.nio.file.Path;
import java.util.ArrayList;
@@ -51,7 +61,12 @@
*/
public class PackageArchiverService extends IPackageArchiverService.Stub {
- private static final String TAG = "PackageArchiver";
+ /**
+ * The maximum time granted for an app store to start a foreground service when unarchival
+ * is requested.
+ */
+ // TODO(b/297358628) Make this configurable through a flag.
+ private static final int DEFAULT_UNARCHIVE_FOREGROUND_TIMEOUT_MS = 120 * 1000;
private final Context mContext;
private final PackageManagerService mPm;
@@ -82,17 +97,14 @@
snapshot.enforceCrossUserPermission(binderUid, userId, true, true,
"archiveApp");
verifyCaller(providedUid, binderUid);
- PackageStateInternal ps = getPackageState(packageName, snapshot, binderUid, userId);
- verifyInstaller(packageName, ps);
-
- // TODO(b/291569242) Verify that this list is not empty and return failure with
- // intentsender
- List<LauncherActivityInfo> mainActivities = getLauncherApps().getActivityList(
- ps.getPackageName(),
- new UserHandle(userId));
-
- // TODO(b/282952870) Bug: should happen after the uninstall completes successfully
- storeArchiveState(ps, mainActivities, userId);
+ ArchiveState archiveState;
+ try {
+ archiveState = createArchiveState(packageName, userId);
+ // TODO(b/282952870) Should be reverted if uninstall fails/cancels
+ storeArchiveState(packageName, archiveState, userId);
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new ParcelableException(e);
+ }
// TODO(b/278553670) Add special strings for the delete dialog
mPm.mInstallerService.uninstall(
@@ -100,25 +112,154 @@
callerPackageName, DELETE_KEEP_DATA, intentSender, userId);
}
- private static void verifyInstaller(String packageName, PackageStateInternal ps) {
- if (ps.getInstallSource().mUpdateOwnerPackageName == null
- && ps.getInstallSource().mInstallerPackageName == null) {
+ private ArchiveState createArchiveState(String packageName, int userId)
+ throws PackageManager.NameNotFoundException {
+ PackageStateInternal ps = getPackageState(packageName, mPm.snapshotComputer(),
+ Binder.getCallingUid(), userId);
+ String responsibleInstallerPackage = getResponsibleInstallerPackage(ps);
+ if (responsibleInstallerPackage == null) {
+ throw new PackageManager.NameNotFoundException(
+ TextUtils.formatSimple("No installer found to archive app %s.",
+ packageName));
+ }
+
+ List<LauncherActivityInfo> mainActivities = getLauncherActivityInfos(ps, userId);
+ List<ArchiveActivityInfo> archiveActivityInfos = new ArrayList<>();
+ for (int i = 0; i < mainActivities.size(); i++) {
+ // TODO(b/278553670) Extract and store launcher icons
+ ArchiveActivityInfo activityInfo = new ArchiveActivityInfo(
+ mainActivities.get(i).getLabel().toString(),
+ Path.of("/TODO"), null);
+ archiveActivityInfos.add(activityInfo);
+ }
+
+ return new ArchiveState(archiveActivityInfos, responsibleInstallerPackage);
+ }
+
+ @Override
+ public void requestUnarchive(
+ @NonNull String packageName,
+ @NonNull String callerPackageName,
+ @NonNull UserHandle userHandle) {
+ Objects.requireNonNull(packageName);
+ Objects.requireNonNull(callerPackageName);
+ Objects.requireNonNull(userHandle);
+
+ Computer snapshot = mPm.snapshotComputer();
+ int userId = userHandle.getIdentifier();
+ int binderUid = Binder.getCallingUid();
+ int providedUid = snapshot.getPackageUid(callerPackageName, 0, userId);
+ snapshot.enforceCrossUserPermission(binderUid, userId, true, true,
+ "unarchiveApp");
+ verifyCaller(providedUid, binderUid);
+ PackageStateInternal ps;
+ try {
+ ps = getPackageState(packageName, snapshot, binderUid, userId);
+ verifyArchived(ps, userId);
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new ParcelableException(e);
+ }
+ String installerPackage = getResponsibleInstallerPackage(ps);
+ if (installerPackage == null) {
throw new ParcelableException(
new PackageManager.NameNotFoundException(
- TextUtils.formatSimple("No installer found to archive app %s.",
+ TextUtils.formatSimple("No installer found to unarchive app %s.",
packageName)));
}
+
+ mPm.mHandler.post(() -> unarchiveInternal(packageName, userHandle, installerPackage));
+ }
+
+ private void verifyArchived(PackageStateInternal ps, int userId)
+ throws PackageManager.NameNotFoundException {
+ PackageUserStateInternal userState = ps.getUserStateOrDefault(userId);
+ // TODO(b/288142708) Check for isInstalled false here too.
+ if (userState.getArchiveState() == null) {
+ throw new PackageManager.NameNotFoundException(
+ TextUtils.formatSimple("Package %s is not currently archived.",
+ ps.getPackageName()));
+ }
+ }
+
+ @RequiresPermission(
+ allOf = {
+ Manifest.permission.INTERACT_ACROSS_USERS,
+ android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST,
+ android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND,
+ android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND},
+ conditional = true)
+ private void unarchiveInternal(String packageName, UserHandle userHandle,
+ String installerPackage) {
+ int userId = userHandle.getIdentifier();
+ Intent unarchiveIntent = new Intent(Intent.ACTION_UNARCHIVE_PACKAGE);
+ unarchiveIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ unarchiveIntent.putExtra(PackageArchiver.EXTRA_UNARCHIVE_PACKAGE_NAME, packageName);
+ unarchiveIntent.putExtra(PackageArchiver.EXTRA_UNARCHIVE_ALL_USERS,
+ userId == UserHandle.USER_ALL);
+ unarchiveIntent.setPackage(installerPackage);
+
+ // If the unarchival is requested for all users, the current user is used for unarchival.
+ UserHandle userForUnarchival = userId == UserHandle.USER_ALL
+ ? UserHandle.of(mPm.mUserManager.getCurrentUserId())
+ : userHandle;
+ mContext.sendOrderedBroadcastAsUser(
+ unarchiveIntent,
+ userForUnarchival,
+ /* receiverPermission = */ null,
+ AppOpsManager.OP_NONE,
+ createUnarchiveOptions(),
+ /* resultReceiver= */ null,
+ /* scheduler= */ null,
+ /* initialCode= */ 0,
+ /* initialData= */ null,
+ /* initialExtras= */ null);
+ }
+
+ private List<LauncherActivityInfo> getLauncherActivityInfos(PackageStateInternal ps,
+ int userId) throws PackageManager.NameNotFoundException {
+ List<LauncherActivityInfo> mainActivities =
+ Binder.withCleanCallingIdentity(() -> getLauncherApps().getActivityList(
+ ps.getPackageName(),
+ new UserHandle(userId)));
+ if (mainActivities.isEmpty()) {
+ throw new PackageManager.NameNotFoundException(
+ TextUtils.formatSimple("The app %s does not have a main activity.",
+ ps.getPackageName()));
+ }
+
+ return mainActivities;
+ }
+
+ @RequiresPermission(anyOf = {android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST,
+ android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND,
+ android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND})
+ private Bundle createUnarchiveOptions() {
+ BroadcastOptions options = BroadcastOptions.makeBasic();
+ options.setTemporaryAppAllowlist(getUnarchiveForegroundTimeout(),
+ TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
+ REASON_PACKAGE_UNARCHIVE, "");
+ return options.toBundle();
+ }
+
+ private static int getUnarchiveForegroundTimeout() {
+ return DEFAULT_UNARCHIVE_FOREGROUND_TIMEOUT_MS;
+ }
+
+ private String getResponsibleInstallerPackage(PackageStateInternal ps) {
+ return ps.getInstallSource().mUpdateOwnerPackageName == null
+ ? ps.getInstallSource().mInstallerPackageName
+ : ps.getInstallSource().mUpdateOwnerPackageName;
}
@NonNull
private static PackageStateInternal getPackageState(String packageName,
- Computer snapshot, int callingUid, int userId) {
+ Computer snapshot, int callingUid, int userId)
+ throws PackageManager.NameNotFoundException {
PackageStateInternal ps = snapshot.getPackageStateFiltered(packageName, callingUid,
userId);
if (ps == null) {
- throw new ParcelableException(
- new PackageManager.NameNotFoundException(
- TextUtils.formatSimple("Package %s not found.", packageName)));
+ throw new PackageManager.NameNotFoundException(
+ TextUtils.formatSimple("Package %s not found.", packageName));
}
return ps;
}
@@ -130,38 +271,25 @@
return mLauncherApps;
}
- private void storeArchiveState(PackageStateInternal ps,
- List<LauncherActivityInfo> mainActivities, int userId) {
- List<ArchiveActivityInfo> activityInfos = new ArrayList<>();
- for (int i = 0; i < mainActivities.size(); i++) {
- // TODO(b/278553670) Extract and store launcher icons
- ArchiveActivityInfo activityInfo = new ArchiveActivityInfo(
- mainActivities.get(i).getLabel().toString(),
- Path.of("/TODO"), null);
- activityInfos.add(activityInfo);
- }
-
- InstallSource installSource = ps.getInstallSource();
- String installerPackageName = installSource.mUpdateOwnerPackageName != null
- ? installSource.mUpdateOwnerPackageName : installSource.mInstallerPackageName;
-
+ private void storeArchiveState(String packageName, ArchiveState archiveState, int userId)
+ throws PackageManager.NameNotFoundException {
synchronized (mPm.mLock) {
- PackageSetting packageSetting = getPackageSettingLocked(ps.getPackageName(), userId);
+ PackageSetting packageSetting = getPackageSettingLocked(packageName, userId);
packageSetting
.modifyUserState(userId)
- .setArchiveState(new ArchiveState(activityInfos, installerPackageName));
+ .setArchiveState(archiveState);
}
}
@NonNull
@GuardedBy("mPm.mLock")
- private PackageSetting getPackageSettingLocked(String packageName, int userId) {
+ private PackageSetting getPackageSettingLocked(String packageName, int userId)
+ throws PackageManager.NameNotFoundException {
PackageSetting ps = mPm.mSettings.getPackageLPr(packageName);
// Shouldn't happen, we already verify presence of the package in getPackageState()
if (ps == null || !ps.getUserStateOrDefault(userId).isInstalled()) {
- throw new ParcelableException(
- new PackageManager.NameNotFoundException(
- TextUtils.formatSimple("Package %s not found.", packageName)));
+ throw new PackageManager.NameNotFoundException(
+ TextUtils.formatSimple("Package %s not found.", packageName));
}
return ps;
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index cbac39a..63794d5 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -3354,6 +3354,13 @@
return true;
}
break;
+ case KeyEvent.KEYCODE_DEL:
+ case KeyEvent.KEYCODE_GRAVE:
+ if (firstDown && event.isMetaPressed()) {
+ logKeyboardSystemsEvent(event, KeyboardLogEvent.BACK);
+ injectBackGesture(event.getDownTime());
+ return true;
+ }
case KeyEvent.KEYCODE_DPAD_UP:
if (firstDown && event.isMetaPressed() && event.isCtrlPressed()) {
StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
@@ -3365,9 +3372,14 @@
}
break;
case KeyEvent.KEYCODE_DPAD_LEFT:
- if (firstDown && event.isMetaPressed() && event.isCtrlPressed()) {
- enterStageSplitFromRunningApp(true /* leftOrTop */);
- logKeyboardSystemsEvent(event, KeyboardLogEvent.SPLIT_SCREEN_NAVIGATION);
+ if (firstDown && event.isMetaPressed()) {
+ if (event.isCtrlPressed()) {
+ enterStageSplitFromRunningApp(true /* leftOrTop */);
+ logKeyboardSystemsEvent(event, KeyboardLogEvent.SPLIT_SCREEN_NAVIGATION);
+ } else {
+ logKeyboardSystemsEvent(event, KeyboardLogEvent.BACK);
+ injectBackGesture(event.getDownTime());
+ }
return true;
}
break;
@@ -3630,6 +3642,25 @@
return (metaState & KeyEvent.META_META_ON) != 0;
}
+ @SuppressLint("MissingPermission")
+ private void injectBackGesture(long downtime) {
+ // Create and inject down event
+ KeyEvent downEvent = new KeyEvent(downtime, downtime, KeyEvent.ACTION_DOWN,
+ KeyEvent.KEYCODE_BACK, 0 /* repeat */, 0 /* metaState */,
+ KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /* scancode */,
+ KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
+ InputDevice.SOURCE_KEYBOARD);
+ mInputManager.injectInputEvent(downEvent, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
+
+
+ // Create and inject up event
+ KeyEvent upEvent = KeyEvent.changeAction(downEvent, KeyEvent.ACTION_UP);
+ mInputManager.injectInputEvent(upEvent, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
+
+ downEvent.recycle();
+ upEvent.recycle();
+ }
+
private boolean handleHomeShortcuts(int displayId, IBinder focusedToken, KeyEvent event) {
// First we always handle the home key here, so applications
// can never break it, although if keyguard is on, we do let
diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
index 01fdc88..7862f58 100644
--- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java
+++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
@@ -41,6 +41,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.AppGlobals;
@@ -62,6 +63,7 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
@@ -1304,6 +1306,46 @@
return false;
}
+ /**
+ * Check if the targetPkg can be granted permission to access uri by
+ * the callingUid using the given modeFlags. See {@link #checkGrantUriPermissionUnlocked}.
+ *
+ * @param callingUid The uid of the grantor app that has permissions to the uri.
+ * @param targetPkg The package name of the granted app that needs permissions to the uri.
+ * @param uri The uri for which permissions should be granted.
+ * @param modeFlags The modes to grant. See {@link Intent#FLAG_GRANT_READ_URI_PERMISSION}, etc.
+ * @param userId The userId in which the uri is to be resolved.
+ * @return uid of the target or -1 if permission grant not required. Returns -1 if the caller
+ * does not hold INTERACT_ACROSS_USERS_FULL
+ * @throws SecurityException if the grant is not allowed.
+ */
+ @Override
+ @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+ public int checkGrantUriPermission_ignoreNonSystem(int callingUid, String targetPkg, Uri uri,
+ int modeFlags, int userId) {
+ if (!isCallerIsSystemOrPrivileged()) {
+ return Process.INVALID_UID;
+ }
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ return checkGrantUriPermissionUnlocked(callingUid, targetPkg, uri, modeFlags,
+ userId);
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ private boolean isCallerIsSystemOrPrivileged() {
+ final int uid = Binder.getCallingUid();
+ if (uid == Process.SYSTEM_UID || uid == Process.ROOT_UID) {
+ return true;
+ }
+ return ActivityManager.checkComponentPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ uid, /* owningUid = */-1, /* exported = */ true)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
@Override
public ArrayList<UriPermission> providePersistentUriGrants() {
final ArrayList<UriPermission> result = new ArrayList<>();
diff --git a/services/core/java/com/android/server/vibrator/HapticFeedbackCustomization.java b/services/core/java/com/android/server/vibrator/HapticFeedbackCustomization.java
index 3fb845f..e4f9607 100644
--- a/services/core/java/com/android/server/vibrator/HapticFeedbackCustomization.java
+++ b/services/core/java/com/android/server/vibrator/HapticFeedbackCustomization.java
@@ -19,7 +19,7 @@
import android.annotation.Nullable;
import android.content.res.Resources;
import android.os.VibrationEffect;
-import android.os.Vibrator;
+import android.os.VibratorInfo;
import android.os.vibrator.persistence.ParsedVibration;
import android.os.vibrator.persistence.VibrationXmlParser;
import android.text.TextUtils;
@@ -107,10 +107,10 @@
* @hide
*/
@Nullable
- static SparseArray<VibrationEffect> loadVibrations(Resources res, Vibrator vibrator)
+ static SparseArray<VibrationEffect> loadVibrations(Resources res, VibratorInfo vibratorInfo)
throws CustomizationParserException, IOException {
try {
- return loadVibrationsInternal(res, vibrator);
+ return loadVibrationsInternal(res, vibratorInfo);
} catch (VibrationXmlParser.VibrationXmlParserException
| XmlParserException
| XmlPullParserException e) {
@@ -121,7 +121,7 @@
@Nullable
private static SparseArray<VibrationEffect> loadVibrationsInternal(
- Resources res, Vibrator vibrator) throws
+ Resources res, VibratorInfo vibratorInfo) throws
CustomizationParserException,
IOException,
VibrationXmlParser.VibrationXmlParserException,
@@ -175,7 +175,7 @@
throw new CustomizationParserException(
"Unable to parse vibration element for effect " + effectId);
}
- VibrationEffect effect = parsedVibration.resolve(vibrator);
+ VibrationEffect effect = parsedVibration.resolve(vibratorInfo);
if (effect != null) {
if (effect.getDuration() == Long.MAX_VALUE) {
throw new CustomizationParserException(String.format(
diff --git a/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java b/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java
index 7c99543..3d89afa 100644
--- a/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java
+++ b/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java
@@ -21,6 +21,7 @@
import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.os.Vibrator;
+import android.os.VibratorInfo;
import android.util.Slog;
import android.util.SparseArray;
import android.view.HapticFeedbackConstants;
@@ -29,8 +30,6 @@
import java.io.IOException;
import java.io.PrintWriter;
-import java.util.HashSet;
-import java.util.Set;
/**
* Provides the {@link VibrationEffect} and {@link VibrationAttributes} for haptic feedback.
@@ -47,7 +46,7 @@
private static final VibrationAttributes HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES =
VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK);
- private final Vibrator mVibrator;
+ private final VibratorInfo mVibratorInfo;
private final boolean mHapticTextHandleEnabled;
// Vibrator effect for haptic feedback during boot when safe mode is enabled.
private final VibrationEffect mSafeModeEnabledVibrationEffect;
@@ -58,25 +57,25 @@
/** @hide */
public HapticFeedbackVibrationProvider(Resources res, Vibrator vibrator) {
- this(res, vibrator, loadHapticCustomizations(res, vibrator));
+ this(res, vibrator.getInfo());
+ }
+
+ /** @hide */
+ public HapticFeedbackVibrationProvider(Resources res, VibratorInfo vibratorInfo) {
+ this(res, vibratorInfo, loadHapticCustomizations(res, vibratorInfo));
}
/** @hide */
@VisibleForTesting HapticFeedbackVibrationProvider(
Resources res,
- Vibrator vibrator,
+ VibratorInfo vibratorInfo,
@Nullable SparseArray<VibrationEffect> hapticCustomizations) {
- mVibrator = vibrator;
+ mVibratorInfo = vibratorInfo;
mHapticTextHandleEnabled = res.getBoolean(
com.android.internal.R.bool.config_enableHapticTextHandle);
- if (hapticCustomizations != null) {
- // Clean up the customizations to remove vibrations which may not ever be used due to
- // Vibrator properties or other device configurations.
- removeUnsupportedVibrations(hapticCustomizations, vibrator);
- if (hapticCustomizations.size() == 0) {
- hapticCustomizations = null;
- }
+ if (hapticCustomizations != null && hapticCustomizations.size() == 0) {
+ hapticCustomizations = null;
}
mHapticCustomizations = hapticCustomizations;
@@ -257,7 +256,7 @@
if (effectHasCustomization(hapticFeedbackId)) {
return mHapticCustomizations.get(hapticFeedbackId);
}
- if (mVibrator.areAllPrimitivesSupported(primitiveId)) {
+ if (mVibratorInfo.isPrimitiveSupported(primitiveId)) {
return VibrationEffect.startComposition()
.addPrimitive(primitiveId, primitiveScale)
.compose();
@@ -270,9 +269,8 @@
if (effectHasCustomization(HapticFeedbackConstants.ASSISTANT_BUTTON)) {
return mHapticCustomizations.get(HapticFeedbackConstants.ASSISTANT_BUTTON);
}
- if (mVibrator.areAllPrimitivesSupported(
- VibrationEffect.Composition.PRIMITIVE_QUICK_RISE,
- VibrationEffect.Composition.PRIMITIVE_TICK)) {
+ if (mVibratorInfo.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_QUICK_RISE)
+ && mVibratorInfo.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_TICK)) {
// quiet ramp, short pause, then sharp tick
return VibrationEffect.startComposition()
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_RISE, 0.25f)
@@ -289,27 +287,12 @@
@Nullable
private static SparseArray<VibrationEffect> loadHapticCustomizations(
- Resources res, Vibrator vibrator) {
+ Resources res, VibratorInfo vibratorInfo) {
try {
- return HapticFeedbackCustomization.loadVibrations(res, vibrator);
+ return HapticFeedbackCustomization.loadVibrations(res, vibratorInfo);
} catch (IOException | HapticFeedbackCustomization.CustomizationParserException e) {
Slog.e(TAG, "Unable to load haptic customizations.", e);
return null;
}
}
-
- private static void removeUnsupportedVibrations(
- SparseArray<VibrationEffect> customizations, Vibrator vibrator) {
- Set<Integer> keysToRemove = new HashSet<>();
- for (int i = 0; i < customizations.size(); i++) {
- int key = customizations.keyAt(i);
- if (!vibrator.areVibrationFeaturesSupported(customizations.get(key))) {
- keysToRemove.add(key);
- }
- }
-
- for (int key : keysToRemove) {
- customizations.remove(key);
- }
- }
}
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index e296c7b..ee3d697 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -28,6 +28,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
+import android.content.res.Resources;
import android.hardware.vibrator.IVibrator;
import android.os.BatteryStats;
import android.os.Binder;
@@ -131,6 +132,7 @@
private final Object mLock = new Object();
private final Context mContext;
+ private final Injector mInjector;
private final PowerManager.WakeLock mWakeLock;
private final IBatteryStats mBatteryStatsService;
private final VibratorFrameworkStatsLogger mFrameworkStatsLogger;
@@ -162,6 +164,8 @@
@GuardedBy("mLock")
@Nullable private VibratorInfo mCombinedVibratorInfo;
+ @GuardedBy("mLock")
+ @Nullable private HapticFeedbackVibrationProvider mHapticFeedbackVibrationProvider;
private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
@@ -201,6 +205,7 @@
@VisibleForTesting
VibratorManagerService(Context context, Injector injector) {
mContext = context;
+ mInjector = injector;
mHandler = injector.createHandler(Looper.myLooper());
mVibrationSettings = new VibrationSettings(mContext, mHandler);
@@ -393,7 +398,42 @@
@Override // Binder call
public void vibrate(int uid, int displayId, String opPkg, @NonNull CombinedVibration effect,
@Nullable VibrationAttributes attrs, String reason, IBinder token) {
- vibrateInternal(uid, displayId, opPkg, effect, attrs, reason, token);
+ vibrateWithPermissionCheck(uid, displayId, opPkg, effect, attrs, reason, token);
+ }
+
+ @Override // Binder call
+ public void performHapticFeedback(
+ int uid, int displayId, String opPkg, int constant, boolean always, String reason,
+ IBinder token) {
+ performHapticFeedbackInternal(uid, displayId, opPkg, constant, always, reason, token);
+ }
+
+ /**
+ * An internal-only version of performHapticFeedback that allows the caller access to the
+ * {@link HalVibration}.
+ * The Vibration is only returned if it is ongoing after this method returns.
+ */
+ @VisibleForTesting
+ @Nullable
+ HalVibration performHapticFeedbackInternal(
+ int uid, int displayId, String opPkg, int constant, boolean always, String reason,
+ IBinder token) {
+ HapticFeedbackVibrationProvider hapticVibrationProvider = getHapticVibrationProvider();
+ if (hapticVibrationProvider == null) {
+ Slog.w(TAG, "performHapticFeedback; haptic vibration provider not ready.");
+ return null;
+ }
+ VibrationEffect effect = hapticVibrationProvider.getVibrationForHapticFeedback(constant);
+ if (effect == null) {
+ Slog.w(TAG, "performHapticFeedback; vibration absent for effect " + constant);
+ return null;
+ }
+ CombinedVibration combinedVibration = CombinedVibration.createParallel(effect);
+ VibrationAttributes attrs =
+ hapticVibrationProvider.getVibrationAttributesForHapticFeedback(
+ constant, /* bypassVibrationIntensitySetting= */ always);
+ return vibrateWithoutPermissionCheck(uid, displayId, opPkg, combinedVibration, attrs,
+ "performHapticFeedback: " + reason, token);
}
/**
@@ -403,93 +443,110 @@
*/
@VisibleForTesting
@Nullable
- HalVibration vibrateInternal(int uid, int displayId, String opPkg,
+ HalVibration vibrateWithPermissionCheck(int uid, int displayId, String opPkg,
@NonNull CombinedVibration effect, @Nullable VibrationAttributes attrs,
String reason, IBinder token) {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "vibrate, reason = " + reason);
try {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.VIBRATE, "vibrate");
-
- if (token == null) {
- Slog.e(TAG, "token must not be null");
- return null;
- }
- enforceUpdateAppOpsStatsPermission(uid);
- if (!isEffectValid(effect)) {
- return null;
- }
- attrs = fixupVibrationAttributes(attrs, effect);
- // Create Vibration.Stats as close to the received request as possible, for tracking.
- HalVibration vib = new HalVibration(token, effect,
- new Vibration.CallerInfo(attrs, uid, displayId, opPkg, reason));
- fillVibrationFallbacks(vib, effect);
-
- if (attrs.isFlagSet(VibrationAttributes.FLAG_INVALIDATE_SETTINGS_CACHE)) {
- // Force update of user settings before checking if this vibration effect should
- // be ignored or scaled.
- mVibrationSettings.update();
- }
-
- synchronized (mLock) {
- if (DEBUG) {
- Slog.d(TAG, "Starting vibrate for vibration " + vib.id);
- }
-
- // Check if user settings or DnD is set to ignore this vibration.
- Vibration.EndInfo vibrationEndInfo = shouldIgnoreVibrationLocked(vib.callerInfo);
-
- // Check if ongoing vibration is more important than this vibration.
- if (vibrationEndInfo == null) {
- vibrationEndInfo = shouldIgnoreVibrationForOngoingLocked(vib);
- }
-
- // If not ignored so far then try to start this vibration.
- if (vibrationEndInfo == null) {
- final long ident = Binder.clearCallingIdentity();
- try {
- if (mCurrentExternalVibration != null) {
- mCurrentExternalVibration.mute();
- vib.stats.reportInterruptedAnotherVibration(
- mCurrentExternalVibration.callerInfo);
- endExternalVibrateLocked(
- new Vibration.EndInfo(Vibration.Status.CANCELLED_SUPERSEDED,
- vib.callerInfo),
- /* continueExternalControl= */ false);
- } else if (mCurrentVibration != null) {
- if (mCurrentVibration.getVibration().canPipelineWith(vib)) {
- // Don't cancel the current vibration if it's pipeline-able.
- // Note that if there is a pending next vibration that can't be
- // pipelined, it will have already cancelled the current one, so we
- // don't need to consider it here as well.
- if (DEBUG) {
- Slog.d(TAG, "Pipelining vibration " + vib.id);
- }
- } else {
- vib.stats.reportInterruptedAnotherVibration(
- mCurrentVibration.getVibration().callerInfo);
- mCurrentVibration.notifyCancelled(
- new Vibration.EndInfo(Vibration.Status.CANCELLED_SUPERSEDED,
- vib.callerInfo),
- /* immediate= */ false);
- }
- }
- vibrationEndInfo = startVibrationLocked(vib);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- // Ignored or failed to start the vibration, end it and report metrics right away.
- if (vibrationEndInfo != null) {
- endVibrationLocked(vib, vibrationEndInfo, /* shouldWriteStats= */ true);
- }
- return vib;
- }
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.VIBRATE, "vibrate");
+ return vibrateInternal(uid, displayId, opPkg, effect, attrs, reason, token);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
}
}
+ HalVibration vibrateWithoutPermissionCheck(int uid, int displayId, String opPkg,
+ @NonNull CombinedVibration effect, @Nullable VibrationAttributes attrs,
+ String reason, IBinder token) {
+ Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "vibrate no perm check, reason = " + reason);
+ try {
+ return vibrateInternal(uid, displayId, opPkg, effect, attrs, reason, token);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+ }
+ }
+
+ private HalVibration vibrateInternal(int uid, int displayId, String opPkg,
+ @NonNull CombinedVibration effect, @Nullable VibrationAttributes attrs,
+ String reason, IBinder token) {
+ if (token == null) {
+ Slog.e(TAG, "token must not be null");
+ return null;
+ }
+ enforceUpdateAppOpsStatsPermission(uid);
+ if (!isEffectValid(effect)) {
+ return null;
+ }
+ attrs = fixupVibrationAttributes(attrs, effect);
+ // Create Vibration.Stats as close to the received request as possible, for tracking.
+ HalVibration vib = new HalVibration(token, effect,
+ new Vibration.CallerInfo(attrs, uid, displayId, opPkg, reason));
+ fillVibrationFallbacks(vib, effect);
+
+ if (attrs.isFlagSet(VibrationAttributes.FLAG_INVALIDATE_SETTINGS_CACHE)) {
+ // Force update of user settings before checking if this vibration effect should
+ // be ignored or scaled.
+ mVibrationSettings.update();
+ }
+
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "Starting vibrate for vibration " + vib.id);
+ }
+
+ // Check if user settings or DnD is set to ignore this vibration.
+ Vibration.EndInfo vibrationEndInfo = shouldIgnoreVibrationLocked(vib.callerInfo);
+
+ // Check if ongoing vibration is more important than this vibration.
+ if (vibrationEndInfo == null) {
+ vibrationEndInfo = shouldIgnoreVibrationForOngoingLocked(vib);
+ }
+
+ // If not ignored so far then try to start this vibration.
+ if (vibrationEndInfo == null) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ if (mCurrentExternalVibration != null) {
+ mCurrentExternalVibration.mute();
+ vib.stats.reportInterruptedAnotherVibration(
+ mCurrentExternalVibration.callerInfo);
+ endExternalVibrateLocked(
+ new Vibration.EndInfo(Vibration.Status.CANCELLED_SUPERSEDED,
+ vib.callerInfo),
+ /* continueExternalControl= */ false);
+ } else if (mCurrentVibration != null) {
+ if (mCurrentVibration.getVibration().canPipelineWith(vib)) {
+ // Don't cancel the current vibration if it's pipeline-able.
+ // Note that if there is a pending next vibration that can't be
+ // pipelined, it will have already cancelled the current one, so we
+ // don't need to consider it here as well.
+ if (DEBUG) {
+ Slog.d(TAG, "Pipelining vibration " + vib.id);
+ }
+ } else {
+ vib.stats.reportInterruptedAnotherVibration(
+ mCurrentVibration.getVibration().callerInfo);
+ mCurrentVibration.notifyCancelled(
+ new Vibration.EndInfo(Vibration.Status.CANCELLED_SUPERSEDED,
+ vib.callerInfo),
+ /* immediate= */ false);
+ }
+ }
+ vibrationEndInfo = startVibrationLocked(vib);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ // Ignored or failed to start the vibration, end it and report metrics right away.
+ if (vibrationEndInfo != null) {
+ endVibrationLocked(vib, vibrationEndInfo, /* shouldWriteStats= */ true);
+ }
+ return vib;
+ }
+ }
+
@Override // Binder call
public void cancelVibrate(int usageFilter, IBinder token) {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "cancelVibrate");
@@ -1315,6 +1372,11 @@
return new VibratorController(vibratorId, listener);
}
+ HapticFeedbackVibrationProvider createHapticFeedbackVibrationProvider(
+ Resources resources, VibratorInfo vibratorInfo) {
+ return new HapticFeedbackVibrationProvider(resources, vibratorInfo);
+ }
+
void addService(String name, IBinder service) {
ServiceManager.addService(name, service);
}
@@ -1831,6 +1893,22 @@
}
}
+ private HapticFeedbackVibrationProvider getHapticVibrationProvider() {
+ synchronized (mLock) {
+ // Used a cached haptic vibration provider if one exists.
+ if (mHapticFeedbackVibrationProvider != null) {
+ return mHapticFeedbackVibrationProvider;
+ }
+ VibratorInfo combinedVibratorInfo = getCombinedVibratorInfo();
+ if (combinedVibratorInfo == null) {
+ return null;
+ }
+ return mHapticFeedbackVibrationProvider =
+ mInjector.createHapticFeedbackVibrationProvider(
+ mContext.getResources(), combinedVibratorInfo);
+ }
+ }
+
private VibratorInfo getCombinedVibratorInfo() {
synchronized (mLock) {
// Used a cached resolving vibrator if one exists.
@@ -2068,6 +2146,9 @@
if ("cancel".equals(cmd)) {
return runCancel();
}
+ if ("feedback".equals(cmd)) {
+ return runHapticFeedback();
+ }
return handleDefaultCommands(cmd);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
@@ -2098,16 +2179,10 @@
// only cancel background vibrations.
IBinder deathBinder = commonOptions.background ? VibratorManagerService.this
: mShellCallbacksToken;
- HalVibration vib = vibrateInternal(Binder.getCallingUid(), Display.DEFAULT_DISPLAY,
- SHELL_PACKAGE_NAME, combined, attrs, commonOptions.description, deathBinder);
- if (vib != null && !commonOptions.background) {
- try {
- // Waits for the client vibration to finish, but the VibrationThread may still
- // do cleanup after this.
- vib.waitForEnd();
- } catch (InterruptedException e) {
- }
- }
+ HalVibration vib = vibrateWithPermissionCheck(Binder.getCallingUid(),
+ Display.DEFAULT_DISPLAY, SHELL_PACKAGE_NAME, combined, attrs,
+ commonOptions.description, deathBinder);
+ maybeWaitOnVibration(vib, commonOptions);
}
private int runMono() {
@@ -2155,6 +2230,21 @@
return 0;
}
+ private int runHapticFeedback() {
+ CommonOptions commonOptions = new CommonOptions();
+ int constant = Integer.parseInt(getNextArgRequired());
+
+ IBinder deathBinder = commonOptions.background ? VibratorManagerService.this
+ : mShellCallbacksToken;
+ HalVibration vib = performHapticFeedbackInternal(Binder.getCallingUid(),
+ Display.DEFAULT_DISPLAY, SHELL_PACKAGE_NAME, constant,
+ /* always= */ commonOptions.force, /* reason= */ commonOptions.description,
+ deathBinder);
+ maybeWaitOnVibration(vib, commonOptions);
+
+ return 0;
+ }
+
private VibrationEffect nextEffect() {
VibrationEffect.Composition composition = VibrationEffect.startComposition();
String nextArg;
@@ -2364,6 +2454,17 @@
}
}
+ private void maybeWaitOnVibration(HalVibration vib, CommonOptions commonOptions) {
+ if (vib != null && !commonOptions.background) {
+ try {
+ // Waits for the client vibration to finish, but the VibrationThread may still
+ // do cleanup after this.
+ vib.waitForEnd();
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+
@Override
public void onHelp() {
try (PrintWriter pw = getOutPrintWriter();) {
@@ -2389,6 +2490,10 @@
pw.println(" XML containing a single effect it runs on all vibrators in sync.");
pw.println(" cancel");
pw.println(" Cancels any active vibration");
+ pw.println(" feedback [-f] [-d <description>] <constant>");
+ pw.println(" Performs a haptic feedback with the given constant.");
+ pw.println(" The force (-f) option enables the `always` configuration, which");
+ pw.println(" plays the haptic irrespective of the vibration intensity settings");
pw.println("");
pw.println("Effect commands:");
pw.println(" oneshot [-w delay] [-a] <duration> [<amplitude>]");
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 53d1adf..fd42077 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -6183,6 +6183,8 @@
@Override
public void onPackageReplaced(ApplicationInfo aInfo) {
synchronized (mGlobalLock) {
+ // In case if setWindowManager hasn't been called yet when booting.
+ if (mRootWindowContainer == null) return;
mRootWindowContainer.updateActivityApplicationInfo(aInfo);
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 1871cf6..707b779 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.inputmethodservice.InputMethodService.ENABLE_HIDE_IME_CAPTION_BAR;
import static android.view.Display.TYPE_INTERNAL;
import static android.view.InsetsFrameProvider.SOURCE_ARBITRARY_RECTANGLE;
import static android.view.InsetsFrameProvider.SOURCE_CONTAINER_BOUNDS;
@@ -1201,8 +1202,8 @@
throw new IllegalArgumentException("IME insets must be provided by a window.");
}
- if (mNavigationBar != null && navigationBarPosition(displayFrames.mRotation)
- == NAV_BAR_BOTTOM) {
+ if (!ENABLE_HIDE_IME_CAPTION_BAR && mNavigationBar != null
+ && navigationBarPosition(displayFrames.mRotation) == NAV_BAR_BOTTOM) {
// In gesture navigation, nav bar frame is larger than frame to calculate insets.
// IME should not provide frame which is smaller than the nav bar frame. Otherwise,
// nav bar might be overlapped with the content of the client when IME is shown.
diff --git a/services/core/java/com/android/server/wm/RemoteDisplayChangeController.java b/services/core/java/com/android/server/wm/RemoteDisplayChangeController.java
index e646f14..106e142 100644
--- a/services/core/java/com/android/server/wm/RemoteDisplayChangeController.java
+++ b/services/core/java/com/android/server/wm/RemoteDisplayChangeController.java
@@ -21,6 +21,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.RemoteException;
+import android.os.Trace;
import android.util.Slog;
import android.view.IDisplayChangeWindowCallback;
import android.window.DisplayAreaInfo;
@@ -40,6 +41,7 @@
public class RemoteDisplayChangeController {
private static final String TAG = "RemoteDisplayChangeController";
+ private static final String REMOTE_DISPLAY_CHANGE_TRACE_TAG = "RemoteDisplayChange";
private static final int REMOTE_DISPLAY_CHANGE_TIMEOUT_MS = 800;
@@ -82,6 +84,10 @@
}
mCallbacks.add(callback);
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) {
+ Trace.beginAsyncSection(REMOTE_DISPLAY_CHANGE_TRACE_TAG, callback.hashCode());
+ }
+
if (newDisplayAreaInfo != null) {
ProtoLog.v(WM_DEBUG_CONFIGURATION,
"Starting remote display change: "
@@ -122,6 +128,10 @@
mCallbacks.clear();
}
callback.onContinueRemoteDisplayChange(null /* transaction */);
+
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) {
+ Trace.endAsyncSection(REMOTE_DISPLAY_CHANGE_TRACE_TAG, callback.hashCode());
+ }
}
}
}
@@ -137,13 +147,23 @@
for (int i = 0; i < idx; ++i) {
// Expect remote callbacks in order. If they don't come in order, then force
// ordering by continuing everything up until this one with empty transactions.
- mCallbacks.get(i).onContinueRemoteDisplayChange(null /* transaction */);
+ ContinueRemoteDisplayChangeCallback currentCallback = mCallbacks.get(i);
+ currentCallback.onContinueRemoteDisplayChange(null /* transaction */);
+
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) {
+ Trace.endAsyncSection(REMOTE_DISPLAY_CHANGE_TRACE_TAG,
+ currentCallback.hashCode());
+ }
}
mCallbacks.subList(0, idx + 1).clear();
if (mCallbacks.isEmpty()) {
mService.mH.removeCallbacks(mTimeoutRunnable);
}
callback.onContinueRemoteDisplayChange(transaction);
+
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) {
+ Trace.endAsyncSection(REMOTE_DISPLAY_CHANGE_TRACE_TAG, callback.hashCode());
+ }
}
}
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 2a995b2..ec5378f 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -41,6 +41,7 @@
"com_android_server_companion_virtual_InputController.cpp",
"com_android_server_devicepolicy_CryptoTestHelper.cpp",
"com_android_server_display_DisplayControl.cpp",
+ "com_android_server_display_SmallAreaDetectionController.cpp",
"com_android_server_connectivity_Vpn.cpp",
"com_android_server_gpu_GpuService.cpp",
"com_android_server_HardwarePropertiesManagerService.cpp",
diff --git a/services/core/jni/com_android_server_display_SmallAreaDetectionController.cpp b/services/core/jni/com_android_server_display_SmallAreaDetectionController.cpp
new file mode 100644
index 0000000..b256f16
--- /dev/null
+++ b/services/core/jni/com_android_server_display_SmallAreaDetectionController.cpp
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "SmallAreaDetectionController"
+
+#include <gui/SurfaceComposerClient.h>
+#include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedPrimitiveArray.h>
+
+#include "jni.h"
+#include "utils/Log.h"
+
+namespace android {
+static void nativeUpdateSmallAreaDetection(JNIEnv* env, jclass clazz, jintArray juids,
+ jfloatArray jthresholds) {
+ if (juids == nullptr || jthresholds == nullptr) return;
+
+ ScopedIntArrayRO uids(env, juids);
+ ScopedFloatArrayRO thresholds(env, jthresholds);
+
+ if (uids.size() != thresholds.size()) {
+ ALOGE("uids size exceeds thresholds size!");
+ return;
+ }
+
+ std::vector<int32_t> uidVector;
+ std::vector<float> thresholdVector;
+ size_t size = uids.size();
+ uidVector.reserve(size);
+ thresholdVector.reserve(size);
+ for (int i = 0; i < size; i++) {
+ uidVector.push_back(static_cast<int32_t>(uids[i]));
+ thresholdVector.push_back(static_cast<float>(thresholds[i]));
+ }
+ SurfaceComposerClient::updateSmallAreaDetection(uidVector, thresholdVector);
+}
+
+static void nativeSetSmallAreaDetectionThreshold(JNIEnv* env, jclass clazz, jint uid,
+ jfloat threshold) {
+ SurfaceComposerClient::setSmallAreaDetectionThreshold(uid, threshold);
+}
+
+static const JNINativeMethod gMethods[] = {
+ {"nativeUpdateSmallAreaDetection", "([I[F)V", (void*)nativeUpdateSmallAreaDetection},
+ {"nativeSetSmallAreaDetectionThreshold", "(IF)V",
+ (void*)nativeSetSmallAreaDetectionThreshold},
+};
+
+int register_android_server_display_smallAreaDetectionController(JNIEnv* env) {
+ return jniRegisterNativeMethods(env, "com/android/server/display/SmallAreaDetectionController",
+ gMethods, NELEM(gMethods));
+}
+
+}; // namespace android
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index 97d7be6..f6f6737 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -67,6 +67,7 @@
int register_com_android_server_wm_TaskFpsCallbackController(JNIEnv* env);
int register_com_android_server_display_DisplayControl(JNIEnv* env);
int register_com_android_server_SystemClockTime(JNIEnv* env);
+int register_android_server_display_smallAreaDetectionController(JNIEnv* env);
};
using namespace android;
@@ -126,5 +127,6 @@
register_com_android_server_wm_TaskFpsCallbackController(env);
register_com_android_server_display_DisplayControl(env);
register_com_android_server_SystemClockTime(env);
+ register_android_server_display_smallAreaDetectionController(env);
return JNI_VERSION_1_4;
}
diff --git a/services/tests/displayservicetests/Android.bp b/services/tests/displayservicetests/Android.bp
index fb14419..e28028f9 100644
--- a/services/tests/displayservicetests/Android.bp
+++ b/services/tests/displayservicetests/Android.bp
@@ -35,6 +35,7 @@
"mockingservicestests-utils-mockito",
"platform-compat-test-rules",
"platform-test-annotations",
+ "service-permission.stubs.system_server",
"services.core",
"servicestests-utils",
"testables",
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
index 40d7a77..a23539e 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -64,6 +64,7 @@
import android.content.Context;
import android.content.ContextWrapper;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
import android.content.res.Resources;
import android.graphics.Insets;
import android.graphics.Rect;
@@ -113,6 +114,7 @@
import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.input.InputManagerInternal;
import com.android.server.lights.LightsManager;
+import com.android.server.pm.UserManagerInternal;
import com.android.server.sensors.SensorManagerInternal;
import com.android.server.wm.WindowManagerInternal;
@@ -291,10 +293,11 @@
@Mock LocalDisplayAdapter.SurfaceControlProxy mSurfaceControlProxy;
@Mock IBinder mMockDisplayToken;
@Mock SensorManagerInternal mMockSensorManagerInternal;
-
@Mock SensorManager mSensorManager;
-
@Mock DisplayDeviceConfig mMockDisplayDeviceConfig;
+ @Mock PackageManagerInternal mMockPackageManagerInternal;
+ @Mock UserManagerInternal mMockUserManagerInternal;
+
@Captor ArgumentCaptor<ContentRecordingSession> mContentRecordingSessionCaptor;
@Mock DisplayManagerFlags mMockFlags;
@@ -315,6 +318,10 @@
LocalServices.removeServiceForTest(VirtualDeviceManagerInternal.class);
LocalServices.addService(
VirtualDeviceManagerInternal.class, mMockVirtualDeviceManagerInternal);
+ LocalServices.removeServiceForTest(PackageManagerInternal.class);
+ LocalServices.addService(PackageManagerInternal.class, mMockPackageManagerInternal);
+ LocalServices.removeServiceForTest(UserManagerInternal.class);
+ LocalServices.addService(UserManagerInternal.class, mMockUserManagerInternal);
// TODO: b/287945043
mContext = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
mResources = Mockito.spy(mContext.getResources());
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/SmallAreaDetectionControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/display/SmallAreaDetectionControllerTest.java
new file mode 100644
index 0000000..1ce79a5
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/display/SmallAreaDetectionControllerTest.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+import static android.os.Process.INVALID_UID;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ContextWrapper;
+import android.content.pm.PackageManagerInternal;
+import android.provider.DeviceConfigInterface;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.LocalServices;
+import com.android.server.pm.UserManagerInternal;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class SmallAreaDetectionControllerTest {
+
+ @Rule
+ public MockitoRule mRule = MockitoJUnit.rule();
+
+ @Mock
+ private PackageManagerInternal mMockPackageManagerInternal;
+ @Mock
+ private UserManagerInternal mMockUserManagerInternal;
+
+ private SmallAreaDetectionController mSmallAreaDetectionController;
+
+ private static final String PKG_A = "com.a.b.c";
+ private static final String PKG_B = "com.d.e.f";
+ private static final String PKG_NOT_INSTALLED = "com.not.installed";
+ private static final float THRESHOLD_A = 0.05f;
+ private static final float THRESHOLD_B = 0.07f;
+ private static final int USER_1 = 110;
+ private static final int USER_2 = 111;
+ private static final int UID_A_1 = 11011111;
+ private static final int UID_A_2 = 11111111;
+ private static final int UID_B_1 = 11022222;
+ private static final int UID_B_2 = 11122222;
+
+ @Before
+ public void setup() {
+ LocalServices.removeServiceForTest(PackageManagerInternal.class);
+ LocalServices.addService(PackageManagerInternal.class, mMockPackageManagerInternal);
+ LocalServices.removeServiceForTest(UserManagerInternal.class);
+ LocalServices.addService(UserManagerInternal.class, mMockUserManagerInternal);
+
+ when(mMockUserManagerInternal.getUserIds()).thenReturn(new int[]{USER_1, USER_2});
+ when(mMockPackageManagerInternal.getPackageUid(PKG_A, 0, USER_1)).thenReturn(UID_A_1);
+ when(mMockPackageManagerInternal.getPackageUid(PKG_A, 0, USER_2)).thenReturn(UID_A_2);
+ when(mMockPackageManagerInternal.getPackageUid(PKG_B, 0, USER_1)).thenReturn(UID_B_1);
+ when(mMockPackageManagerInternal.getPackageUid(PKG_B, 0, USER_2)).thenReturn(UID_B_2);
+ when(mMockPackageManagerInternal.getPackageUid(PKG_NOT_INSTALLED, 0, USER_1)).thenReturn(
+ INVALID_UID);
+ when(mMockPackageManagerInternal.getPackageUid(PKG_NOT_INSTALLED, 0, USER_2)).thenReturn(
+ INVALID_UID);
+
+ mSmallAreaDetectionController = spy(new SmallAreaDetectionController(
+ new ContextWrapper(ApplicationProvider.getApplicationContext()),
+ DeviceConfigInterface.REAL));
+ doNothing().when(mSmallAreaDetectionController).updateSmallAreaDetection(any(), any());
+ }
+
+ @Test
+ public void testUpdateAllowlist_validProperty() {
+ final String property = PKG_A + ":" + THRESHOLD_A + "," + PKG_B + ":" + THRESHOLD_B;
+ mSmallAreaDetectionController.updateAllowlist(property);
+
+ final int[] resultUidArray = {UID_A_1, UID_B_1, UID_A_2, UID_B_2};
+ final float[] resultThresholdArray = {THRESHOLD_A, THRESHOLD_B, THRESHOLD_A, THRESHOLD_B};
+ verify(mSmallAreaDetectionController).updateSmallAreaDetection(eq(resultUidArray),
+ eq(resultThresholdArray));
+ }
+
+ @Test
+ public void testUpdateAllowlist_includeInvalidRow() {
+ final String property = PKG_A + "," + PKG_B + ":" + THRESHOLD_B;
+ mSmallAreaDetectionController.updateAllowlist(property);
+
+ final int[] resultUidArray = {UID_B_1, UID_B_2};
+ final float[] resultThresholdArray = {THRESHOLD_B, THRESHOLD_B};
+ verify(mSmallAreaDetectionController).updateSmallAreaDetection(eq(resultUidArray),
+ eq(resultThresholdArray));
+ }
+
+ @Test
+ public void testUpdateAllowlist_includeNotInstalledPkg() {
+ final String property =
+ PKG_A + ":" + THRESHOLD_A + "," + PKG_NOT_INSTALLED + ":" + THRESHOLD_B;
+ mSmallAreaDetectionController.updateAllowlist(property);
+
+ final int[] resultUidArray = {UID_A_1, UID_A_2};
+ final float[] resultThresholdArray = {THRESHOLD_A, THRESHOLD_A};
+ verify(mSmallAreaDetectionController).updateSmallAreaDetection(eq(resultUidArray),
+ eq(resultThresholdArray));
+ }
+
+ @Test
+ public void testUpdateAllowlist_invalidProperty() {
+ final String property = PKG_A;
+ mSmallAreaDetectionController.updateAllowlist(property);
+
+ verify(mSmallAreaDetectionController, never()).updateSmallAreaDetection(any(), any());
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverServiceTest.java
index c7e1bda..e58a234 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverServiceTest.java
@@ -16,41 +16,51 @@
package com.android.server.pm;
+import static android.content.Intent.FLAG_RECEIVER_FOREGROUND;
import static android.content.pm.PackageManager.DELETE_KEEP_DATA;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
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 static org.mockito.Mockito.when;
+import android.app.AppOpsManager;
import android.content.Context;
+import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.LauncherApps;
+import android.content.pm.PackageArchiver;
import android.content.pm.PackageManager;
import android.content.pm.VersionedPackage;
import android.os.Binder;
import android.os.Build;
+import android.os.Bundle;
import android.os.ParcelableException;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
+import android.text.TextUtils;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.pm.pkg.ArchiveState;
import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.PackageUserStateImpl;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -64,7 +74,8 @@
public class PackageArchiverServiceTest {
private static final String PACKAGE = "com.example";
- private static final String CALLER_PACKAGE = "com.vending";
+ private static final String CALLER_PACKAGE = "com.caller";
+ private static final String INSTALLER_PACKAGE = "com.installer";
@Rule
public final MockSystemRule mMockSystem = new MockSystemRule();
@@ -84,11 +95,11 @@
private final InstallSource mInstallSource =
InstallSource.create(
- CALLER_PACKAGE,
- CALLER_PACKAGE,
- CALLER_PACKAGE,
+ INSTALLER_PACKAGE,
+ INSTALLER_PACKAGE,
+ INSTALLER_PACKAGE,
Binder.getCallingUid(),
- CALLER_PACKAGE,
+ INSTALLER_PACKAGE,
/* installerAttributionTag= */ null,
/* packageSource= */ 0);
@@ -96,6 +107,8 @@
private final int mUserId = UserHandle.CURRENT.getIdentifier();
+ private PackageUserStateImpl mUserState;
+
private PackageSetting mPackageSetting;
private PackageArchiverService mArchiveService;
@@ -116,11 +129,16 @@
when(mComputer.getPackageStateFiltered(eq(PACKAGE), anyInt(), anyInt())).thenReturn(
mPackageState);
+ when(mComputer.getPackageStateFiltered(eq(INSTALLER_PACKAGE), anyInt(),
+ anyInt())).thenReturn(mock(PackageStateInternal.class));
when(mPackageState.getPackageName()).thenReturn(PACKAGE);
when(mPackageState.getInstallSource()).thenReturn(mInstallSource);
mPackageSetting = createBasicPackageSetting();
when(mMockSystem.mocks().getSettings().getPackageLPr(eq(PACKAGE))).thenReturn(
mPackageSetting);
+ mUserState = new PackageUserStateImpl().setInstalled(true);
+ mPackageSetting.setUserState(mUserId, mUserState);
+ when(mPackageState.getUserStateOrDefault(eq(mUserId))).thenReturn(mUserState);
when(mContext.getSystemService(LauncherApps.class)).thenReturn(mLauncherApps);
when(mLauncherApps.getActivityList(eq(PACKAGE), eq(UserHandle.CURRENT))).thenReturn(
mLauncherActivityInfos);
@@ -135,9 +153,7 @@
Exception e = assertThrows(
SecurityException.class,
() -> mArchiveService.requestArchive(PACKAGE, "different", mIntentSender,
- UserHandle.CURRENT
- )
- );
+ UserHandle.CURRENT));
assertThat(e).hasMessageThat().isEqualTo(
String.format(
"The UID %s of callerPackageName set by the caller doesn't match the "
@@ -154,9 +170,7 @@
Exception e = assertThrows(
ParcelableException.class,
() -> mArchiveService.requestArchive(PACKAGE, CALLER_PACKAGE, mIntentSender,
- UserHandle.CURRENT
- )
- );
+ UserHandle.CURRENT));
assertThat(e.getCause()).isInstanceOf(PackageManager.NameNotFoundException.class);
assertThat(e.getCause()).hasMessageThat().isEqualTo(
String.format("Package %s not found.", PACKAGE));
@@ -169,9 +183,7 @@
Exception e = assertThrows(
ParcelableException.class,
() -> mArchiveService.requestArchive(PACKAGE, CALLER_PACKAGE, mIntentSender,
- UserHandle.CURRENT
- )
- );
+ UserHandle.CURRENT));
assertThat(e.getCause()).isInstanceOf(PackageManager.NameNotFoundException.class);
assertThat(e.getCause()).hasMessageThat().isEqualTo(
String.format("Package %s not found.", PACKAGE));
@@ -193,25 +205,28 @@
Exception e = assertThrows(
ParcelableException.class,
() -> mArchiveService.requestArchive(PACKAGE, CALLER_PACKAGE, mIntentSender,
- UserHandle.CURRENT
- )
- );
+ UserHandle.CURRENT));
assertThat(e.getCause()).isInstanceOf(PackageManager.NameNotFoundException.class);
assertThat(e.getCause()).hasMessageThat().isEqualTo(
String.format("No installer found to archive app %s.", PACKAGE));
}
@Test
- public void archiveApp_success() {
- List<ArchiveState.ArchiveActivityInfo> activityInfos = new ArrayList<>();
- for (LauncherActivityInfo mainActivity : createLauncherActivities()) {
- // TODO(b/278553670) Extract and store launcher icons
- ArchiveState.ArchiveActivityInfo activityInfo = new ArchiveState.ArchiveActivityInfo(
- mainActivity.getLabel().toString(),
- Path.of("/TODO"), null);
- activityInfos.add(activityInfo);
- }
+ public void archiveApp_noMainActivities() {
+ when(mLauncherApps.getActivityList(eq(PACKAGE), eq(UserHandle.CURRENT))).thenReturn(
+ List.of());
+ Exception e = assertThrows(
+ ParcelableException.class,
+ () -> mArchiveService.requestArchive(PACKAGE, CALLER_PACKAGE, mIntentSender,
+ UserHandle.CURRENT));
+ assertThat(e.getCause()).isInstanceOf(PackageManager.NameNotFoundException.class);
+ assertThat(e.getCause()).hasMessageThat().isEqualTo(
+ TextUtils.formatSimple("The app %s does not have a main activity.", PACKAGE));
+ }
+
+ @Test
+ public void archiveApp_success() {
mArchiveService.requestArchive(PACKAGE, CALLER_PACKAGE, mIntentSender, UserHandle.CURRENT);
verify(mInstallerService).uninstall(
@@ -220,7 +235,112 @@
eq(UserHandle.CURRENT.getIdentifier()));
assertThat(mPackageSetting.readUserState(
UserHandle.CURRENT.getIdentifier()).getArchiveState()).isEqualTo(
- new ArchiveState(activityInfos, CALLER_PACKAGE));
+ createArchiveState());
+ }
+
+ @Test
+ public void unarchiveApp_callerPackageNameIncorrect() {
+ mUserState.setArchiveState(createArchiveState()).setInstalled(false);
+
+ Exception e = assertThrows(
+ SecurityException.class,
+ () -> mArchiveService.requestUnarchive(PACKAGE, "different",
+ UserHandle.CURRENT));
+ assertThat(e).hasMessageThat().isEqualTo(
+ String.format(
+ "The UID %s of callerPackageName set by the caller doesn't match the "
+ + "caller's actual UID %s.",
+ 0,
+ Binder.getCallingUid()));
+ }
+
+ @Test
+ public void unarchiveApp_packageNotInstalled() {
+ mUserState.setArchiveState(createArchiveState()).setInstalled(false);
+ when(mComputer.getPackageStateFiltered(eq(PACKAGE), anyInt(), anyInt())).thenReturn(
+ null);
+
+ Exception e = assertThrows(
+ ParcelableException.class,
+ () -> mArchiveService.requestUnarchive(PACKAGE, CALLER_PACKAGE,
+ UserHandle.CURRENT));
+ assertThat(e.getCause()).isInstanceOf(PackageManager.NameNotFoundException.class);
+ assertThat(e.getCause()).hasMessageThat().isEqualTo(
+ String.format("Package %s not found.", PACKAGE));
+ }
+
+ @Test
+ public void unarchiveApp_notArchived() {
+ Exception e = assertThrows(
+ ParcelableException.class,
+ () -> mArchiveService.requestUnarchive(PACKAGE, CALLER_PACKAGE,
+ UserHandle.CURRENT));
+ assertThat(e.getCause()).isInstanceOf(PackageManager.NameNotFoundException.class);
+ assertThat(e.getCause()).hasMessageThat().isEqualTo(
+ String.format("Package %s is not currently archived.", PACKAGE));
+ }
+
+ @Test
+ public void unarchiveApp_noInstallerFound() {
+ mUserState.setArchiveState(createArchiveState());
+ InstallSource otherInstallSource =
+ InstallSource.create(
+ CALLER_PACKAGE,
+ CALLER_PACKAGE,
+ /* installerPackageName= */ null,
+ Binder.getCallingUid(),
+ /* updateOwnerPackageName= */ null,
+ /* installerAttributionTag= */ null,
+ /* packageSource= */ 0);
+ when(mPackageState.getInstallSource()).thenReturn(otherInstallSource);
+
+ Exception e = assertThrows(
+ ParcelableException.class,
+ () -> mArchiveService.requestUnarchive(PACKAGE, CALLER_PACKAGE,
+ UserHandle.CURRENT));
+ assertThat(e.getCause()).isInstanceOf(PackageManager.NameNotFoundException.class);
+ assertThat(e.getCause()).hasMessageThat().isEqualTo(
+ String.format("No installer found to unarchive app %s.", PACKAGE));
+ }
+
+ @Test
+ public void unarchiveApp_success() {
+ mUserState.setArchiveState(createArchiveState()).setInstalled(false);
+
+ mArchiveService.requestUnarchive(PACKAGE, CALLER_PACKAGE, UserHandle.CURRENT);
+ mMockSystem.mocks().getHandler().flush();
+
+ ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+ verify(mContext).sendOrderedBroadcastAsUser(
+ intentCaptor.capture(),
+ eq(UserHandle.CURRENT),
+ /* receiverPermission = */ isNull(),
+ eq(AppOpsManager.OP_NONE),
+ any(Bundle.class),
+ /* resultReceiver= */ isNull(),
+ /* scheduler= */ isNull(),
+ /* initialCode= */ eq(0),
+ /* initialData= */ isNull(),
+ /* initialExtras= */ isNull());
+ Intent intent = intentCaptor.getValue();
+ assertThat(intent.getFlags() & FLAG_RECEIVER_FOREGROUND).isNotEqualTo(0);
+ assertThat(intent.getStringExtra(PackageArchiver.EXTRA_UNARCHIVE_PACKAGE_NAME)).isEqualTo(
+ PACKAGE);
+ assertThat(
+ intent.getBooleanExtra(PackageArchiver.EXTRA_UNARCHIVE_ALL_USERS, true)).isFalse();
+ assertThat(intent.getPackage()).isEqualTo(INSTALLER_PACKAGE);
+ }
+
+ private static ArchiveState createArchiveState() {
+ List<ArchiveState.ArchiveActivityInfo> activityInfos = new ArrayList<>();
+ for (LauncherActivityInfo mainActivity : createLauncherActivities()) {
+ // TODO(b/278553670) Extract and store launcher icons
+ ArchiveState.ArchiveActivityInfo activityInfo = new ArchiveState.ArchiveActivityInfo(
+ mainActivity.getLabel().toString(),
+ Path.of("/TODO"), null);
+ activityInfos.add(activityInfo);
+ }
+ return new ArchiveState(activityInfos, INSTALLER_PACKAGE);
}
private static List<LauncherActivityInfo> createLauncherActivities() {
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackCustomizationTest.java b/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackCustomizationTest.java
index 10b49c6..bc826a3 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackCustomizationTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackCustomizationTest.java
@@ -30,7 +30,6 @@
import android.content.res.Resources;
import android.os.VibrationEffect;
-import android.os.Vibrator;
import android.os.VibratorInfo;
import android.util.AtomicFile;
import android.util.SparseArray;
@@ -73,12 +72,10 @@
VibrationEffect.createWaveform(new long[] {123}, new int[] {254}, -1);
@Mock private Resources mResourcesMock;
- @Mock private Vibrator mVibratorMock;
@Mock private VibratorInfo mVibratorInfoMock;
@Before
public void setUp() {
- when(mVibratorMock.getInfo()).thenReturn(mVibratorInfoMock);
when(mVibratorInfoMock.areVibrationFeaturesSupported(any())).thenReturn(true);
}
@@ -220,17 +217,17 @@
public void testParseCustomizations_noCustomizationFile_returnsNull() throws Exception {
setCustomizationFilePath("");
- assertThat(HapticFeedbackCustomization.loadVibrations(mResourcesMock, mVibratorMock))
+ assertThat(HapticFeedbackCustomization.loadVibrations(mResourcesMock, mVibratorInfoMock))
.isNull();
setCustomizationFilePath(null);
- assertThat(HapticFeedbackCustomization.loadVibrations(mResourcesMock, mVibratorMock))
+ assertThat(HapticFeedbackCustomization.loadVibrations(mResourcesMock, mVibratorInfoMock))
.isNull();
setCustomizationFilePath("non_existent_file.xml");
- assertThat(HapticFeedbackCustomization.loadVibrations(mResourcesMock, mVibratorMock))
+ assertThat(HapticFeedbackCustomization.loadVibrations(mResourcesMock, mVibratorInfoMock))
.isNull();
}
@@ -387,7 +384,7 @@
String xml, SparseArray<VibrationEffect> expectedCustomizations) throws Exception {
setupCustomizationFile(xml);
assertThat(expectedCustomizations.contentEquals(
- HapticFeedbackCustomization.loadVibrations(mResourcesMock, mVibratorMock)))
+ HapticFeedbackCustomization.loadVibrations(mResourcesMock, mVibratorInfoMock)))
.isTrue();
}
@@ -395,13 +392,15 @@
setupCustomizationFile(xml);
assertThrows("Expected haptic feedback customization to fail for " + xml,
CustomizationParserException.class,
- () -> HapticFeedbackCustomization.loadVibrations(mResourcesMock, mVibratorMock));
+ () -> HapticFeedbackCustomization.loadVibrations(
+ mResourcesMock, mVibratorInfoMock));
}
private void assertParseCustomizationsFails() throws Exception {
assertThrows("Expected haptic feedback customization to fail",
CustomizationParserException.class,
- () -> HapticFeedbackCustomization.loadVibrations(mResourcesMock, mVibratorMock));
+ () -> HapticFeedbackCustomization.loadVibrations(
+ mResourcesMock, mVibratorInfoMock));
}
private void setupCustomizationFile(String xml) throws Exception {
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java b/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java
index cae811e..a91bd2b 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java
@@ -31,8 +31,10 @@
import android.content.Context;
import android.content.res.Resources;
+import android.hardware.vibrator.IVibrator;
+import android.os.VibrationAttributes;
import android.os.VibrationEffect;
-import android.os.test.FakeVibrator;
+import android.os.VibratorInfo;
import android.util.AtomicFile;
import android.util.SparseArray;
@@ -58,7 +60,7 @@
VibrationEffect.startComposition().addPrimitive(PRIMITIVE_CLICK, 0.3497f).compose();
private Context mContext = InstrumentationRegistry.getContext();
- private FakeVibrator mVibrator = new FakeVibrator(mContext);
+ private VibratorInfo mVibratorInfo = VibratorInfo.EMPTY_VIBRATOR_INFO;
@Mock private Resources mResourcesMock;
@@ -66,14 +68,14 @@
public void testNonExistentCustomization_useDefault() throws Exception {
// No customization file is set.
HapticFeedbackVibrationProvider hapticProvider =
- new HapticFeedbackVibrationProvider(mResourcesMock, mVibrator);
+ new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo);
assertThat(hapticProvider.getVibrationForHapticFeedback(CONTEXT_CLICK))
.isEqualTo(VibrationEffect.get(EFFECT_TICK));
// The customization file specifies no customization.
setupCustomizationFile("<haptic-feedback-constants></haptic-feedback-constants>");
- hapticProvider = new HapticFeedbackVibrationProvider(mResourcesMock, mVibrator);
+ hapticProvider = new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo);
assertThat(hapticProvider.getVibrationForHapticFeedback(CONTEXT_CLICK))
.isEqualTo(VibrationEffect.get(EFFECT_TICK));
@@ -83,7 +85,7 @@
public void testExceptionParsingCustomizations_useDefault() throws Exception {
setupCustomizationFile("<bad-xml></bad-xml>");
HapticFeedbackVibrationProvider hapticProvider =
- new HapticFeedbackVibrationProvider(mResourcesMock, mVibrator);
+ new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo);
assertThat(hapticProvider.getVibrationForHapticFeedback(CONTEXT_CLICK))
.isEqualTo(VibrationEffect.get(EFFECT_TICK));
@@ -96,7 +98,7 @@
customizations.put(CONTEXT_CLICK, PRIMITIVE_CLICK_EFFECT);
HapticFeedbackVibrationProvider hapticProvider =
- new HapticFeedbackVibrationProvider(mResourcesMock, mVibrator, customizations);
+ new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo, customizations);
// The override for `CONTEXT_CLICK` is used.
assertThat(hapticProvider.getVibrationForHapticFeedback(CONTEXT_CLICK))
@@ -109,11 +111,15 @@
@Test
public void testDoNotUseInvalidCustomizedVibration() throws Exception {
mockVibratorPrimitiveSupport(new int[] {});
- SparseArray<VibrationEffect> customizations = new SparseArray<>();
- customizations.put(CONTEXT_CLICK, PRIMITIVE_CLICK_EFFECT);
+ String xml = "<haptic-feedback-constants>"
+ + "<constant id=\"" + CONTEXT_CLICK + "\">"
+ + PRIMITIVE_CLICK_EFFECT
+ + "</constant>"
+ + "</haptic-feedback-constants>";
+ setupCustomizationFile(xml);
HapticFeedbackVibrationProvider hapticProvider =
- new HapticFeedbackVibrationProvider(mResourcesMock, mVibrator, customizations);
+ new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo);
// The override for `CONTEXT_CLICK` is not used because the vibration is not supported.
assertThat(hapticProvider.getVibrationForHapticFeedback(CONTEXT_CLICK))
@@ -132,14 +138,14 @@
// Test with a customization available for `TEXT_HANDLE_MOVE`.
HapticFeedbackVibrationProvider hapticProvider =
- new HapticFeedbackVibrationProvider(mResourcesMock, mVibrator, customizations);
+ new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo, customizations);
assertThat(hapticProvider.getVibrationForHapticFeedback(TEXT_HANDLE_MOVE)).isNull();
// Test with no customization available for `TEXT_HANDLE_MOVE`.
hapticProvider =
new HapticFeedbackVibrationProvider(
- mResourcesMock, mVibrator, /* hapticCustomizations= */ null);
+ mResourcesMock, mVibratorInfo, /* hapticCustomizations= */ null);
assertThat(hapticProvider.getVibrationForHapticFeedback(TEXT_HANDLE_MOVE)).isNull();
}
@@ -153,7 +159,7 @@
// Test with a customization available for `TEXT_HANDLE_MOVE`.
HapticFeedbackVibrationProvider hapticProvider =
- new HapticFeedbackVibrationProvider(mResourcesMock, mVibrator, customizations);
+ new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo, customizations);
assertThat(hapticProvider.getVibrationForHapticFeedback(TEXT_HANDLE_MOVE))
.isEqualTo(PRIMITIVE_CLICK_EFFECT);
@@ -161,7 +167,7 @@
// Test with no customization available for `TEXT_HANDLE_MOVE`.
hapticProvider =
new HapticFeedbackVibrationProvider(
- mResourcesMock, mVibrator, /* hapticCustomizations= */ null);
+ mResourcesMock, mVibratorInfo, /* hapticCustomizations= */ null);
assertThat(hapticProvider.getVibrationForHapticFeedback(TEXT_HANDLE_MOVE))
.isEqualTo(VibrationEffect.get(EFFECT_TEXTURE_TICK));
@@ -176,14 +182,14 @@
customizations.put(SAFE_MODE_ENABLED, PRIMITIVE_CLICK_EFFECT);
HapticFeedbackVibrationProvider hapticProvider =
- new HapticFeedbackVibrationProvider(mResourcesMock, mVibrator, customizations);
+ new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo, customizations);
assertThat(hapticProvider.getVibrationForHapticFeedback(SAFE_MODE_ENABLED))
.isEqualTo(PRIMITIVE_CLICK_EFFECT);
mockSafeModeEnabledVibration(null);
hapticProvider =
- new HapticFeedbackVibrationProvider(mResourcesMock, mVibrator, customizations);
+ new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo, customizations);
assertThat(hapticProvider.getVibrationForHapticFeedback(SAFE_MODE_ENABLED))
.isEqualTo(PRIMITIVE_CLICK_EFFECT);
@@ -193,20 +199,9 @@
public void testNoValidCustomizationPresentForSafeModeEnabled_resourceBasedVibrationUsed()
throws Exception {
mockSafeModeEnabledVibration(10, 20, 30, 40);
- SparseArray<VibrationEffect> customizations = new SparseArray<>();
- customizations.put(SAFE_MODE_ENABLED, PRIMITIVE_CLICK_EFFECT);
-
- // Test with a customization that is not supported by the vibrator.
HapticFeedbackVibrationProvider hapticProvider =
- new HapticFeedbackVibrationProvider(mResourcesMock, mVibrator, customizations);
-
- assertThat(hapticProvider.getVibrationForHapticFeedback(SAFE_MODE_ENABLED))
- .isEqualTo(VibrationEffect.createWaveform(new long[] {10, 20, 30, 40}, -1));
-
- // Test with no customizations.
- hapticProvider =
new HapticFeedbackVibrationProvider(
- mResourcesMock, mVibrator, /* hapticCustomizations= */ null);
+ mResourcesMock, mVibratorInfo, /* hapticCustomizations= */ null);
assertThat(hapticProvider.getVibrationForHapticFeedback(SAFE_MODE_ENABLED))
.isEqualTo(VibrationEffect.createWaveform(new long[] {10, 20, 30, 40}, -1));
@@ -216,25 +211,44 @@
public void testNoValidCustomizationAndResourcePresentForSafeModeEnabled_noVibrationUsed()
throws Exception {
mockSafeModeEnabledVibration(null);
- SparseArray<VibrationEffect> customizations = new SparseArray<>();
- customizations.put(SAFE_MODE_ENABLED, PRIMITIVE_CLICK_EFFECT);
-
- // Test with a customization that is not supported by the vibrator.
HapticFeedbackVibrationProvider hapticProvider =
- new HapticFeedbackVibrationProvider(mResourcesMock, mVibrator, customizations);
-
- assertThat(hapticProvider.getVibrationForHapticFeedback(SAFE_MODE_ENABLED)).isNull();
-
- // Test with no customizations.
- hapticProvider =
new HapticFeedbackVibrationProvider(
- mResourcesMock, mVibrator, /* hapticCustomizations= */ null);
+ mResourcesMock, mVibratorInfo, /* hapticCustomizations= */ null);
assertThat(hapticProvider.getVibrationForHapticFeedback(SAFE_MODE_ENABLED)).isNull();
}
+ @Test
+ public void testVibrationAttribute_forNotBypassingIntensitySettings() {
+ HapticFeedbackVibrationProvider hapticProvider =
+ new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo);
+
+ VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback(
+ SAFE_MODE_ENABLED, /* bypassVibrationIntensitySetting= */ false);
+
+ assertThat(attrs.getFlags() & VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)
+ .isEqualTo(0);
+ }
+
+ @Test
+ public void testVibrationAttribute_forByassingIntensitySettings() {
+ HapticFeedbackVibrationProvider hapticProvider =
+ new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo);
+
+ VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback(
+ SAFE_MODE_ENABLED, /* bypassVibrationIntensitySetting= */ true);
+
+ assertThat(attrs.getFlags() & VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)
+ .isNotEqualTo(0);
+ }
+
private void mockVibratorPrimitiveSupport(int... supportedPrimitives) {
- mVibrator = new FakeVibrator(mContext, supportedPrimitives);
+ VibratorInfo.Builder builder = new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+ for (int primitive : supportedPrimitives) {
+ builder.setSupportedPrimitive(primitive, 10);
+ }
+ mVibratorInfo = builder.build();
}
private void mockHapticTextSupport(boolean supported) {
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index 4e3a893..c25f0cb 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -22,6 +22,7 @@
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -30,6 +31,7 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -45,6 +47,7 @@
import android.content.Context;
import android.content.ContextWrapper;
import android.content.pm.PackageManagerInternal;
+import android.content.res.Resources;
import android.hardware.input.IInputManager;
import android.hardware.input.InputManager;
import android.hardware.input.InputManagerGlobal;
@@ -79,8 +82,10 @@
import android.os.vibrator.VibrationEffectSegment;
import android.provider.Settings;
import android.util.ArraySet;
+import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.view.Display;
+import android.view.HapticFeedbackConstants;
import android.view.InputDevice;
import androidx.test.InstrumentationRegistry;
@@ -169,6 +174,8 @@
private final Map<Integer, FakeVibratorControllerProvider> mVibratorProviders = new HashMap<>();
+ private SparseArray<VibrationEffect> mHapticFeedbackVibrationMap = new SparseArray<>();
+
private VibratorManagerService mService;
private Context mContextSpy;
private TestLooper mTestLooper;
@@ -309,6 +316,12 @@
mExternalVibratorService =
(VibratorManagerService.ExternalVibratorService) serviceInstance;
}
+
+ HapticFeedbackVibrationProvider createHapticFeedbackVibrationProvider(
+ Resources resources, VibratorInfo vibratorInfo) {
+ return new HapticFeedbackVibrationProvider(
+ resources, vibratorInfo, mHapticFeedbackVibrationMap);
+ }
});
return mService;
}
@@ -623,6 +636,18 @@
}
@Test
+ public void vibrate_withoutVibratePermission_throwsSecurityException() {
+ denyPermission(android.Manifest.permission.VIBRATE);
+ VibratorManagerService service = createSystemReadyService();
+
+ assertThrows("Expected vibrating without permission to fail!",
+ SecurityException.class,
+ () -> vibrate(service,
+ VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK),
+ VibrationAttributes.createForUsage(VibrationAttributes.USAGE_TOUCH)));
+ }
+
+ @Test
public void vibrate_withRingtone_usesRingerModeSettings() throws Exception {
mockVibrators(1);
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
@@ -1274,6 +1299,60 @@
}
@Test
+ public void performHapticFeedback_doesNotRequirePermission() throws Exception {
+ denyPermission(android.Manifest.permission.VIBRATE);
+ mHapticFeedbackVibrationMap.put(
+ HapticFeedbackConstants.KEYBOARD_TAP,
+ VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK));
+ mockVibrators(1);
+ FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
+ fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK);
+ VibratorManagerService service = createSystemReadyService();
+
+ HalVibration vibration =
+ performHapticFeedbackAndWaitUntilFinished(
+ service, HapticFeedbackConstants.KEYBOARD_TAP, /* always= */ true);
+
+ List<VibrationEffectSegment> playedSegments = fakeVibrator.getAllEffectSegments();
+ assertEquals(1, playedSegments.size());
+ PrebakedSegment segment = (PrebakedSegment) playedSegments.get(0);
+ assertEquals(VibrationEffect.EFFECT_CLICK, segment.getEffectId());
+ assertEquals(VibrationAttributes.USAGE_TOUCH, vibration.callerInfo.attrs.getUsage());
+ }
+
+ @Test
+ public void performHapticFeedback_doesNotVibrateWhenVibratorInfoNotReady() throws Exception {
+ denyPermission(android.Manifest.permission.VIBRATE);
+ mHapticFeedbackVibrationMap.put(
+ HapticFeedbackConstants.KEYBOARD_TAP,
+ VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK));
+ mockVibrators(1);
+ FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
+ fakeVibrator.setVibratorInfoLoadSuccessful(false);
+ fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK);
+ VibratorManagerService service = createService();
+
+ performHapticFeedbackAndWaitUntilFinished(
+ service, HapticFeedbackConstants.KEYBOARD_TAP, /* always= */ true);
+
+ assertTrue(fakeVibrator.getAllEffectSegments().isEmpty());
+ }
+
+ @Test
+ public void performHapticFeedback_doesNotVibrateForInvalidConstant() throws Exception {
+ denyPermission(android.Manifest.permission.VIBRATE);
+ mockVibrators(1);
+ VibratorManagerService service = createSystemReadyService();
+
+ // These are bad haptic feedback IDs, so expect no vibration played.
+ performHapticFeedbackAndWaitUntilFinished(service, /* constant= */ -1, /* always= */ false);
+ performHapticFeedbackAndWaitUntilFinished(
+ service, HapticFeedbackConstants.NO_HAPTICS, /* always= */ true);
+
+ assertTrue(mVibratorProviders.get(1).getAllEffectSegments().isEmpty());
+ }
+
+ @Test
public void vibrate_withIntensitySettings_appliesSettingsToScaleVibrations() throws Exception {
int defaultNotificationIntensity =
mVibrator.getDefaultVibrationIntensity(VibrationAttributes.USAGE_NOTIFICATION);
@@ -2231,6 +2310,18 @@
mContextSpy.getContentResolver(), settingName, value, UserHandle.USER_CURRENT);
}
+ private HalVibration performHapticFeedbackAndWaitUntilFinished(VibratorManagerService service,
+ int constant, boolean always) throws InterruptedException {
+ HalVibration vib =
+ service.performHapticFeedbackInternal(UID, Display.DEFAULT_DISPLAY, PACKAGE_NAME,
+ constant, always, "some reason", service);
+ if (vib != null) {
+ vib.waitForEnd();
+ }
+
+ return vib;
+ }
+
private void vibrateAndWaitUntilFinished(VibratorManagerService service, VibrationEffect effect,
VibrationAttributes attrs) throws InterruptedException {
vibrateAndWaitUntilFinished(service, CombinedVibration.createParallel(effect), attrs);
@@ -2239,8 +2330,8 @@
private void vibrateAndWaitUntilFinished(VibratorManagerService service,
CombinedVibration effect, VibrationAttributes attrs) throws InterruptedException {
HalVibration vib =
- service.vibrateInternal(UID, Display.DEFAULT_DISPLAY, PACKAGE_NAME, effect, attrs,
- "some reason", service);
+ service.vibrateWithPermissionCheck(UID, Display.DEFAULT_DISPLAY, PACKAGE_NAME,
+ effect, attrs, "some reason", service);
if (vib != null) {
vib.waitForEnd();
}
@@ -2271,4 +2362,9 @@
}
return predicateResult;
}
+
+ private void denyPermission(String permission) {
+ doThrow(new SecurityException()).when(mContextSpy)
+ .enforceCallingOrSelfPermission(eq(permission), anyString());
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java b/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java
index 8fadecd..e13dc3e 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java
@@ -65,6 +65,12 @@
KeyboardLogEvent.RECENT_APPS, KeyEvent.KEYCODE_TAB, ALT_ON},
{"BACK key -> Go back", new int[]{KeyEvent.KEYCODE_BACK}, KeyboardLogEvent.BACK,
KeyEvent.KEYCODE_BACK, 0},
+ {"Meta + `(grave) -> Go back", new int[]{META_KEY, KeyEvent.KEYCODE_GRAVE},
+ KeyboardLogEvent.BACK, KeyEvent.KEYCODE_GRAVE, META_ON},
+ {"Meta + Left arrow -> Go back", new int[]{META_KEY, KeyEvent.KEYCODE_DPAD_LEFT},
+ KeyboardLogEvent.BACK, KeyEvent.KEYCODE_DPAD_LEFT, META_ON},
+ {"Meta + Del -> Go back", new int[]{META_KEY, KeyEvent.KEYCODE_DEL},
+ KeyboardLogEvent.BACK, KeyEvent.KEYCODE_DEL, META_ON},
{"APP_SWITCH key -> Open App switcher", new int[]{KeyEvent.KEYCODE_APP_SWITCH},
KeyboardLogEvent.APP_SWITCH, KeyEvent.KEYCODE_APP_SWITCH, 0},
{"ASSIST key -> Launch assistant", new int[]{KeyEvent.KEYCODE_ASSIST},
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
index dd90e04..bf86563 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.inputmethodservice.InputMethodService.ENABLE_HIDE_IME_CAPTION_BAR;
import static android.view.DisplayCutout.NO_CUTOUT;
import static android.view.InsetsSource.ID_IME;
import static android.view.RoundedCorners.NO_ROUNDED_CORNERS;
@@ -427,11 +428,11 @@
@SetupWindows(addWindows = { W_NAVIGATION_BAR, W_INPUT_METHOD })
@Test
public void testImeMinimalSourceFrame() {
+ Assume.assumeFalse("Behavior no longer needed with ENABLE_HIDE_IME_CAPTION_BAR",
+ ENABLE_HIDE_IME_CAPTION_BAR);
+
final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy();
- final DisplayInfo displayInfo = new DisplayInfo();
- displayInfo.logicalWidth = 1000;
- displayInfo.logicalHeight = 2000;
- displayInfo.rotation = ROTATION_0;
+ final DisplayInfo displayInfo = mDisplayContent.getDisplayInfo();
WindowManager.LayoutParams attrs = mNavBarWindow.mAttrs;
displayPolicy.addWindowLw(mNavBarWindow, attrs);
@@ -466,10 +467,6 @@
@Test
public void testImeInsetsGivenContentFrame() {
final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy();
- final DisplayInfo displayInfo = new DisplayInfo();
- displayInfo.logicalWidth = 1000;
- displayInfo.logicalHeight = 2000;
- displayInfo.rotation = ROTATION_0;
mDisplayContent.setInputMethodWindowLocked(mImeWindow);
mImeWindow.getControllableInsetProvider().setServerVisible(true);
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationPersisterTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationPersisterTest.java
index 06033c7..3fcec96 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationPersisterTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationPersisterTest.java
@@ -184,7 +184,7 @@
LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT);
firstPersister.setLetterboxPositionForVerticalReachability(false,
LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP);
- waitForCompletion(mPersisterQueue);
+ waitForCompletion(firstPersisterQueue);
final int newPositionForHorizontalReachability =
firstPersister.getLetterboxPositionForHorizontalReachability(false);
final int newPositionForVerticalReachability =
diff --git a/tests/ChoreographerTests/src/main/java/android/view/choreographertests/AttachedChoreographerTest.java b/tests/ChoreographerTests/src/main/java/android/view/choreographertests/AttachedChoreographerTest.java
index bb0d30a..5460e4e87 100644
--- a/tests/ChoreographerTests/src/main/java/android/view/choreographertests/AttachedChoreographerTest.java
+++ b/tests/ChoreographerTests/src/main/java/android/view/choreographertests/AttachedChoreographerTest.java
@@ -421,7 +421,7 @@
@Test
public void testChoreographerAttachedAfterSetFrameRate() {
- Log.i(TAG, "adyabr: starting testChoreographerAttachedAfterSetFrameRate");
+ Log.i(TAG, "starting testChoreographerAttachedAfterSetFrameRate");
class TransactionGenerator implements SurfaceControl.TransactionCommittedListener {
private SurfaceControl mSc;
diff --git a/tests/FlickerTests/Android.bp b/tests/FlickerTests/Android.bp
index 2ccc0fa..a2ae56e 100644
--- a/tests/FlickerTests/Android.bp
+++ b/tests/FlickerTests/Android.bp
@@ -95,6 +95,7 @@
"flickertestapplib",
"flickerlib",
"flickerlib-helpers",
+ "flickerlib-trace_processor_shell",
"platform-test-annotations",
"wm-flicker-common-app-helpers",
"wm-shell-flicker-utils",
diff --git a/tests/FlickerTests/manifests/AndroidManifest.xml b/tests/FlickerTests/manifests/AndroidManifest.xml
index 1a34d9e..6bc7cbe 100644
--- a/tests/FlickerTests/manifests/AndroidManifest.xml
+++ b/tests/FlickerTests/manifests/AndroidManifest.xml
@@ -44,8 +44,12 @@
<uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS" />
<!-- ActivityOptions.makeCustomTaskAnimation() -->
<uses-permission android:name="android.permission.START_TASKS_FROM_RECENTS" />
- <!-- Allow the test to write directly to /sdcard/ -->
- <application android:requestLegacyExternalStorage="true" android:largeHeap="true">
+ <!-- Allow the test to connect to perfetto trace processor -->
+ <uses-permission android:name="android.permission.INTERNET"/>
+ <!-- Allow the test to write directly to /sdcard/ and connect to trace processor -->
+ <application android:requestLegacyExternalStorage="true"
+ android:networkSecurityConfig="@xml/network_security_config"
+ android:largeHeap="true">
<uses-library android:name="android.test.runner"/>
<uses-library android:name="androidx.window.extensions" android:required="false"/>
diff --git a/tests/FlickerTests/res/xml/network_security_config.xml b/tests/FlickerTests/res/xml/network_security_config.xml
new file mode 100644
index 0000000..4bd9ca0
--- /dev/null
+++ b/tests/FlickerTests/res/xml/network_security_config.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<network-security-config>
+ <domain-config cleartextTrafficPermitted="true">
+ <domain includeSubdomains="true">localhost</domain>
+ </domain-config>
+</network-security-config>
diff --git a/tests/SurfaceViewBufferTests/Android.bp b/tests/SurfaceViewBufferTests/Android.bp
index dc75f00..38313f8 100644
--- a/tests/SurfaceViewBufferTests/Android.bp
+++ b/tests/SurfaceViewBufferTests/Android.bp
@@ -23,7 +23,10 @@
android_test {
name: "SurfaceViewBufferTests",
- srcs: ["**/*.java","**/*.kt"],
+ srcs: [
+ "**/*.java",
+ "**/*.kt",
+ ],
manifest: "AndroidManifest.xml",
test_config: "AndroidTest.xml",
platform_apis: true,
@@ -41,6 +44,7 @@
"kotlin-stdlib",
"kotlinx-coroutines-android",
"flickerlib",
+ "flickerlib-trace_processor_shell",
"truth-prebuilt",
"cts-wm-util",
"CtsSurfaceValidatorLib",
@@ -60,7 +64,7 @@
"libandroid",
],
include_dirs: [
- "system/core/include"
+ "system/core/include",
],
stl: "libc++_static",
cflags: [
diff --git a/tests/SurfaceViewBufferTests/AndroidManifest.xml b/tests/SurfaceViewBufferTests/AndroidManifest.xml
index 78415e8..798c67a 100644
--- a/tests/SurfaceViewBufferTests/AndroidManifest.xml
+++ b/tests/SurfaceViewBufferTests/AndroidManifest.xml
@@ -29,9 +29,12 @@
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<!-- Save failed test bitmap images !-->
<uses-permission android:name="android.Manifest.permission.WRITE_EXTERNAL_STORAGE"/>
+ <!-- Allow the test to connect to perfetto trace processor -->
+ <uses-permission android:name="android.permission.INTERNET"/>
<application android:allowBackup="false"
- android:supportsRtl="true">
+ android:supportsRtl="true"
+ android:networkSecurityConfig="@xml/network_security_config">
<activity android:name=".MainActivity"
android:taskAffinity="com.android.test.MainActivity"
android:theme="@style/AppTheme"
diff --git a/tests/SurfaceViewBufferTests/res/xml/network_security_config.xml b/tests/SurfaceViewBufferTests/res/xml/network_security_config.xml
new file mode 100644
index 0000000..4bd9ca0
--- /dev/null
+++ b/tests/SurfaceViewBufferTests/res/xml/network_security_config.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<network-security-config>
+ <domain-config cleartextTrafficPermitted="true">
+ <domain includeSubdomains="true">localhost</domain>
+ </domain-config>
+</network-security-config>
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceTracingTestBase.kt b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceTracingTestBase.kt
index a38019d..b03b733 100644
--- a/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceTracingTestBase.kt
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceTracingTestBase.kt
@@ -21,11 +21,9 @@
import android.graphics.Rect
import android.util.Log
import androidx.test.ext.junit.rules.ActivityScenarioRule
-import android.tools.common.flicker.subject.layers.LayerSubject
import android.tools.common.traces.surfaceflinger.LayersTrace
-import android.tools.device.traces.io.ResultWriter
-import android.tools.device.traces.monitors.surfaceflinger.LayersTraceMonitor
import android.tools.device.traces.monitors.withSFTracing
+import android.tools.device.traces.monitors.PerfettoTraceMonitor
import junit.framework.Assert
import org.junit.After
import org.junit.Before
@@ -33,6 +31,7 @@
import java.io.FileOutputStream
import java.io.IOException
import java.util.concurrent.CountDownLatch
+import perfetto.protos.PerfettoConfig.SurfaceFlingerLayersConfig
open class SurfaceTracingTestBase(useBlastAdapter: Boolean) :
SurfaceViewBufferTestBase(useBlastAdapter) {
@@ -43,7 +42,7 @@
@Before
override fun setup() {
super.setup()
- stopLayerTrace()
+ PerfettoTraceMonitor.stopAllSessions()
addSurfaceView()
}
@@ -83,10 +82,6 @@
instrumentation.waitForIdleSync()
}
- private fun stopLayerTrace() {
- LayersTraceMonitor().stop(ResultWriter())
- }
-
fun checkPixels(bounds: Rect, @ColorInt color: Int) {
val screenshot = instrumentation.getUiAutomation().takeScreenshot()
val pixels = IntArray(screenshot.width * screenshot.height)
@@ -106,14 +101,19 @@
Log.e("SurfaceViewBufferTests", "Error writing bitmap to file", e)
}
}
- Assert.assertEquals("Checking $bounds found mismatch $i,$j",
- Color.valueOf(color), Color.valueOf(actualColor))
+ Assert.assertEquals(
+ "Checking $bounds found mismatch $i,$j",
+ Color.valueOf(color),
+ Color.valueOf(actualColor)
+ )
}
}
}
private companion object {
- private const val TRACE_FLAGS =
- (1 shl 0) or (1 shl 5) or (1 shl 6) // TRACE_CRITICAL | TRACE_BUFFERS | TRACE_SYNC
+ private val TRACE_FLAGS = listOf(
+ SurfaceFlingerLayersConfig.TraceFlag.TRACE_FLAG_BUFFERS,
+ SurfaceFlingerLayersConfig.TraceFlag.TRACE_FLAG_VIRTUAL_DISPLAYS,
+ )
}
}
\ No newline at end of file
diff --git a/tests/TaskOrganizerTest/Android.bp b/tests/TaskOrganizerTest/Android.bp
index 9b72d35..bf12f42 100644
--- a/tests/TaskOrganizerTest/Android.bp
+++ b/tests/TaskOrganizerTest/Android.bp
@@ -25,7 +25,10 @@
android_test {
name: "TaskOrganizerTest",
- srcs: ["**/*.java","**/*.kt"],
+ srcs: [
+ "**/*.java",
+ "**/*.kt",
+ ],
manifest: "AndroidManifest.xml",
test_config: "AndroidTest.xml",
platform_apis: true,
@@ -39,6 +42,7 @@
"kotlin-stdlib",
"kotlinx-coroutines-android",
"flickerlib",
+ "flickerlib-trace_processor_shell",
"truth-prebuilt",
],
-}
\ No newline at end of file
+}
diff --git a/tests/TaskOrganizerTest/AndroidManifest.xml b/tests/TaskOrganizerTest/AndroidManifest.xml
index 1f1bd3e..cbeb246 100644
--- a/tests/TaskOrganizerTest/AndroidManifest.xml
+++ b/tests/TaskOrganizerTest/AndroidManifest.xml
@@ -16,9 +16,11 @@
<uses-permission android:name="android.permission.CHANGE_CONFIGURATION"/>
<uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS"/>
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"/>
+ <!-- Allow the test to connect to perfetto trace processor -->
+ <uses-permission android:name="android.permission.INTERNET"/>
<!-- Enable / Disable tracing !-->
<uses-permission android:name="android.permission.DUMP" />
- <application>
+ <application android:networkSecurityConfig="@xml/network_security_config">
<activity android:name="TaskOrganizerMultiWindowTest"
android:label="TaskOrganizer MW Test"
android:exported="true"
diff --git a/tests/TaskOrganizerTest/res/xml/network_security_config.xml b/tests/TaskOrganizerTest/res/xml/network_security_config.xml
new file mode 100644
index 0000000..e450a99
--- /dev/null
+++ b/tests/TaskOrganizerTest/res/xml/network_security_config.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+~ Copyright (C) 2023 The Android Open Source Project
+~
+~ Licensed under the Apache License, Version 2.0 (the "License");
+~ you may not use this file except in compliance with the License.
+~ You may obtain a copy of the License at
+~
+~ http://www.apache.org/licenses/LICENSE-2.0
+~
+~ Unless required by applicable law or agreed to in writing, software
+~ distributed under the License is distributed on an "AS IS" BASIS,
+~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+~ See the License for the specific language governing permissions and
+~ limitations under the License.
+-->
+<network-security-config>
+ <domain-config cleartextTrafficPermitted="true">
+ <domain includeSubdomains="true">localhost</domain>
+ </domain-config>
+</network-security-config>
\ No newline at end of file
diff --git a/tests/TaskOrganizerTest/src/com/android/test/taskembed/ResizeTasksSyncTest.kt b/tests/TaskOrganizerTest/src/com/android/test/taskembed/ResizeTasksSyncTest.kt
index 6f4f7b1..2c7905d 100644
--- a/tests/TaskOrganizerTest/src/com/android/test/taskembed/ResizeTasksSyncTest.kt
+++ b/tests/TaskOrganizerTest/src/com/android/test/taskembed/ResizeTasksSyncTest.kt
@@ -22,8 +22,6 @@
import androidx.test.runner.AndroidJUnit4
import android.tools.common.datatypes.Size
import android.tools.common.flicker.subject.layers.LayersTraceSubject
-import android.tools.device.traces.io.ResultWriter
-import android.tools.device.traces.monitors.surfaceflinger.LayersTraceMonitor
import android.tools.device.traces.monitors.withSFTracing
import org.junit.After
import org.junit.Before
@@ -41,16 +39,13 @@
@get:Rule
var scenarioRule: ActivityScenarioRule<TaskOrganizerMultiWindowTest> =
ActivityScenarioRule<TaskOrganizerMultiWindowTest>(
- TaskOrganizerMultiWindowTest::class.java)
+ TaskOrganizerMultiWindowTest::class.java
+ )
protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
@Before
fun setup() {
- val monitor = LayersTraceMonitor()
- if (monitor.isEnabled) {
- monitor.stop(ResultWriter())
- }
val firstTaskBounds = Rect(0, 0, 1080, 1000)
val secondTaskBounds = Rect(0, 1000, 1080, 2000)
@@ -71,7 +66,7 @@
val firstBounds = Rect(0, 0, 1080, 800)
val secondBounds = Rect(0, 1000, 1080, 1800)
- val trace = withSFTracing(TRACE_FLAGS) {
+ val trace = withSFTracing() {
lateinit var resizeReadyLatch: CountDownLatch
scenarioRule.getScenario().onActivity {
resizeReadyLatch = it.resizeTaskView(firstBounds, secondBounds)
@@ -106,7 +101,6 @@
}
companion object {
- private const val TRACE_FLAGS = 0x1 // TRACE_CRITICAL
private const val FIRST_ACTIVITY = "Activity1"
private const val SECOND_ACTIVITY = "Activity2"
}