diff options
480 files changed, 7303 insertions, 2797 deletions
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java index c74c48ce6ba8..5f57c3973ab0 100644 --- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java +++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java @@ -31,6 +31,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; +import android.annotation.SuppressLint; import android.app.Notification; import android.compat.Compatibility; import android.compat.annotation.ChangeId; @@ -1366,6 +1367,7 @@ public class JobInfo implements Parcelable { * @return This object for method chaining */ @FlaggedApi(Flags.FLAG_JOB_DEBUG_INFO_APIS) + @SuppressLint("BuilderSetStyle") @NonNull public Builder removeDebugTag(@NonNull String tag) { mDebugTags.remove(tag); diff --git a/api/StubLibraries.bp b/api/StubLibraries.bp index 9b81bd441015..13d6ae5be3e8 100644 --- a/api/StubLibraries.bp +++ b/api/StubLibraries.bp @@ -50,7 +50,7 @@ non_updatable_exportable_droidstubs { }, api_lint: { enabled: true, - new_since: ":android.api.public.latest", + new_since: ":android.api.combined.public.latest", baseline_file: ":non-updatable-lint-baseline.txt", }, }, @@ -130,7 +130,7 @@ non_updatable_exportable_droidstubs { }, api_lint: { enabled: true, - new_since: ":android.api.system.latest", + new_since: ":android.api.combined.system.latest", baseline_file: ":non-updatable-system-lint-baseline.txt", }, }, @@ -185,7 +185,7 @@ non_updatable_exportable_droidstubs { }, api_lint: { enabled: true, - new_since: ":android.api.test.latest", + new_since: ":android.api.combined.test.latest", baseline_file: ":non-updatable-test-lint-baseline.txt", }, }, @@ -269,7 +269,7 @@ non_updatable_exportable_droidstubs { }, api_lint: { enabled: true, - new_since: ":android.api.module-lib.latest", + new_since: ":android.api.combined.module-lib.latest", baseline_file: ":non-updatable-module-lib-lint-baseline.txt", }, }, diff --git a/core/api/current.txt b/core/api/current.txt index 53cf7d59f974..13958d24096b 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -26592,7 +26592,7 @@ package android.media.session { method public long getFlags(); method @Nullable public android.media.MediaMetadata getMetadata(); method public String getPackageName(); - method @Nullable public android.media.session.MediaController.PlaybackInfo getPlaybackInfo(); + method @NonNull public android.media.session.MediaController.PlaybackInfo getPlaybackInfo(); method @Nullable public android.media.session.PlaybackState getPlaybackState(); method @Nullable public java.util.List<android.media.session.MediaSession.QueueItem> getQueue(); method @Nullable public CharSequence getQueueTitle(); diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 624227dc26f3..14ae3f543436 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -4260,6 +4260,12 @@ package android.window { field @NonNull public static final android.os.Parcelable.Creator<android.window.TaskFragmentOrganizerToken> CREATOR; } + public final class TaskFragmentParentInfo implements android.os.Parcelable { + method public int describeContents(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.window.TaskFragmentParentInfo> CREATOR; + } + public final class TaskFragmentTransaction implements android.os.Parcelable { ctor public TaskFragmentTransaction(); method public void addChange(@Nullable android.window.TaskFragmentTransaction.Change); @@ -4284,8 +4290,8 @@ package android.window { method @Nullable public android.os.IBinder getActivityToken(); method @NonNull public android.os.Bundle getErrorBundle(); method @Nullable public android.os.IBinder getErrorCallbackToken(); - method @Nullable public android.content.res.Configuration getTaskConfiguration(); method @Nullable public android.window.TaskFragmentInfo getTaskFragmentInfo(); + method @Nullable public android.window.TaskFragmentParentInfo getTaskFragmentParentInfo(); method @Nullable public android.os.IBinder getTaskFragmentToken(); method public int getTaskId(); method public int getType(); @@ -4293,7 +4299,6 @@ package android.window { method @NonNull public android.window.TaskFragmentTransaction.Change setActivityToken(@NonNull android.os.IBinder); method @NonNull public android.window.TaskFragmentTransaction.Change setErrorBundle(@NonNull android.os.Bundle); method @NonNull public android.window.TaskFragmentTransaction.Change setErrorCallbackToken(@Nullable android.os.IBinder); - method @NonNull public android.window.TaskFragmentTransaction.Change setTaskConfiguration(@NonNull android.content.res.Configuration); method @NonNull public android.window.TaskFragmentTransaction.Change setTaskFragmentInfo(@NonNull android.window.TaskFragmentInfo); method @NonNull public android.window.TaskFragmentTransaction.Change setTaskFragmentToken(@NonNull android.os.IBinder); method @NonNull public android.window.TaskFragmentTransaction.Change setTaskId(int); diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java index 7ee3413550af..497d47adc7cc 100644 --- a/core/java/android/accounts/AccountManager.java +++ b/core/java/android/accounts/AccountManager.java @@ -2015,7 +2015,7 @@ public class AccountManager { * null for no callback * @param handler {@link Handler} identifying the callback thread, * null for the main thread - * @return An {@link AccountManagerFuture} which resolves to a Boolean indicated wether it + * @return An {@link AccountManagerFuture} which resolves to a Boolean indicated whether it * succeeded. * @hide */ diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index a5dd4a7207c3..e1cb630f4d82 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -5796,6 +5796,8 @@ public class Activity extends ContextThemeWrapper * @see #onRequestPermissionsResult */ @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED) + @SuppressLint("OnNameExpected") + // Suppress lint as this is an overload of the original API. public boolean shouldShowRequestPermissionRationale(@NonNull String permission, int deviceId) { final PackageManager packageManager = getDeviceId() == deviceId ? getPackageManager() : createDeviceContext(deviceId).getPackageManager(); diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index d8df447982a0..8e99e46be6ac 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -1282,4 +1282,15 @@ public abstract class ActivityManagerInternal { */ public abstract void addStartInfoTimestamp(int key, long timestampNs, int uid, int pid, int userId); + + /** + * It is similar {@link IActivityManager#killApplication(String, int, int, String, int)} but + * it immediately stop the package. + * + * <p>Note: Do not call this method from inside PMS's lock, otherwise it'll run into + * watchdog reset. + * @hide + */ + public abstract void killApplicationSync(String pkgName, int appId, int userId, + String reason, int exitInfoReason); } diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 54f69099e081..6865f9c5013b 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -3583,6 +3583,9 @@ public class AppOpsManager { return mAttributionTag; } + /** + * Persistent device Id of the proxy that noted the op + */ @FlaggedApi(Flags.FLAG_DEVICE_ID_IN_OP_PROXY_INFO_ENABLED) public @Nullable String getDeviceId() { return mDeviceId; } diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 8b19664c6abf..0672064bd5ea 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -6492,8 +6492,13 @@ public class Notification implements Parcelable // visual regressions. @SuppressWarnings("AndroidFrameworkCompatChange") private boolean bigContentViewRequired() { - if (!Flags.notificationExpansionOptional() - && mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.S) { + if (Flags.notificationExpansionOptional()) { + // Notifications without a bigContentView, style, or actions do not need to expand + boolean exempt = mN.bigContentView == null + && mStyle == null && mActions.size() == 0; + return !exempt; + } + if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.S) { return true; } // Notifications with contentView and without a bigContentView, style, or actions would diff --git a/core/java/android/app/TEST_MAPPING b/core/java/android/app/TEST_MAPPING index dc44764dc046..0deb842aff61 100644 --- a/core/java/android/app/TEST_MAPPING +++ b/core/java/android/app/TEST_MAPPING @@ -378,6 +378,14 @@ } ], "file_patterns": ["(/|^)ContextImpl.java"] + }, + { + "file_patterns": [ + "(/|^)Activity.*.java", + "(/|^)PendingIntent.java", + "(/|^)ComtextImpl.java" + ], + "name": "CtsWindowManagerBackgroundActivityTestCases" } ], "postsubmit": [ @@ -392,14 +400,6 @@ { "file_patterns": ["(/|^)AppOpsManager.java"], "name": "CtsAppOpsTestCases" - }, - { - "file_patterns": [ - "(/|^)Activity.*.java", - "(/|^)PendingIntent.java", - "(/|^)ComtextImpl.java" - ], - "name": "CtsWindowManagerBackgroundActivityTestCases" } ] } diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 69f29f3ab081..c529f7d95190 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -1698,7 +1698,7 @@ public class DevicePolicyManager { /** * A boolean extra indicating whether device encryption can be skipped as part of - * <a href="#managed-provisioning>provisioning</a>. + * <a href="#managed-provisioning">provisioning</a>. * * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} or an intent with action * {@link #ACTION_PROVISION_MANAGED_DEVICE} that starts device owner provisioning. @@ -10427,7 +10427,7 @@ public class DevicePolicyManager { @WorkerThread public void setApplicationRestrictions(@Nullable ComponentName admin, String packageName, Bundle settings) { - if (!Flags.dmrhCanSetAppRestriction()) { + if (!Flags.dmrhSetAppRestrictions()) { throwIfParentInstance("setApplicationRestrictions"); } @@ -11835,7 +11835,7 @@ public class DevicePolicyManager { @WorkerThread public @NonNull Bundle getApplicationRestrictions( @Nullable ComponentName admin, String packageName) { - if (!Flags.dmrhCanSetAppRestriction()) { + if (!Flags.dmrhSetAppRestrictions()) { throwIfParentInstance("getApplicationRestrictions"); } @@ -14120,7 +14120,7 @@ public class DevicePolicyManager { public @NonNull DevicePolicyManager getParentProfileInstance(@NonNull ComponentName admin) { throwIfParentInstance("getParentProfileInstance"); try { - if (Flags.dmrhCanSetAppRestriction()) { + if (Flags.dmrhSetAppRestrictions()) { UserManager um = mContext.getSystemService(UserManager.class); if (!um.isManagedProfile()) { throw new SecurityException("The current user does not have a parent profile."); diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig index 3d6ec19299cb..4154e667360b 100644 --- a/core/java/android/app/admin/flags/flags.aconfig +++ b/core/java/android/app/admin/flags/flags.aconfig @@ -244,10 +244,13 @@ flag { } flag { - name: "dmrh_can_set_app_restriction" + name: "dmrh_set_app_restrictions" namespace: "enterprise" description: "Allow DMRH to set application restrictions (both on the profile and the parent)" bug: "328758346" + metadata { + purpose: PURPOSE_BUGFIX + } } flag { diff --git a/core/java/android/app/prediction/AppPredictionContext.java b/core/java/android/app/prediction/AppPredictionContext.java index 99fa869cee93..1b718d436d6d 100644 --- a/core/java/android/app/prediction/AppPredictionContext.java +++ b/core/java/android/app/prediction/AppPredictionContext.java @@ -24,6 +24,8 @@ import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; +import java.util.Objects; + /** * Class that provides contextual information about the environment in which the app prediction is * used, such as package name, UI in which the app targets are shown, and number of targets. @@ -99,6 +101,13 @@ public final class AppPredictionContext implements Parcelable { } @Override + public int hashCode() { + int hashCode = Objects.hash(mUiSurface, mPackageName); + hashCode = 31 * hashCode + mPredictedTargetCount; + return hashCode; + } + + @Override public int describeContents() { return 0; } diff --git a/core/java/android/app/prediction/AppTarget.java b/core/java/android/app/prediction/AppTarget.java index fef9e7020097..25c1a594ad81 100644 --- a/core/java/android/app/prediction/AppTarget.java +++ b/core/java/android/app/prediction/AppTarget.java @@ -167,6 +167,16 @@ public final class AppTarget implements Parcelable { } @Override + public int hashCode() { + int hashCode = Objects.hash(mId, mPackageName, mClassName, mUser); + if (mShortcutInfo != null) { + hashCode = 31 * hashCode + mShortcutInfo.getId().hashCode(); + } + hashCode = 31 * hashCode + mRank; + return hashCode; + } + + @Override public int describeContents() { return 0; } diff --git a/core/java/android/app/prediction/AppTargetEvent.java b/core/java/android/app/prediction/AppTargetEvent.java index 91da8ec71dae..e36d87899b62 100644 --- a/core/java/android/app/prediction/AppTargetEvent.java +++ b/core/java/android/app/prediction/AppTargetEvent.java @@ -24,6 +24,7 @@ import android.os.Parcelable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Objects; /** * A representation of an app target event. @@ -116,6 +117,13 @@ public final class AppTargetEvent implements Parcelable { } @Override + public int hashCode() { + int hashCode = Objects.hash(mTarget, mLocation); + hashCode = 31 * hashCode + mAction; + return hashCode; + } + + @Override public int describeContents() { return 0; } diff --git a/core/java/android/app/prediction/OWNERS b/core/java/android/app/prediction/OWNERS index fe012da8e307..73168fb90dbc 100644 --- a/core/java/android/app/prediction/OWNERS +++ b/core/java/android/app/prediction/OWNERS @@ -1,2 +1,4 @@ +pinyaoting@google.com +hyunyoungs@google.com adamcohen@google.com sunnygoyal@google.com diff --git a/core/java/android/app/servertransaction/ClientTransactionListenerController.java b/core/java/android/app/servertransaction/ClientTransactionListenerController.java index cda286742d28..9b53461568ca 100644 --- a/core/java/android/app/servertransaction/ClientTransactionListenerController.java +++ b/core/java/android/app/servertransaction/ClientTransactionListenerController.java @@ -33,11 +33,13 @@ import android.hardware.display.DisplayManagerGlobal; import android.os.IBinder; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.Log; import android.window.ActivityWindowInfo; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import java.util.concurrent.RejectedExecutionException; import java.util.function.BiConsumer; /** @@ -47,6 +49,8 @@ import java.util.function.BiConsumer; */ public class ClientTransactionListenerController { + private static final String TAG = "ClientTransactionListenerController"; + private static ClientTransactionListenerController sController; private final Object mLock = new Object(); @@ -179,10 +183,14 @@ public class ClientTransactionListenerController { } // Dispatch the display changed callbacks. - final int displayCount = configUpdatedDisplayIds.size(); - for (int i = 0; i < displayCount; i++) { - final int displayId = configUpdatedDisplayIds.valueAt(i); - onDisplayChanged(displayId); + try { + final int displayCount = configUpdatedDisplayIds.size(); + for (int i = 0; i < displayCount; i++) { + final int displayId = configUpdatedDisplayIds.valueAt(i); + onDisplayChanged(displayId); + } + } catch (RejectedExecutionException e) { + Log.w(TAG, "Failed to notify DisplayListener because the Handler is shutting down"); } } @@ -222,7 +230,11 @@ public class ClientTransactionListenerController { } if (changedDisplayId != INVALID_DISPLAY) { - onDisplayChanged(changedDisplayId); + try { + onDisplayChanged(changedDisplayId); + } catch (RejectedExecutionException e) { + Log.w(TAG, "Failed to notify DisplayListener because the Handler is shutting down"); + } } } @@ -235,9 +247,11 @@ public class ClientTransactionListenerController { /** * Called when receives a {@link Configuration} changed event that is updating display-related * window configuration. + * + * @throws RejectedExecutionException if the display listener handler is closing. */ @VisibleForTesting - public void onDisplayChanged(int displayId) { + public void onDisplayChanged(int displayId) throws RejectedExecutionException { mDisplayManager.handleDisplayChangeFromWindowManager(displayId); } } diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 9e316a2d3560..c8cae822570e 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -4353,13 +4353,6 @@ public class Intent implements Parcelable, Cloneable { "android.intent.extra.BRIGHTNESS_DIALOG_IS_FULL_WIDTH"; /** - * Activity Action: Shows the contrast setting dialog. - * @hide - */ - public static final String ACTION_SHOW_CONTRAST_DIALOG = - "com.android.intent.action.SHOW_CONTRAST_DIALOG"; - - /** * Broadcast Action: A global button was pressed. Includes a single * extra field, {@link #EXTRA_KEY_EVENT}, containing the key event that * caused the broadcast. diff --git a/core/java/android/content/TEST_MAPPING b/core/java/android/content/TEST_MAPPING index f08395ae5e40..41a4288eae5c 100644 --- a/core/java/android/content/TEST_MAPPING +++ b/core/java/android/content/TEST_MAPPING @@ -56,6 +56,10 @@ } ], "file_patterns": ["(/|^)Context.java", "(/|^)ContextWrapper.java"] + }, + { + "name": "CtsWindowManagerBackgroundActivityTestCases", + "file_patterns": ["(/|^)IntentSender.java"] } ], "ravenwood-presubmit": [ @@ -65,9 +69,5 @@ } ], "postsubmit": [ - { - "name": "CtsWindowManagerBackgroundActivityTestCases", - "file_patterns": ["(/|^)IntentSender.java"] - } ] } diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 83285e0b8e8b..c506c9741635 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -270,11 +270,20 @@ public abstract class PackageManager { /** * Application level {@link android.content.pm.PackageManager.Property PackageManager * .Property} for a app to inform the installer that a file containing the app's android - * safety label data is bundled into the APK at the given path. + * safety label data is bundled into the APK as a raw resource. + * + * <p>For example: + * <pre> + * <application> + * <property + * android:name="android.content.PROPERTY_ANDROID_SAFETY_LABEL" + * android:resource="@raw/app-metadata"/> + * </application> + * </pre> * @hide */ - public static final String PROPERTY_ANDROID_SAFETY_LABEL_PATH = - "android.content.SAFETY_LABEL_PATH"; + public static final String PROPERTY_ANDROID_SAFETY_LABEL = + "android.content.PROPERTY_ANDROID_SAFETY_LABEL"; /** * A property value set within the manifest. diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig index 83742eb7ae84..e2a131c0d527 100644 --- a/core/java/android/content/pm/multiuser.aconfig +++ b/core/java/android/content/pm/multiuser.aconfig @@ -247,3 +247,13 @@ flag { description: "Allow MAIN user to access blocked number provider" bug: "338579331" } + +flag { + name: "restrict_quiet_mode_credential_bug_fix_to_managed_profiles" + namespace: "profile_experiences" + description: "Use user states to check the state of quiet mode for managed profiles only" + bug: "332812630" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java index d683d72f17be..1eb466cb10a3 100644 --- a/core/java/android/database/sqlite/SQLiteDatabase.java +++ b/core/java/android/database/sqlite/SQLiteDatabase.java @@ -731,6 +731,7 @@ public final class SQLiteDatabase extends SQLiteClosable { * commits, or is rolled back, either explicitly or by a call to * {@link #yieldIfContendedSafely}. */ + // TODO(340874899) Provide an Executor overload public void beginTransactionWithListener( @Nullable SQLiteTransactionListener transactionListener) { beginTransaction(transactionListener, true); @@ -760,6 +761,7 @@ public final class SQLiteDatabase extends SQLiteClosable { * transaction begins, commits, or is rolled back, either * explicitly or by a call to {@link #yieldIfContendedSafely}. */ + // TODO(340874899) Provide an Executor overload public void beginTransactionWithListenerNonExclusive( @Nullable SQLiteTransactionListener transactionListener) { beginTransaction(transactionListener, false); @@ -785,6 +787,8 @@ public final class SQLiteDatabase extends SQLiteClosable { * } * </pre> */ + // TODO(340874899) Provide an Executor overload + @SuppressLint("ExecutorRegistration") @FlaggedApi(Flags.FLAG_SQLITE_APIS_35) public void beginTransactionWithListenerReadOnly( @Nullable SQLiteTransactionListener transactionListener) { diff --git a/core/java/android/hardware/usb/DeviceFilter.java b/core/java/android/hardware/usb/DeviceFilter.java index 66b0a426f35d..3a271b44eef2 100644 --- a/core/java/android/hardware/usb/DeviceFilter.java +++ b/core/java/android/hardware/usb/DeviceFilter.java @@ -18,6 +18,7 @@ package android.hardware.usb; import android.annotation.NonNull; import android.annotation.Nullable; +import android.hardware.usb.flags.Flags; import android.service.usb.UsbDeviceFilterProto; import android.util.Slog; @@ -57,9 +58,12 @@ public class DeviceFilter { public final String mProductName; // USB device serial number string (or null for unspecified) public final String mSerialNumber; + // USB interface name (or null for unspecified). This will be used when matching devices using + // the available interfaces. + public final String mInterfaceName; public DeviceFilter(int vid, int pid, int clasz, int subclass, int protocol, - String manufacturer, String product, String serialnum) { + String manufacturer, String product, String serialnum, String interfaceName) { mVendorId = vid; mProductId = pid; mClass = clasz; @@ -68,6 +72,7 @@ public class DeviceFilter { mManufacturerName = manufacturer; mProductName = product; mSerialNumber = serialnum; + mInterfaceName = interfaceName; } public DeviceFilter(UsbDevice device) { @@ -79,6 +84,7 @@ public class DeviceFilter { mManufacturerName = device.getManufacturerName(); mProductName = device.getProductName(); mSerialNumber = device.getSerialNumber(); + mInterfaceName = null; } public DeviceFilter(@NonNull DeviceFilter filter) { @@ -90,6 +96,7 @@ public class DeviceFilter { mManufacturerName = filter.mManufacturerName; mProductName = filter.mProductName; mSerialNumber = filter.mSerialNumber; + mInterfaceName = filter.mInterfaceName; } public static DeviceFilter read(XmlPullParser parser) @@ -102,7 +109,7 @@ public class DeviceFilter { String manufacturerName = null; String productName = null; String serialNumber = null; - + String interfaceName = null; int count = parser.getAttributeCount(); for (int i = 0; i < count; i++) { String name = parser.getAttributeName(i); @@ -114,6 +121,8 @@ public class DeviceFilter { productName = value; } else if ("serial-number".equals(name)) { serialNumber = value; + } else if ("interface-name".equals(name)) { + interfaceName = value; } else { int intValue; int radix = 10; @@ -144,7 +153,7 @@ public class DeviceFilter { } return new DeviceFilter(vendorId, productId, deviceClass, deviceSubclass, deviceProtocol, - manufacturerName, productName, serialNumber); + manufacturerName, productName, serialNumber, interfaceName); } public void write(XmlSerializer serializer) throws IOException { @@ -173,13 +182,25 @@ public class DeviceFilter { if (mSerialNumber != null) { serializer.attribute(null, "serial-number", mSerialNumber); } + if (mInterfaceName != null) { + serializer.attribute(null, "interface-name", mInterfaceName); + } serializer.endTag(null, "usb-device"); } - private boolean matches(int clasz, int subclass, int protocol) { - return ((mClass == -1 || clasz == mClass) && - (mSubclass == -1 || subclass == mSubclass) && - (mProtocol == -1 || protocol == mProtocol)); + private boolean matches(int usbClass, int subclass, int protocol) { + return ((mClass == -1 || usbClass == mClass) + && (mSubclass == -1 || subclass == mSubclass) + && (mProtocol == -1 || protocol == mProtocol)); + } + + private boolean matches(int usbClass, int subclass, int protocol, String interfaceName) { + if (Flags.enableInterfaceNameDeviceFilter()) { + return matches(usbClass, subclass, protocol) + && (mInterfaceName == null || mInterfaceName.equals(interfaceName)); + } else { + return matches(usbClass, subclass, protocol); + } } public boolean matches(UsbDevice device) { @@ -204,7 +225,7 @@ public class DeviceFilter { for (int i = 0; i < count; i++) { UsbInterface intf = device.getInterface(i); if (matches(intf.getInterfaceClass(), intf.getInterfaceSubclass(), - intf.getInterfaceProtocol())) return true; + intf.getInterfaceProtocol(), intf.getName())) return true; } return false; @@ -320,11 +341,12 @@ public class DeviceFilter { @Override public String toString() { - return "DeviceFilter[mVendorId=" + mVendorId + ",mProductId=" + mProductId + - ",mClass=" + mClass + ",mSubclass=" + mSubclass + - ",mProtocol=" + mProtocol + ",mManufacturerName=" + mManufacturerName + - ",mProductName=" + mProductName + ",mSerialNumber=" + mSerialNumber + - "]"; + return "DeviceFilter[mVendorId=" + mVendorId + ",mProductId=" + mProductId + + ",mClass=" + mClass + ",mSubclass=" + mSubclass + + ",mProtocol=" + mProtocol + ",mManufacturerName=" + mManufacturerName + + ",mProductName=" + mProductName + ",mSerialNumber=" + mSerialNumber + + ",mInterfaceName=" + mInterfaceName + + "]"; } /** diff --git a/core/java/android/hardware/usb/flags/usb_framework_flags.aconfig b/core/java/android/hardware/usb/flags/usb_framework_flags.aconfig index 94df16030cdb..40e5ffb141ab 100644 --- a/core/java/android/hardware/usb/flags/usb_framework_flags.aconfig +++ b/core/java/android/hardware/usb/flags/usb_framework_flags.aconfig @@ -16,3 +16,11 @@ flag { description: "Feature flag for the api to check if a port supports mode change" bug: "323470419" } + +flag { + name: "enable_interface_name_device_filter" + is_exported: true + namespace: "usb" + description: "Feature flag to enable interface name as a parameter for device filter" + bug: "312828160" +} diff --git a/core/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java b/core/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java index 8237b20260ea..144c1cda3470 100644 --- a/core/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java +++ b/core/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java @@ -120,6 +120,11 @@ public abstract class OnDeviceSandboxedInferenceService extends Service { */ public static final String MODEL_UNLOADED_BUNDLE_KEY = "model_unloaded"; + /** + * @hide + */ + public static final String DEVICE_CONFIG_UPDATE_BUNDLE_KEY = "device_config_update"; + private IRemoteStorageService mRemoteStorageService; /** diff --git a/core/java/android/view/FocusFinder.java b/core/java/android/view/FocusFinder.java index 35b137a322e3..c5b6aa7118cb 100644 --- a/core/java/android/view/FocusFinder.java +++ b/core/java/android/view/FocusFinder.java @@ -129,7 +129,7 @@ public class FocusFinder { } ViewGroup effective = null; ViewParent nextParent = focused.getParent(); - do { + while (nextParent instanceof ViewGroup) { if (nextParent == root) { return effective != null ? effective : root; } @@ -143,7 +143,7 @@ public class FocusFinder { effective = vg; } nextParent = nextParent.getParent(); - } while (nextParent instanceof ViewGroup); + } return root; } diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index f1cb4103f008..d7f2b01f46ea 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -1262,10 +1262,13 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation mHost.getInputMethodManager(), null /* icProto */); } + final var statsToken = ImeTracker.forLogging().onStart(ImeTracker.TYPE_USER, + ImeTracker.ORIGIN_CLIENT, SoftInputShowHideReason.CONTROL_WINDOW_INSETS_ANIMATION, + mHost.isHandlingPointerEvent() /* fromUser */); controlAnimationUnchecked(types, cancellationSignal, listener, mFrame, fromIme, durationMs, interpolator, animationType, getLayoutInsetsDuringAnimationMode(types, fromPredictiveBack), - false /* useInsetsAnimationThread */, null /* statsToken */); + false /* useInsetsAnimationThread */, statsToken); } private void controlAnimationUnchecked(@InsetsType int types, @@ -1567,7 +1570,9 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation return; } final ImeTracker.Token statsToken = runner.getStatsToken(); - if (shown) { + if (runner.getAnimationType() == ANIMATION_TYPE_USER) { + ImeTracker.forLogging().onUserFinished(statsToken, shown); + } else if (shown) { ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_CLIENT_ANIMATION_FINISHED_SHOW); ImeTracker.forLogging().onShown(statsToken); @@ -1838,6 +1843,9 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.pendingAnim", 0); mHost.dispatchWindowInsetsAnimationStart(animation, bounds); mStartingAnimation = true; + if (runner.getAnimationType() == ANIMATION_TYPE_USER) { + ImeTracker.forLogging().onDispatched(runner.getStatsToken()); + } runner.setReadyDispatched(true); listener.onReady(runner, types); mStartingAnimation = false; diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 1df851a29b07..0715474f1c13 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -921,15 +921,6 @@ public final class ViewRootImpl implements ViewParent, private String mFrameRateCategoryView; /** - * The resolved pointer icon type requested by this window. - * A null value indicates the resolved pointer icon has not yet been calculated. - */ - // TODO(b/293587049): Remove pointer icon tracking by type when refactor is complete. - @Nullable - private Integer mPointerIconType = null; - private PointerIcon mCustomPointerIcon = null; - - /** * The resolved pointer icon requested by this window. * A null value indicates the resolved pointer icon has not yet been calculated. */ @@ -6430,7 +6421,6 @@ public final class ViewRootImpl implements ViewParent, private static final int MSG_SYNTHESIZE_INPUT_EVENT = 24; private static final int MSG_DISPATCH_WINDOW_SHOWN = 25; private static final int MSG_REQUEST_KEYBOARD_SHORTCUTS = 26; - private static final int MSG_UPDATE_POINTER_ICON = 27; private static final int MSG_POINTER_CAPTURE_CHANGED = 28; private static final int MSG_INSETS_CONTROL_CHANGED = 29; private static final int MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED = 30; @@ -6495,8 +6485,6 @@ public final class ViewRootImpl implements ViewParent, return "MSG_SYNTHESIZE_INPUT_EVENT"; case MSG_DISPATCH_WINDOW_SHOWN: return "MSG_DISPATCH_WINDOW_SHOWN"; - case MSG_UPDATE_POINTER_ICON: - return "MSG_UPDATE_POINTER_ICON"; case MSG_POINTER_CAPTURE_CHANGED: return "MSG_POINTER_CAPTURE_CHANGED"; case MSG_INSETS_CONTROL_CHANGED: @@ -6747,10 +6735,6 @@ public final class ViewRootImpl implements ViewParent, final int deviceId = msg.arg1; handleRequestKeyboardShortcuts(receiver, deviceId); } break; - case MSG_UPDATE_POINTER_ICON: { - MotionEvent event = (MotionEvent) msg.obj; - resetPointerIcon(event); - } break; case MSG_POINTER_CAPTURE_CHANGED: { final boolean hasCapture = msg.arg1 != 0; handlePointerCaptureChanged(hasCapture); @@ -7836,14 +7820,12 @@ public final class ViewRootImpl implements ViewParent, || action == MotionEvent.ACTION_HOVER_EXIT) { // Other apps or the window manager may change the icon type outside of // this app, therefore the icon type has to be reset on enter/exit event. - mPointerIconType = null; mResolvedPointerIcon = null; } if (action != MotionEvent.ACTION_HOVER_EXIT) { // Resolve the pointer icon if (!updatePointerIcon(event) && action == MotionEvent.ACTION_HOVER_MOVE) { - mPointerIconType = null; mResolvedPointerIcon = null; } } @@ -7906,12 +7888,6 @@ public final class ViewRootImpl implements ViewParent, return mAttachInfo.mHandlingPointerEvent; } - private void resetPointerIcon(MotionEvent event) { - mPointerIconType = null; - mResolvedPointerIcon = null; - updatePointerIcon(event); - } - /** * If there is pointer that is showing a PointerIcon in this window, refresh the icon for that diff --git a/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java index f454a6abf6a0..3091bf48a09e 100644 --- a/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java +++ b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java @@ -754,6 +754,19 @@ final class IInputMethodManagerGlobalInvoker { } } + /** @see com.android.server.inputmethod.ImeTrackerService#onDispatched */ + static void onDispatched(@NonNull ImeTracker.Token statsToken) { + final IImeTracker service = getImeTrackerService(); + if (service == null) { + return; + } + try { + service.onDispatched(statsToken); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** @see com.android.server.inputmethod.ImeTrackerService#hasPendingImeVisibilityRequests */ @AnyThread @RequiresPermission(Manifest.permission.TEST_INPUT_METHOD) diff --git a/core/java/android/view/inputmethod/ImeTracker.java b/core/java/android/view/inputmethod/ImeTracker.java index d992febc375e..edc99218c71e 100644 --- a/core/java/android/view/inputmethod/ImeTracker.java +++ b/core/java/android/view/inputmethod/ImeTracker.java @@ -71,24 +71,40 @@ public interface ImeTracker { /** The type of the IME request. */ @IntDef(prefix = { "TYPE_" }, value = { TYPE_SHOW, - TYPE_HIDE + TYPE_HIDE, + TYPE_USER, }) @Retention(RetentionPolicy.SOURCE) @interface Type {} - /** IME show request type. */ + /** + * IME show request type. + * + * @see android.view.InsetsController#ANIMATION_TYPE_SHOW + */ int TYPE_SHOW = ImeProtoEnums.TYPE_SHOW; - /** IME hide request type. */ + /** + * IME hide request type. + * + * @see android.view.InsetsController#ANIMATION_TYPE_HIDE + */ int TYPE_HIDE = ImeProtoEnums.TYPE_HIDE; + /** + * IME user-controlled animation request type. + * + * @see android.view.InsetsController#ANIMATION_TYPE_USER + */ + int TYPE_USER = ImeProtoEnums.TYPE_USER; + /** The status of the IME request. */ @IntDef(prefix = { "STATUS_" }, value = { STATUS_RUN, STATUS_CANCEL, STATUS_FAIL, STATUS_SUCCESS, - STATUS_TIMEOUT + STATUS_TIMEOUT, }) @Retention(RetentionPolicy.SOURCE) @interface Status {} @@ -117,7 +133,7 @@ public interface ImeTracker { @IntDef(prefix = { "ORIGIN_" }, value = { ORIGIN_CLIENT, ORIGIN_SERVER, - ORIGIN_IME + ORIGIN_IME, }) @Retention(RetentionPolicy.SOURCE) @interface Origin {} @@ -400,20 +416,36 @@ public interface ImeTracker { void onCancelled(@Nullable Token token, @Phase int phase); /** - * Called when the IME show request is successful. + * Called when the show IME request is successful. * * @param token the token tracking the current IME request or {@code null} otherwise. */ void onShown(@Nullable Token token); /** - * Called when the IME hide request is successful. + * Called when the hide IME request is successful. * * @param token the token tracking the current IME request or {@code null} otherwise. */ void onHidden(@Nullable Token token); /** + * Called when the user-controlled IME request was dispatched to the requesting app. The + * user animation can take an undetermined amount of time, so it shouldn't be tracked. + * + * @param token the token tracking the current IME request or {@code null} otherwise. + */ + void onDispatched(@Nullable Token token); + + /** + * Called when the animation of the user-controlled IME request finished. + * + * @param token the token tracking the current IME request or {@code null} otherwise. + * @param shown whether the end state of the animation was shown or hidden. + */ + void onUserFinished(@Nullable Token token, boolean shown); + + /** * Returns whether the current IME request was created due to a user interaction. This can * only be {@code true} when running on the view's UI thread. * @@ -482,13 +514,6 @@ public interface ImeTracker { /** Whether the stack trace at the request call site should be logged. */ private boolean mLogStackTrace; - private void reloadSystemProperties() { - mLogProgress = SystemProperties.getBoolean( - "persist.debug.imetracker", false); - mLogStackTrace = SystemProperties.getBoolean( - "persist.debug.imerequest.logstacktrace", false); - } - @NonNull @Override public Token onStart(@NonNull String component, int uid, @Type int type, @Origin int origin, @@ -497,7 +522,7 @@ public interface ImeTracker { final var token = IInputMethodManagerGlobalInvoker.onStart(tag, uid, type, origin, reason, fromUser); - Log.i(TAG, token.mTag + ": onRequest" + (type == TYPE_SHOW ? "Show" : "Hide") + Log.i(TAG, token.mTag + ": " + getOnStartPrefix(type) + " at " + Debug.originToString(origin) + " reason " + InputMethodDebug.softInputDisplayReasonToString(reason) + " fromUser " + fromUser, @@ -552,6 +577,45 @@ public interface ImeTracker { Log.i(TAG, token.mTag + ": onHidden"); } + + @Override + public void onDispatched(@Nullable Token token) { + if (token == null) return; + IInputMethodManagerGlobalInvoker.onDispatched(token); + + Log.i(TAG, token.mTag + ": onDispatched"); + } + + @Override + public void onUserFinished(@Nullable Token token, boolean shown) { + if (token == null) return; + // This is already sent to ImeTrackerService to mark it finished during onDispatched. + + Log.i(TAG, token.mTag + ": onUserFinished " + (shown ? "shown" : "hidden")); + } + + /** + * Gets the prefix string for {@link #onStart} based on the given request type. + * + * @param type request type for which to create the prefix string with. + */ + @NonNull + private static String getOnStartPrefix(@Type int type) { + return switch (type) { + case TYPE_SHOW -> "onRequestShow"; + case TYPE_HIDE -> "onRequestHide"; + case TYPE_USER -> "onRequestUser"; + default -> "onRequestUnknown"; + }; + } + + /** Reloads the system properties related to this class. */ + private void reloadSystemProperties() { + mLogProgress = SystemProperties.getBoolean( + "persist.debug.imetracker", false); + mLogStackTrace = SystemProperties.getBoolean( + "persist.debug.imerequest.logstacktrace", false); + } }; /** The singleton IME tracker instance for instrumenting jank metrics. */ diff --git a/core/java/android/window/TaskFragmentParentInfo.java b/core/java/android/window/TaskFragmentParentInfo.java index a77c23475c60..15554167c702 100644 --- a/core/java/android/window/TaskFragmentParentInfo.java +++ b/core/java/android/window/TaskFragmentParentInfo.java @@ -18,6 +18,8 @@ package android.window; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SuppressLint; +import android.annotation.TestApi; import android.app.WindowConfiguration; import android.content.res.Configuration; import android.os.Parcel; @@ -27,10 +29,13 @@ import android.view.SurfaceControl; import java.util.Objects; /** - * The information about the parent Task of a particular TaskFragment + * The information about the parent Task of a particular TaskFragment. + * * @hide */ -public class TaskFragmentParentInfo implements Parcelable { +@SuppressLint("UnflaggedApi") // @TestApi to replace legacy usages. +@TestApi +public final class TaskFragmentParentInfo implements Parcelable { @NonNull private final Configuration mConfiguration = new Configuration(); @@ -42,6 +47,7 @@ public class TaskFragmentParentInfo implements Parcelable { @Nullable private final SurfaceControl mDecorSurface; + /** @hide */ public TaskFragmentParentInfo(@NonNull Configuration configuration, int displayId, boolean visible, boolean hasDirectActivity, @Nullable SurfaceControl decorSurface) { mConfiguration.setTo(configuration); @@ -51,6 +57,7 @@ public class TaskFragmentParentInfo implements Parcelable { mDecorSurface = decorSurface; } + /** @hide */ public TaskFragmentParentInfo(@NonNull TaskFragmentParentInfo info) { mConfiguration.setTo(info.getConfiguration()); mDisplayId = info.mDisplayId; @@ -59,7 +66,11 @@ public class TaskFragmentParentInfo implements Parcelable { mDecorSurface = info.mDecorSurface; } - /** The {@link Configuration} of the parent Task */ + /** + * The {@link Configuration} of the parent Task + * + * @hide + */ @NonNull public Configuration getConfiguration() { return mConfiguration; @@ -68,19 +79,27 @@ public class TaskFragmentParentInfo implements Parcelable { /** * The display ID of the parent Task. {@link android.view.Display#INVALID_DISPLAY} means the * Task is detached from previously associated display. + * + * @hide */ public int getDisplayId() { return mDisplayId; } - /** Whether the parent Task is visible or not */ + /** + * Whether the parent Task is visible or not + * + * @hide + */ public boolean isVisible() { return mVisible; } /** * Whether the parent Task has any direct child activity, which is not embedded in any - * TaskFragment, or not + * TaskFragment, or not. + * + * @hide */ public boolean hasDirectActivity() { return mHasDirectActivity; @@ -93,6 +112,8 @@ public class TaskFragmentParentInfo implements Parcelable { * {@link com.android.server.wm.WindowOrganizerController#configurationsAreEqualForOrganizer( * Configuration, Configuration)} to determine if this {@link TaskFragmentParentInfo} should * be dispatched to the client. + * + * @hide */ public boolean equalsForTaskFragmentOrganizer(@Nullable TaskFragmentParentInfo that) { if (that == null) { @@ -103,6 +124,7 @@ public class TaskFragmentParentInfo implements Parcelable { && mDecorSurface == that.mDecorSurface; } + /** @hide */ @Nullable public SurfaceControl getDecorSurface() { return mDecorSurface; @@ -156,6 +178,7 @@ public class TaskFragmentParentInfo implements Parcelable { return result; } + @SuppressLint("UnflaggedApi") // @TestApi to replace legacy usages. @Override public void writeToParcel(@NonNull Parcel dest, int flags) { mConfiguration.writeToParcel(dest, flags); @@ -173,6 +196,8 @@ public class TaskFragmentParentInfo implements Parcelable { mDecorSurface = in.readTypedObject(SurfaceControl.CREATOR); } + @SuppressLint("UnflaggedApi") // @TestApi to replace legacy usages. + @NonNull public static final Creator<TaskFragmentParentInfo> CREATOR = new Creator<TaskFragmentParentInfo>() { @Override @@ -186,6 +211,7 @@ public class TaskFragmentParentInfo implements Parcelable { } }; + @SuppressLint("UnflaggedApi") // @TestApi to replace legacy usages. @Override public int describeContents() { return 0; diff --git a/core/java/android/window/TaskFragmentTransaction.java b/core/java/android/window/TaskFragmentTransaction.java index 4dada108c4c6..32e3f5ad10ca 100644 --- a/core/java/android/window/TaskFragmentTransaction.java +++ b/core/java/android/window/TaskFragmentTransaction.java @@ -24,7 +24,6 @@ import android.annotation.Nullable; import android.annotation.SuppressLint; import android.annotation.TestApi; import android.content.Intent; -import android.content.res.Configuration; import android.os.Binder; import android.os.Bundle; import android.os.IBinder; @@ -248,13 +247,6 @@ public final class TaskFragmentTransaction implements Parcelable { return this; } - // TODO(b/241043377): Keep this API to prevent @TestApi changes. Remove in the next release. - /** Configuration of the parent Task. */ - @NonNull - public Change setTaskConfiguration(@NonNull Configuration configuration) { - return this; - } - /** * If the {@link #TYPE_TASK_FRAGMENT_ERROR} is from a {@link WindowContainerTransaction} * from the {@link TaskFragmentOrganizer}, it may come with an error callback token to @@ -299,12 +291,11 @@ public final class TaskFragmentTransaction implements Parcelable { return this; } - // TODO(b/241043377): Hide this API to prevent @TestApi changes. Remove in the next release. /** * Sets info of the parent Task of the embedded TaskFragment. * @see TaskFragmentParentInfo * - * @hide pending unhide + * @hide */ @NonNull public Change setTaskFragmentParentInfo(@NonNull TaskFragmentParentInfo info) { @@ -338,12 +329,6 @@ public final class TaskFragmentTransaction implements Parcelable { return mTaskId; } - // TODO(b/241043377): Keep this API to prevent @TestApi changes. Remove in the next release. - @Nullable - public Configuration getTaskConfiguration() { - return mTaskFragmentParentInfo.getConfiguration(); - } - @Nullable public IBinder getErrorCallbackToken() { return mErrorCallbackToken; @@ -365,8 +350,10 @@ public final class TaskFragmentTransaction implements Parcelable { return mActivityToken; } - // TODO(b/241043377): Hide this API to prevent @TestApi changes. Remove in the next release. - /** @hide pending unhide */ + /** + * Obtains the {@link TaskFragmentParentInfo} for this transaction. + */ + @SuppressLint("UnflaggedApi") // @TestApi to replace legacy usages. @Nullable public TaskFragmentParentInfo getTaskFragmentParentInfo() { return mTaskFragmentParentInfo; diff --git a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig index cd13c4abac0b..4b2beb903325 100644 --- a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig +++ b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig @@ -9,6 +9,16 @@ flag { } flag { + name: "disable_thin_letterboxing_policy" + namespace: "large_screen_experiences_app_compat" + description: "Whether reachability is disabled in case of thin letterboxing" + bug: "341027847" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "allows_screen_size_decoupled_from_status_bar_and_cutout" namespace: "large_screen_experiences_app_compat" description: "When necessary, configuration decoupled from status bar and display cutout" diff --git a/core/java/com/android/internal/inputmethod/IImeTracker.aidl b/core/java/com/android/internal/inputmethod/IImeTracker.aidl index ab4edb65780b..ebae39ee3241 100644 --- a/core/java/com/android/internal/inputmethod/IImeTracker.aidl +++ b/core/java/com/android/internal/inputmethod/IImeTracker.aidl @@ -64,20 +64,28 @@ interface IImeTracker { oneway void onCancelled(in ImeTracker.Token statsToken, int phase); /** - * Called when the IME show request is successful. + * Called when the show IME request is successful. * * @param statsToken the token tracking the current IME request. */ oneway void onShown(in ImeTracker.Token statsToken); /** - * Called when the IME hide request is successful. + * Called when the hide IME request is successful. * * @param statsToken the token tracking the current IME request. */ oneway void onHidden(in ImeTracker.Token statsToken); /** + * Called when the user-controlled IME request was dispatched to the requesting app. The + * user animation can take an undetermined amount of time, so it shouldn't be tracked. + * + * @param statsToken the token tracking the current IME request. + */ + oneway void onDispatched(in ImeTracker.Token statsToken); + + /** * Checks whether there are any pending IME visibility requests. * * @return {@code true} iff there are pending IME visibility requests. diff --git a/core/java/com/android/internal/inputmethod/InputMethodDebug.java b/core/java/com/android/internal/inputmethod/InputMethodDebug.java index a0aad31d2e04..2a5593f6d584 100644 --- a/core/java/com/android/internal/inputmethod/InputMethodDebug.java +++ b/core/java/com/android/internal/inputmethod/InputMethodDebug.java @@ -297,6 +297,8 @@ public final class InputMethodDebug { return "SHOW_SOFT_INPUT_IME_TOGGLE_SOFT_INPUT"; case SoftInputShowHideReason.SHOW_SOFT_INPUT_IMM_DEPRECATION: return "SHOW_SOFT_INPUT_IMM_DEPRECATION"; + case SoftInputShowHideReason.CONTROL_WINDOW_INSETS_ANIMATION: + return "CONTROL_WINDOW_INSETS_ANIMATION"; default: return "Unknown=" + reason; } diff --git a/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java index da738a01ec39..eb6a81031321 100644 --- a/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java +++ b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java @@ -88,6 +88,7 @@ import java.lang.annotation.Retention; SoftInputShowHideReason.HIDE_SOFT_INPUT_REQUEST_HIDE_WITH_CONTROL, SoftInputShowHideReason.SHOW_SOFT_INPUT_IME_TOGGLE_SOFT_INPUT, SoftInputShowHideReason.SHOW_SOFT_INPUT_IMM_DEPRECATION, + SoftInputShowHideReason.CONTROL_WINDOW_INSETS_ANIMATION, }) public @interface SoftInputShowHideReason { /** Default, undefined reason. */ @@ -397,4 +398,10 @@ public @interface SoftInputShowHideReason { * {@link InputMethodManager#showSoftInputFromInputMethod(IBinder, int)}. */ int SHOW_SOFT_INPUT_IMM_DEPRECATION = ImeProtoEnums.REASON_SHOW_SOFT_INPUT_IMM_DEPRECATION; + + /** + * Show / Hide soft input by application-controlled animation in + * {@link android.view.InsetsController#controlWindowInsetsAnimation}. + */ + int CONTROL_WINDOW_INSETS_ANIMATION = ImeProtoEnums.REASON_CONTROL_WINDOW_INSETS_ANIMATION; } diff --git a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java index 97ce96ec30f6..1dcd893eb143 100644 --- a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java +++ b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java @@ -1908,12 +1908,16 @@ public class ParsingPackageUtils { } else if (parser.getName().equals("package")) { final TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestQueriesPackage); - final String packageName = sa.getNonConfigurationString( - R.styleable.AndroidManifestQueriesPackage_name, 0); - if (TextUtils.isEmpty(packageName)) { - return input.error("Package name is missing from package tag."); + try { + final String packageName = sa.getNonConfigurationString( + R.styleable.AndroidManifestQueriesPackage_name, 0); + if (TextUtils.isEmpty(packageName)) { + return input.error("Package name is missing from package tag."); + } + pkg.addQueriesPackage(packageName.intern()); + } finally { + sa.recycle(); } - pkg.addQueriesPackage(packageName.intern()); } else if (parser.getName().equals("provider")) { final TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestQueriesProvider); diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 5bd20332f381..f5b45660c547 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -4723,6 +4723,8 @@ <!-- The broadcast intent name for notifying when the on-device model has been unloaded --> <string name="config_onDeviceIntelligenceModelUnloadedBroadcastKey" translatable="false"></string> + <!-- The DeviceConfig namespace for the default system on-device sandboxed inference service. --> + <string name="config_defaultOnDeviceIntelligenceDeviceConfigNamespace" translatable="false"></string> <!-- Component name that accepts ACTION_SEND intents for requesting ambient context consent for wearable sensing. --> diff --git a/core/res/res/values/config_battery_stats.xml b/core/res/res/values/config_battery_stats.xml index 8d9736273145..80cf0881e8cc 100644 --- a/core/res/res/values/config_battery_stats.xml +++ b/core/res/res/values/config_battery_stats.xml @@ -27,16 +27,18 @@ <!-- Whether to reset Battery Stats on unplug if the battery was significantly charged --> <bool name="config_batteryStatsResetOnUnplugAfterSignificantCharge">true</bool> - <!-- CPU power stats collection throttle period in milliseconds. Since power stats collection - is a relatively expensive operation, this throttle period may need to be adjusted for low-power - devices--> - <integer name="config_defaultPowerStatsThrottlePeriodCpu">60000</integer> - - <!-- Mobile Radio power stats collection throttle period in milliseconds. --> - <integer name="config_defaultPowerStatsThrottlePeriodMobileRadio">3600000</integer> - - <!-- Mobile Radio power stats collection throttle period in milliseconds. --> - <integer name="config_defaultPowerStatsThrottlePeriodWifi">3600000</integer> + <!-- Power stats collection throttle periods in milliseconds. Since power stats collection + is a relatively expensive operation, these throttle period may need to be adjusted for low-power + devices. + + The syntax of this config string is as follows: + <pre> + power-component-name1:throttle-period-millis1 power-component-name2:throttle-period-ms2 ... + </pre> + Use "*" for the power-component-name to represent the default for all power components + not mentioned by name. + --> + <string name="config_powerStatsThrottlePeriods">cpu:60000 *:300000</string> <!-- PowerStats aggregation period in milliseconds. This is the interval at which the power stats aggregation procedure is performed and the results stored in PowerStatsStore. --> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index a9d03f543d55..a6bb8d7e28e1 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3947,6 +3947,7 @@ <java-symbol type="string" name="config_defaultOnDeviceSandboxedInferenceService" /> <java-symbol type="string" name="config_onDeviceIntelligenceModelLoadedBroadcastKey" /> <java-symbol type="string" name="config_onDeviceIntelligenceModelUnloadedBroadcastKey" /> + <java-symbol type="string" name="config_defaultOnDeviceIntelligenceDeviceConfigNamespace" /> <java-symbol type="string" name="config_retailDemoPackage" /> <java-symbol type="string" name="config_retailDemoPackageSignature" /> @@ -5243,9 +5244,7 @@ <java-symbol type="bool" name="config_batteryStatsResetOnUnplugHighBatteryLevel" /> <java-symbol type="bool" name="config_batteryStatsResetOnUnplugAfterSignificantCharge" /> - <java-symbol type="integer" name="config_defaultPowerStatsThrottlePeriodCpu" /> - <java-symbol type="integer" name="config_defaultPowerStatsThrottlePeriodMobileRadio" /> - <java-symbol type="integer" name="config_defaultPowerStatsThrottlePeriodWifi" /> + <java-symbol type="string" name="config_powerStatsThrottlePeriods" /> <java-symbol type="integer" name="config_powerStatsAggregationPeriod" /> <java-symbol type="integer" name="config_aggregatedPowerStatsSpanDuration" /> diff --git a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java index b6f4429aec8a..ee1d1e1b975c 100644 --- a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java +++ b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java @@ -29,6 +29,7 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -65,6 +66,7 @@ import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.concurrent.RejectedExecutionException; import java.util.function.BiConsumer; /** @@ -221,4 +223,17 @@ public class ClientTransactionListenerControllerTest { 123 /* newDisplayId */, true /* shouldReportConfigChange*/); inOrder.verify(mController).onContextConfigurationPostChanged(context); } + + @Test + public void testDisplayListenerHandlerClosed() { + doReturn(123).when(mActivity).getDisplayId(); + doThrow(new RejectedExecutionException()).when(mController).onDisplayChanged(123); + + mController.onContextConfigurationPreChanged(mActivity); + mConfiguration.windowConfiguration.setMaxBounds(new Rect(0, 0, 100, 200)); + mController.onContextConfigurationPostChanged(mActivity); + + // No crash + verify(mController).onDisplayChanged(123); + } } diff --git a/data/etc/core.protolog.pb b/data/etc/core.protolog.pb Binary files differindex 000f6ef46c2c..b41a607e91c7 100644 --- a/data/etc/core.protolog.pb +++ b/data/etc/core.protolog.pb diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index 01deb4957cd3..b93cd468118a 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -583,6 +583,12 @@ "group": "WM_DEBUG_STATES", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, + "7211222997110112110": { + "message": "Refreshing activity for freeform camera compatibility treatment, activityRecord=%s", + "level": "VERBOSE", + "group": "WM_DEBUG_STATES", + "at": "com\/android\/server\/wm\/ActivityRefresher.java" + }, "1665699123574159131": { "message": "Starting activity when config will change = %b", "level": "VERBOSE", @@ -1771,12 +1777,6 @@ "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/DisplayRotationCompatPolicy.java" }, - "-7756685416834187936": { - "message": "Refreshing activity for camera compatibility treatment, activityRecord=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/DisplayRotationCompatPolicy.java" - }, "-5176775281239247368": { "message": "Reverting orientation after camera compat force rotation", "level": "VERBOSE", diff --git a/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java index 9284c0e5b928..4bc3ecebb067 100644 --- a/graphics/java/android/graphics/ColorSpace.java +++ b/graphics/java/android/graphics/ColorSpace.java @@ -2493,7 +2493,11 @@ public abstract class ColorSpace { return mNativePtr; } - /** Need a nested class due to b/337329128. */ + /** + * These methods can't be put in the Rgb class directly, because ColorSpace's + * static initializer instantiates Rgb, whose constructor needs them, which is a variation + * of b/337329128. + */ static class Native { static native long nativeGetNativeFinalizer(); static native long nativeCreate(float a, float b, float c, float d, diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java index 1fbaeeac8608..29936cc2cac3 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java @@ -33,7 +33,9 @@ import static androidx.window.extensions.embedding.SplitPresenter.CONTAINER_POSI import static androidx.window.extensions.embedding.SplitPresenter.CONTAINER_POSITION_RIGHT; import static androidx.window.extensions.embedding.SplitPresenter.CONTAINER_POSITION_TOP; -import android.annotation.DimenRes; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; import android.annotation.Nullable; import android.app.Activity; import android.app.ActivityThread; @@ -53,9 +55,11 @@ import android.view.Gravity; import android.view.MotionEvent; import android.view.SurfaceControl; import android.view.SurfaceControlViewHost; +import android.view.VelocityTracker; import android.view.View; import android.view.WindowManager; import android.view.WindowlessWindowManager; +import android.view.animation.PathInterpolator; import android.widget.FrameLayout; import android.widget.ImageButton; import android.window.InputTransferToken; @@ -97,6 +101,16 @@ class DividerPresenter implements View.OnTouchListener { @VisibleForTesting static final int DEFAULT_DIVIDER_WIDTH_DP = 24; + @VisibleForTesting + static final PathInterpolator FLING_ANIMATION_INTERPOLATOR = + new PathInterpolator(0.4f, 0f, 0.2f, 1f); + @VisibleForTesting + static final int FLING_ANIMATION_DURATION = 250; + @VisibleForTesting + static final int MIN_DISMISS_VELOCITY_DP_PER_SECOND = 600; + @VisibleForTesting + static final int MIN_FLING_VELOCITY_DP_PER_SECOND = 400; + private final int mTaskId; @NonNull @@ -109,6 +123,14 @@ class DividerPresenter implements View.OnTouchListener { private final Executor mCallbackExecutor; /** + * The VelocityTracker of the divider, used to track the dragging velocity. This field is + * {@code null} until dragging starts. + */ + @GuardedBy("mLock") + @Nullable + VelocityTracker mVelocityTracker; + + /** * The {@link Properties} of the divider. This field is {@code null} when no divider should be * drawn, e.g. when the split doesn't have {@link DividerAttributes} or when the decor surface * is not available. @@ -370,13 +392,11 @@ class DividerPresenter implements View.OnTouchListener { applicationContext.getResources().getDisplayMetrics()); } - private static int getDimensionDp(@DimenRes int resId) { - final Context context = ActivityThread.currentActivityThread().getApplication(); - final int px = context.getResources().getDimensionPixelSize(resId); - return (int) TypedValue.convertPixelsToDimension( - COMPLEX_UNIT_DIP, - px, - context.getResources().getDisplayMetrics()); + private static float getDisplayDensity() { + // TODO(b/329193115) support divider on secondary display + final Context applicationContext = + ActivityThread.currentActivityThread().getApplication(); + return applicationContext.getResources().getDisplayMetrics().density; } /** @@ -487,24 +507,27 @@ class DividerPresenter implements View.OnTouchListener { @Override public boolean onTouch(@NonNull View view, @NonNull MotionEvent event) { synchronized (mLock) { - final Rect taskBounds = mProperties.mConfiguration.windowConfiguration.getBounds(); - mDividerPosition = calculateDividerPosition( - event, taskBounds, mRenderer.mDividerWidthPx, mProperties.mDividerAttributes, - mProperties.mIsVerticalSplit, calculateMinPosition(), calculateMaxPosition()); - mRenderer.setDividerPosition(mDividerPosition); - switch (event.getAction()) { - case MotionEvent.ACTION_DOWN: - onStartDragging(); - break; - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: - onFinishDragging(); - break; - case MotionEvent.ACTION_MOVE: - onDrag(); - break; - default: - break; + if (mProperties != null && mRenderer != null) { + final Rect taskBounds = mProperties.mConfiguration.windowConfiguration.getBounds(); + mDividerPosition = calculateDividerPosition( + event, taskBounds, mRenderer.mDividerWidthPx, + mProperties.mDividerAttributes, mProperties.mIsVerticalSplit, + calculateMinPosition(), calculateMaxPosition()); + mRenderer.setDividerPosition(mDividerPosition); + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + onStartDragging(event); + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + onFinishDragging(event); + break; + case MotionEvent.ACTION_MOVE: + onDrag(event); + break; + default: + break; + } } } @@ -514,7 +537,10 @@ class DividerPresenter implements View.OnTouchListener { } @GuardedBy("mLock") - private void onStartDragging() { + private void onStartDragging(@NonNull MotionEvent event) { + mVelocityTracker = VelocityTracker.obtain(); + mVelocityTracker.addMovement(event); + mRenderer.mIsDragging = true; mRenderer.mDragHandle.setPressed(mRenderer.mIsDragging); mRenderer.updateSurface(); @@ -536,16 +562,81 @@ class DividerPresenter implements View.OnTouchListener { } @GuardedBy("mLock") - private void onDrag() { + private void onDrag(@NonNull MotionEvent event) { + if (mVelocityTracker != null) { + mVelocityTracker.addMovement(event); + } mRenderer.updateSurface(); } @GuardedBy("mLock") - private void onFinishDragging() { - mDividerPosition = adjustDividerPositionForSnapPoints(mDividerPosition); - mRenderer.setDividerPosition(mDividerPosition); + private void onFinishDragging(@NonNull MotionEvent event) { + float velocity = 0.0f; + if (mVelocityTracker != null) { + mVelocityTracker.addMovement(event); + mVelocityTracker.computeCurrentVelocity(1000 /* units */); + velocity = mProperties.mIsVerticalSplit + ? mVelocityTracker.getXVelocity() + : mVelocityTracker.getYVelocity(); + mVelocityTracker.recycle(); + } + + final int prevDividerPosition = mDividerPosition; + mDividerPosition = dividerPositionForSnapPoints(mDividerPosition, velocity); + if (mDividerPosition != prevDividerPosition) { + ValueAnimator animator = getFlingAnimator(prevDividerPosition, mDividerPosition); + animator.start(); + } else { + onDraggingEnd(); + } + } + + @GuardedBy("mLock") + @NonNull + @VisibleForTesting + ValueAnimator getFlingAnimator(int prevDividerPosition, int snappedDividerPosition) { + final ValueAnimator animator = + getValueAnimator(prevDividerPosition, snappedDividerPosition); + animator.addUpdateListener(animation -> { + synchronized (mLock) { + updateDividerPosition((int) animation.getAnimatedValue()); + } + }); + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + synchronized (mLock) { + onDraggingEnd(); + } + } + + @Override + public void onAnimationCancel(Animator animation) { + synchronized (mLock) { + onDraggingEnd(); + } + } + }); + return animator; + } + + @VisibleForTesting + static ValueAnimator getValueAnimator(int prevDividerPosition, int snappedDividerPosition) { + ValueAnimator animator = ValueAnimator + .ofInt(prevDividerPosition, snappedDividerPosition) + .setDuration(FLING_ANIMATION_DURATION); + animator.setInterpolator(FLING_ANIMATION_INTERPOLATOR); + return animator; + } + + @GuardedBy("mLock") + private void updateDividerPosition(int position) { + mRenderer.setDividerPosition(position); mRenderer.updateSurface(); + } + @GuardedBy("mLock") + private void onDraggingEnd() { // Veil visibility change should be applied together with the surface boost transaction in // the wct. final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); @@ -570,36 +661,76 @@ class DividerPresenter implements View.OnTouchListener { /** * Returns the divider position adjusted for the min max ratio and fullscreen expansion. - * - * If the dragging position is above the {@link DividerAttributes#getPrimaryMaxRatio()} or below - * {@link DividerAttributes#getPrimaryMinRatio()} and - * {@link DividerAttributes#isDraggingToFullscreenAllowed} is {@code true}, the system will - * choose a snap algorithm to adjust the ending position to either fully expand one container or - * move the divider back to the specified min/max ratio. - * - * TODO(b/327067596) implement snap algorithm - * * The adjusted divider position is in the range of [minPosition, maxPosition] for a split, 0 * for expanded right (bottom) container, or task width (height) minus the divider width for * expanded left (top) container. */ @GuardedBy("mLock") - private int adjustDividerPositionForSnapPoints(int dividerPosition) { + private int dividerPositionForSnapPoints(int dividerPosition, float velocity) { final Rect taskBounds = mProperties.mConfiguration.windowConfiguration.getBounds(); final int minPosition = calculateMinPosition(); final int maxPosition = calculateMaxPosition(); final int fullyExpandedPosition = mProperties.mIsVerticalSplit ? taskBounds.right - mRenderer.mDividerWidthPx : taskBounds.bottom - mRenderer.mDividerWidthPx; + if (isDraggingToFullscreenAllowed(mProperties.mDividerAttributes)) { - if (dividerPosition < minPosition) { - return 0; + final float displayDensity = getDisplayDensity(); + return dividerPositionWithDraggingToFullscreenAllowed( + dividerPosition, + minPosition, + maxPosition, + fullyExpandedPosition, + velocity, + displayDensity); + } + return Math.clamp(dividerPosition, minPosition, maxPosition); + } + + /** + * Returns the divider position given a set of position options. A snap algorithm is used to + * adjust the ending position to either fully expand one container or move the divider back to + * the specified min/max ratio depending on the dragging velocity. + */ + @VisibleForTesting + static int dividerPositionWithDraggingToFullscreenAllowed(int dividerPosition, int minPosition, + int maxPosition, int fullyExpandedPosition, float velocity, float displayDensity) { + final float minDismissVelocityPxPerSecond = + MIN_DISMISS_VELOCITY_DP_PER_SECOND * displayDensity; + final float minFlingVelocityPxPerSecond = + MIN_FLING_VELOCITY_DP_PER_SECOND * displayDensity; + if (dividerPosition < minPosition && velocity < -minDismissVelocityPxPerSecond) { + return 0; + } + if (dividerPosition > maxPosition && velocity > minDismissVelocityPxPerSecond) { + return fullyExpandedPosition; + } + if (Math.abs(velocity) < minFlingVelocityPxPerSecond) { + if (dividerPosition >= minPosition && dividerPosition <= maxPosition) { + return dividerPosition; } - if (dividerPosition > maxPosition) { - return fullyExpandedPosition; + int[] possiblePositions = {0, minPosition, maxPosition, fullyExpandedPosition}; + return snap(dividerPosition, possiblePositions); + } + if (velocity < 0) { + return 0; + } else { + return fullyExpandedPosition; + } + } + + /** Calculates the snapped divider position based on the possible positions and distance. */ + private static int snap(int dividerPosition, int[] possiblePositions) { + int snappedPosition = dividerPosition; + float minDistance = Float.MAX_VALUE; + for (int position : possiblePositions) { + float distance = Math.abs(dividerPosition - position); + if (distance < minDistance) { + snappedPosition = position; + minDistance = distance; } } - return Math.clamp(dividerPosition, minPosition, maxPosition); + return snappedPosition; } private static void setDecorSurfaceBoosted( diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java index b0a45e285896..746607c8094c 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java @@ -19,6 +19,10 @@ package androidx.window.extensions.embedding; import static android.window.TaskFragmentOperation.OP_TYPE_CREATE_OR_MOVE_TASK_FRAGMENT_DECOR_SURFACE; import static android.window.TaskFragmentOperation.OP_TYPE_REMOVE_TASK_FRAGMENT_DECOR_SURFACE; +import static androidx.window.extensions.embedding.DividerPresenter.FLING_ANIMATION_DURATION; +import static androidx.window.extensions.embedding.DividerPresenter.FLING_ANIMATION_INTERPOLATOR; +import static androidx.window.extensions.embedding.DividerPresenter.MIN_DISMISS_VELOCITY_DP_PER_SECOND; +import static androidx.window.extensions.embedding.DividerPresenter.MIN_FLING_VELOCITY_DP_PER_SECOND; import static androidx.window.extensions.embedding.DividerPresenter.getBoundsOffsetForDivider; import static androidx.window.extensions.embedding.DividerPresenter.getInitialDividerPosition; import static androidx.window.extensions.embedding.SplitPresenter.CONTAINER_POSITION_BOTTOM; @@ -35,6 +39,7 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.animation.ValueAnimator; import android.app.Activity; import android.content.res.Configuration; import android.graphics.Color; @@ -637,6 +642,105 @@ public class DividerPresenterTest { DividerPresenter.getContainerBackgroundColor(container, defaultColor)); } + @Test + public void testGetValueAnimator() { + ValueAnimator animator = + DividerPresenter.getValueAnimator( + 375 /* prevDividerPosition */, + 500 /* snappedDividerPosition */); + + assertEquals(animator.getDuration(), FLING_ANIMATION_DURATION); + assertEquals(animator.getInterpolator(), FLING_ANIMATION_INTERPOLATOR); + } + + @Test + public void testDividerPositionWithDraggingToFullscreenAllowed() { + final float displayDensity = 600F; + final float dismissVelocity = MIN_DISMISS_VELOCITY_DP_PER_SECOND * displayDensity + 10f; + final float nonFlingVelocity = MIN_FLING_VELOCITY_DP_PER_SECOND * displayDensity - 10f; + final float flingVelocity = MIN_FLING_VELOCITY_DP_PER_SECOND * displayDensity + 10f; + + // Divider position is less than minPosition and the velocity is enough to be dismissed + assertEquals( + 0, // Closed position + DividerPresenter.dividerPositionWithDraggingToFullscreenAllowed( + 10 /* dividerPosition */, + 30 /* minPosition */, + 900 /* maxPosition */, + 1200 /* fullyExpandedPosition */, + -dismissVelocity, + displayDensity)); + + // Divider position is greater than maxPosition and the velocity is enough to be dismissed + assertEquals( + 1200, // Fully expanded position + DividerPresenter.dividerPositionWithDraggingToFullscreenAllowed( + 1000 /* dividerPosition */, + 30 /* minPosition */, + 900 /* maxPosition */, + 1200 /* fullyExpandedPosition */, + dismissVelocity, + displayDensity)); + + // Divider position is returned when the velocity is not fast enough for fling and is in + // between minPosition and maxPosition + assertEquals( + 500, // dividerPosition is not snapped + DividerPresenter.dividerPositionWithDraggingToFullscreenAllowed( + 500 /* dividerPosition */, + 30 /* minPosition */, + 900 /* maxPosition */, + 1200 /* fullyExpandedPosition */, + nonFlingVelocity, + displayDensity)); + + // Divider position is snapped when the velocity is not fast enough for fling and larger + // than maxPosition + assertEquals( + 900, // Closest position is maxPosition + DividerPresenter.dividerPositionWithDraggingToFullscreenAllowed( + 950 /* dividerPosition */, + 30 /* minPosition */, + 900 /* maxPosition */, + 1200 /* fullyExpandedPosition */, + nonFlingVelocity, + displayDensity)); + + // Divider position is snapped when the velocity is not fast enough for fling and smaller + // than minPosition + assertEquals( + 30, // Closest position is minPosition + DividerPresenter.dividerPositionWithDraggingToFullscreenAllowed( + 20 /* dividerPosition */, + 30 /* minPosition */, + 900 /* maxPosition */, + 1200 /* fullyExpandedPosition */, + nonFlingVelocity, + displayDensity)); + + // Divider position is greater than minPosition and the velocity is enough for fling + assertEquals( + 0, // Closed position + DividerPresenter.dividerPositionWithDraggingToFullscreenAllowed( + 50 /* dividerPosition */, + 30 /* minPosition */, + 900 /* maxPosition */, + 1200 /* fullyExpandedPosition */, + -flingVelocity, + displayDensity)); + + // Divider position is less than maxPosition and the velocity is enough for fling + assertEquals( + 1200, // Fully expanded position + DividerPresenter.dividerPositionWithDraggingToFullscreenAllowed( + 800 /* dividerPosition */, + 30 /* minPosition */, + 900 /* maxPosition */, + 1200 /* fullyExpandedPosition */, + flingVelocity, + displayDensity)); + } + private TaskFragmentContainer createMockTaskFragmentContainer( @NonNull IBinder token, @NonNull Rect bounds) { final TaskFragmentContainer container = mock(TaskFragmentContainer.class); diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/TransitionUtil.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/TransitionUtil.java index dcd4062cb819..785e30d879d2 100644 --- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/TransitionUtil.java +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/TransitionUtil.java @@ -69,8 +69,12 @@ public class TransitionUtil { /** Returns {@code true} if the transition is opening or closing mode. */ public static boolean isOpenOrCloseMode(@TransitionInfo.TransitionMode int mode) { - return mode == TRANSIT_OPEN || mode == TRANSIT_CLOSE - || mode == TRANSIT_TO_FRONT || mode == TRANSIT_TO_BACK; + return isOpeningMode(mode) || mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK; + } + + /** Returns {@code true} if the transition is opening mode. */ + public static boolean isOpeningMode(@TransitionInfo.TransitionMode int mode) { + return mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT; } /** Returns {@code true} if the transition has a display change. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java index 9e6c5fbf9f5e..38c344322a30 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java @@ -87,6 +87,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; import com.android.internal.statusbar.IStatusBarService; import com.android.launcher3.icons.BubbleIconFactory; +import com.android.wm.shell.Flags; import com.android.wm.shell.R; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.WindowManagerShellWrapper; @@ -1170,7 +1171,9 @@ public class BubbleController implements ConfigurationChangeListener, * @param bubbleKey key of the bubble being dragged */ public void startBubbleDrag(String bubbleKey) { - onBubbleDrag(bubbleKey, true /* isBeingDragged */); + if (mBubbleData.getSelectedBubble() != null) { + mBubbleBarViewCallback.expansionChanged(/* isExpanded = */ false); + } if (mBubbleStateListener != null) { boolean overflow = BubbleOverflow.KEY.equals(bubbleKey); Rect rect = new Rect(); @@ -1183,23 +1186,29 @@ public class BubbleController implements ConfigurationChangeListener, } /** - * A bubble is no longer being dragged in Launcher. As was released in given location. + * A bubble is no longer being dragged in Launcher. And was released in given location. * Will be called only when bubble bar is expanded. * - * @param bubbleKey key of the bubble being dragged * @param location location where bubble was released */ - public void stopBubbleDrag(String bubbleKey, BubbleBarLocation location) { + public void stopBubbleDrag(BubbleBarLocation location) { mBubblePositioner.setBubbleBarLocation(location); - onBubbleDrag(bubbleKey, false /* isBeingDragged */); + if (mBubbleData.getSelectedBubble() != null) { + mBubbleBarViewCallback.expansionChanged(/* isExpanded = */ true); + } } - private void onBubbleDrag(String bubbleKey, boolean isBeingDragged) { - // TODO(b/330585402): collapse stack if any bubble is dragged - if (mBubbleData.getSelectedBubble() != null - && mBubbleData.getSelectedBubble().getKey().equals(bubbleKey)) { - // Should collapse/expand only if equals to selected bubble. - mBubbleBarViewCallback.expansionChanged(/* isExpanded = */ !isBeingDragged); + /** + * A bubble was dragged and is released in dismiss target in Launcher. + * + * @param bubbleKey key of the bubble being dragged to dismiss target + */ + public void dragBubbleToDismiss(String bubbleKey) { + String selectedBubbleKey = mBubbleData.getSelectedBubbleKey(); + removeBubble(bubbleKey, Bubbles.DISMISS_USER_GESTURE); + if (selectedBubbleKey != null && !selectedBubbleKey.equals(bubbleKey)) { + // We did not remove the selected bubble. Expand it again + mBubbleBarViewCallback.expansionChanged(/* isExpanded = */ true); } } @@ -1858,7 +1867,11 @@ public class BubbleController implements ConfigurationChangeListener, @Override public void bubbleOverflowChanged(boolean hasBubbles) { - // TODO (b/334175587): tell stack view to hide / show the overflow + if (Flags.enableOptionalBubbleOverflow()) { + if (mStackView != null) { + mStackView.showOverflow(hasBubbles); + } + } } }; @@ -2358,12 +2371,6 @@ public class BubbleController implements ConfigurationChangeListener, } @Override - public void removeBubble(String key) { - mMainExecutor.execute( - () -> mController.removeBubble(key, Bubbles.DISMISS_USER_GESTURE)); - } - - @Override public void removeAllBubbles() { mMainExecutor.execute(() -> mController.removeAllBubbles(Bubbles.DISMISS_USER_GESTURE)); } @@ -2379,8 +2386,13 @@ public class BubbleController implements ConfigurationChangeListener, } @Override - public void stopBubbleDrag(String bubbleKey, BubbleBarLocation location) { - mMainExecutor.execute(() -> mController.stopBubbleDrag(bubbleKey, location)); + public void stopBubbleDrag(BubbleBarLocation location) { + mMainExecutor.execute(() -> mController.stopBubbleDrag(location)); + } + + @Override + public void dragBubbleToDismiss(String key) { + mMainExecutor.execute(() -> mController.dragBubbleToDismiss(key)); } @Override @@ -2397,7 +2409,10 @@ public class BubbleController implements ConfigurationChangeListener, @Override public void setBubbleBarBounds(Rect bubbleBarBounds) { - mMainExecutor.execute(() -> mBubblePositioner.setBubbleBarBounds(bubbleBarBounds)); + mMainExecutor.execute(() -> { + mBubblePositioner.setBubbleBarBounds(bubbleBarBounds); + if (mLayerView != null) mLayerView.updateExpandedView(); + }); } } @@ -2709,6 +2724,15 @@ public class BubbleController implements ConfigurationChangeListener, () -> BubbleController.this.onSensitiveNotificationProtectionStateChanged( sensitiveNotificationProtectionActive)); } + + @Override + public boolean canShowBubbleNotification() { + // in bubble bar mode, when the IME is visible we can't animate new bubbles. + if (BubbleController.this.isShowingAsBubbleBar()) { + return !BubbleController.this.mBubblePositioner.getIsImeVisible(); + } + return true; + } } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java index ea30af5c3d5a..26483c8428c6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java @@ -327,6 +327,14 @@ public class BubbleData { return mSelectedBubble; } + /** + * Returns the key of the selected bubble, or null if no bubble is selected. + */ + @Nullable + public String getSelectedBubbleKey() { + return mSelectedBubble != null ? mSelectedBubble.getKey() : null; + } + public BubbleOverflow getOverflow() { return mOverflow; } @@ -1228,9 +1236,7 @@ public class BubbleData { public void dump(PrintWriter pw) { pw.println("BubbleData state:"); pw.print(" selected: "); - pw.println(mSelectedBubble != null - ? mSelectedBubble.getKey() - : "null"); + pw.println(getSelectedBubbleKey()); pw.print(" expanded: "); pw.println(mExpanded); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java index 633b01bde4ca..18e04d14c71b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java @@ -44,6 +44,7 @@ import androidx.recyclerview.widget.RecyclerView; import com.android.internal.protolog.common.ProtoLog; import com.android.internal.util.ContrastColorUtil; +import com.android.wm.shell.Flags; import com.android.wm.shell.R; import java.util.ArrayList; @@ -195,7 +196,9 @@ public class BubbleOverflowContainerView extends LinearLayout { } void updateEmptyStateVisibility() { - mEmptyState.setVisibility(mOverflowBubbles.isEmpty() ? View.VISIBLE : View.GONE); + boolean showEmptyState = mOverflowBubbles.isEmpty() + && !Flags.enableOptionalBubbleOverflow(); + mEmptyState.setVisibility(showEmptyState ? View.VISIBLE : View.GONE); mRecyclerView.setVisibility(mOverflowBubbles.isEmpty() ? View.GONE : View.VISIBLE); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java index c4bbe32e3205..a35a004cdace 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java @@ -324,6 +324,11 @@ public class BubblePositioner { return 0; } + /** Returns whether the IME is visible. */ + public boolean getIsImeVisible() { + return mImeVisible; + } + /** Sets whether the IME is visible. **/ public void setImeVisible(boolean visible, int height) { mImeVisible = visible; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java index be88b3497000..9fabd4247670 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java @@ -80,6 +80,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.policy.ScreenDecorationsUtils; import com.android.internal.protolog.common.ProtoLog; import com.android.internal.util.FrameworkStatsLog; +import com.android.wm.shell.Flags; import com.android.wm.shell.R; import com.android.wm.shell.animation.Interpolators; import com.android.wm.shell.bubbles.BubblesNavBarMotionEventHandler.MotionEventListener; @@ -863,6 +864,7 @@ public class BubbleStackView extends FrameLayout } }; + private boolean mShowingOverflow; private BubbleOverflow mBubbleOverflow; private StackEducationView mStackEduView; private StackEducationView.Manager mStackEducationViewManager; @@ -992,18 +994,12 @@ public class BubbleStackView extends FrameLayout mBubbleOverflow = mBubbleData.getOverflow(); - resetOverflowView(); - mBubbleContainer.addView(mBubbleOverflow.getIconView(), - mBubbleContainer.getChildCount() /* index */, - new FrameLayout.LayoutParams(mPositioner.getBubbleSize(), - mPositioner.getBubbleSize())); - updateOverflow(); - mBubbleOverflow.getIconView().setOnClickListener((View v) -> { - mBubbleData.setShowingOverflow(true); - mBubbleData.setSelectedBubble(mBubbleOverflow); - mBubbleData.setExpanded(true); - }); - + if (Flags.enableOptionalBubbleOverflow()) { + showOverflow(mBubbleData.hasOverflowBubbles()); + } else { + mShowingOverflow = true; // if the flags not on this is always true + setUpOverflow(); + } mScrim = new View(getContext()); mScrim.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO); mScrim.setBackgroundDrawable(new ColorDrawable( @@ -1220,6 +1216,19 @@ public class BubbleStackView extends FrameLayout } }; + private void setUpOverflow() { + resetOverflowView(); + mBubbleContainer.addView(mBubbleOverflow.getIconView(), + mBubbleContainer.getChildCount() /* index */, + new FrameLayout.LayoutParams(mBubbleSize, mBubbleSize)); + updateOverflow(); + mBubbleOverflow.getIconView().setOnClickListener((View v) -> { + mBubbleData.setShowingOverflow(true); + mBubbleData.setSelectedBubble(mBubbleOverflow); + mBubbleData.setExpanded(true); + }); + } + private void setUpDismissView() { if (mDismissView != null) { removeView(mDismissView); @@ -1458,24 +1467,56 @@ public class BubbleStackView extends FrameLayout b.getExpandedView().updateFontSize(); } } - if (mBubbleOverflow != null && mBubbleOverflow.getExpandedView() != null) { + if (mShowingOverflow && mBubbleOverflow != null + && mBubbleOverflow.getExpandedView() != null) { mBubbleOverflow.getExpandedView().updateFontSize(); } } void updateLocale() { - if (mBubbleOverflow != null && mBubbleOverflow.getExpandedView() != null) { + if (mShowingOverflow && mBubbleOverflow != null + && mBubbleOverflow.getExpandedView() != null) { mBubbleOverflow.getExpandedView().updateLocale(); } } private void updateOverflow() { mBubbleOverflow.update(); - mBubbleContainer.reorderView(mBubbleOverflow.getIconView(), - mBubbleContainer.getChildCount() - 1 /* index */); + if (mShowingOverflow) { + mBubbleContainer.reorderView(mBubbleOverflow.getIconView(), + mBubbleContainer.getChildCount() - 1 /* index */); + } updateOverflowVisibility(); } + private void updateOverflowVisibility() { + mBubbleOverflow.setVisible(mShowingOverflow + && (mIsExpanded || mBubbleData.isShowingOverflow()) + ? VISIBLE + : GONE); + } + + private void updateOverflowDotVisibility(boolean expanding) { + if (mShowingOverflow && mBubbleOverflow.showDot()) { + mBubbleOverflow.getIconView().animateDotScale(expanding ? 1 : 0f, () -> { + mBubbleOverflow.setVisible(expanding ? VISIBLE : GONE); + }); + } + } + + /** Sets whether the overflow should be visible or not. */ + public void showOverflow(boolean showOverflow) { + if (!Flags.enableOptionalBubbleOverflow()) return; + if (mShowingOverflow != showOverflow) { + mShowingOverflow = showOverflow; + if (showOverflow) { + setUpOverflow(); + } else if (mBubbleOverflow != null) { + resetOverflowView(); + } + } + } + /** * Handle theme changes. */ @@ -1535,7 +1576,10 @@ public class BubbleStackView extends FrameLayout b.getExpandedView().updateDimensions(); } } - mBubbleOverflow.getIconView().setLayoutParams(new LayoutParams(mBubbleSize, mBubbleSize)); + if (mShowingOverflow) { + mBubbleOverflow.getIconView().setLayoutParams( + new LayoutParams(mBubbleSize, mBubbleSize)); + } mExpandedAnimationController.updateResources(); mStackAnimationController.updateResources(); mDismissView.updateResources(); @@ -1699,7 +1743,7 @@ public class BubbleStackView extends FrameLayout bubble.getIconView().setContentDescription(getResources().getString( R.string.bubble_content_description_single, titleStr, appName)); } else { - final int moreCount = mBubbleContainer.getChildCount() - 1; + final int moreCount = getBubbleCount(); bubble.getIconView().setContentDescription(getResources().getString( R.string.bubble_content_description_stack, titleStr, appName, moreCount)); @@ -1752,7 +1796,8 @@ public class BubbleStackView extends FrameLayout View bubbleOverflowIconView = mBubbleOverflow != null ? mBubbleOverflow.getIconView() : null; - if (bubbleOverflowIconView != null && !mBubbleData.getBubbles().isEmpty()) { + if (mShowingOverflow && bubbleOverflowIconView != null + && !mBubbleData.getBubbles().isEmpty()) { Bubble lastBubble = mBubbleData.getBubbles().get(mBubbleData.getBubbles().size() - 1); View lastBubbleIconView = lastBubble.getIconView(); @@ -1928,20 +1973,6 @@ public class BubbleStackView extends FrameLayout } } - private void updateOverflowVisibility() { - mBubbleOverflow.setVisible((mIsExpanded || mBubbleData.isShowingOverflow()) - ? VISIBLE - : GONE); - } - - private void updateOverflowDotVisibility(boolean expanding) { - if (mBubbleOverflow.showDot()) { - mBubbleOverflow.getIconView().animateDotScale(expanding ? 1 : 0f, () -> { - mBubbleOverflow.setVisible(expanding ? VISIBLE : GONE); - }); - } - } - // via BubbleData.Listener void updateBubble(Bubble bubble) { animateInFlyoutForBubble(bubble); @@ -3428,8 +3459,9 @@ public class BubbleStackView extends FrameLayout * @return the number of bubbles in the stack view. */ public int getBubbleCount() { - // Subtract 1 for the overflow button that is always in the bubble container. - return mBubbleContainer.getChildCount() - 1; + final int childCount = mBubbleContainer.getChildCount(); + // Subtract 1 for the overflow button if it's showing. + return mShowingOverflow ? childCount - 1 : childCount; } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java index 322088b17e63..1d053f9aab35 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java @@ -297,6 +297,15 @@ public interface Bubbles { boolean sensitiveNotificationProtectionActive); /** + * Determines whether Bubbles can show notifications. + * + * <p>Normally bubble notifications are shown by Bubbles, but in some cases the bubble + * notification is suppressed and should be shown by the Notifications pipeline as regular + * notifications. + */ + boolean canShowBubbleNotification(); + + /** * A listener to be notified of bubble state changes, used by launcher to render bubbles in * its process. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl index 66f77fa6f76d..1eff149f2e91 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl @@ -33,7 +33,7 @@ interface IBubbles { oneway void showBubble(in String key, in Rect bubbleBarBounds) = 3; - oneway void removeBubble(in String key) = 4; + oneway void dragBubbleToDismiss(in String key) = 4; oneway void removeAllBubbles() = 5; @@ -47,5 +47,5 @@ interface IBubbles { oneway void setBubbleBarBounds(in Rect bubbleBarBounds) = 10; - oneway void stopBubbleDrag(in String key, in BubbleBarLocation location) = 11; + oneway void stopBubbleDrag(in BubbleBarLocation location) = 11; }
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java index a351cef223b5..123cc7e9d488 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java @@ -356,7 +356,7 @@ public class BubbleBarLayerView extends FrameLayout } /** Updates the expanded view size and position. */ - private void updateExpandedView() { + public void updateExpandedView() { if (mExpandedView == null || mExpandedBubble == null) return; boolean isOverflowExpanded = mExpandedBubble.getKey().equals(BubbleOverflow.KEY); mPositioner.getBubbleBarExpandedViewBounds(mPositioner.isBubbleBarOnLeft(), diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java index 607a3b5423d1..2234041b8c9d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java @@ -347,7 +347,7 @@ public class DividerView extends FrameLayout implements View.OnTouchListener { if (mMoving) { final int position = mSplitLayout.getDividerPosition() + touchPos - mStartPos; mLastDraggingPosition = position; - mSplitLayout.updateDividerBounds(position); + mSplitLayout.updateDividerBounds(position, true /* shouldUseParallaxEffect */); } break; case MotionEvent.ACTION_UP: diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java index 30eb8b5d2f05..de016d3ae400 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java @@ -31,7 +31,6 @@ import android.animation.ValueAnimator; import android.app.ActivityManager; import android.content.Context; import android.content.res.Configuration; -import android.graphics.Color; import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.drawable.Drawable; @@ -57,7 +56,13 @@ import com.android.wm.shell.common.SurfaceUtils; import java.util.function.Consumer; /** - * Handles split decor like showing resizing hint for a specific split. + * Handles additional layers over a running task in a split pair, for example showing a veil with an + * app icon when the task is being resized (usually to hide weird layouts while the app is being + * stretched). One SplitDecorManager is initialized on each window. + * <br> + * Currently, we show a veil when: + * a) Task is resizing down from a fullscreen window. + * b) Task is being stretched past its original bounds. */ public class SplitDecorManager extends WindowlessWindowManager { private static final String TAG = SplitDecorManager.class.getSimpleName(); @@ -78,7 +83,11 @@ public class SplitDecorManager extends WindowlessWindowManager { private boolean mShown; private boolean mIsResizing; - private final Rect mOldBounds = new Rect(); + /** The original bounds of the main task, captured at the beginning of a resize transition. */ + private final Rect mOldMainBounds = new Rect(); + /** The original bounds of the side task, captured at the beginning of a resize transition. */ + private final Rect mOldSideBounds = new Rect(); + /** The current bounds of the main task, mid-resize. */ private final Rect mResizingBounds = new Rect(); private final Rect mTempRect = new Rect(); private ValueAnimator mFadeAnimator; @@ -184,29 +193,38 @@ public class SplitDecorManager extends WindowlessWindowManager { mResizingIconView = null; mIsResizing = false; mShown = false; - mOldBounds.setEmpty(); + mOldMainBounds.setEmpty(); + mOldSideBounds.setEmpty(); mResizingBounds.setEmpty(); } /** Showing resizing hint. */ public void onResizing(ActivityManager.RunningTaskInfo resizingTask, Rect newBounds, Rect sideBounds, SurfaceControl.Transaction t, int offsetX, int offsetY, - boolean immediately) { + boolean immediately, float[] veilColor) { if (mResizingIconView == null) { return; } if (!mIsResizing) { mIsResizing = true; - mOldBounds.set(newBounds); + mOldMainBounds.set(newBounds); + mOldSideBounds.set(sideBounds); } mResizingBounds.set(newBounds); mOffsetX = offsetX; mOffsetY = offsetY; - final boolean show = - newBounds.width() > mOldBounds.width() || newBounds.height() > mOldBounds.height(); - final boolean update = show != mShown; + // Show a veil when: + // a) Task is resizing down from a fullscreen window. + // b) Task is being stretched past its original bounds. + final boolean isResizingDownFromFullscreen = + mOldSideBounds.width() <= 1 || mOldSideBounds.height() <= 1; + final boolean isStretchingPastOriginalBounds = + newBounds.width() > mOldMainBounds.width() + || newBounds.height() > mOldMainBounds.height(); + final boolean showVeil = isResizingDownFromFullscreen || isStretchingPastOriginalBounds; + final boolean update = showVeil != mShown; if (update && mFadeAnimator != null && mFadeAnimator.isRunning()) { // If we need to animate and animator still running, cancel it before we ensure both // background and icon surfaces are non null for next animation. @@ -216,18 +234,18 @@ public class SplitDecorManager extends WindowlessWindowManager { if (mBackgroundLeash == null) { mBackgroundLeash = SurfaceUtils.makeColorLayer(mHostLeash, RESIZING_BACKGROUND_SURFACE_NAME, mSurfaceSession); - t.setColor(mBackgroundLeash, getResizingBackgroundColor(resizingTask)) + t.setColor(mBackgroundLeash, veilColor) .setLayer(mBackgroundLeash, Integer.MAX_VALUE - 1); } if (mGapBackgroundLeash == null && !immediately) { final boolean isLandscape = newBounds.height() == sideBounds.height(); - final int left = isLandscape ? mOldBounds.width() : 0; - final int top = isLandscape ? 0 : mOldBounds.height(); + final int left = isLandscape ? mOldMainBounds.width() : 0; + final int top = isLandscape ? 0 : mOldMainBounds.height(); mGapBackgroundLeash = SurfaceUtils.makeColorLayer(mHostLeash, GAP_BACKGROUND_SURFACE_NAME, mSurfaceSession); // Fill up another side bounds area. - t.setColor(mGapBackgroundLeash, getResizingBackgroundColor(resizingTask)) + t.setColor(mGapBackgroundLeash, veilColor) .setLayer(mGapBackgroundLeash, Integer.MAX_VALUE - 2) .setPosition(mGapBackgroundLeash, left, top) .setWindowCrop(mGapBackgroundLeash, sideBounds.width(), sideBounds.height()); @@ -251,12 +269,12 @@ public class SplitDecorManager extends WindowlessWindowManager { if (update) { if (immediately) { - t.setVisibility(mBackgroundLeash, show); - t.setVisibility(mIconLeash, show); + t.setVisibility(mBackgroundLeash, showVeil); + t.setVisibility(mIconLeash, showVeil); } else { - startFadeAnimation(show, false, null); + startFadeAnimation(showVeil, false, null); } - mShown = show; + mShown = showVeil; } } @@ -309,7 +327,8 @@ public class SplitDecorManager extends WindowlessWindowManager { mIsResizing = false; mOffsetX = 0; mOffsetY = 0; - mOldBounds.setEmpty(); + mOldMainBounds.setEmpty(); + mOldSideBounds.setEmpty(); mResizingBounds.setEmpty(); if (mFadeAnimator != null && mFadeAnimator.isRunning()) { if (!mShown) { @@ -346,14 +365,14 @@ public class SplitDecorManager extends WindowlessWindowManager { /** Screenshot host leash and attach on it if meet some conditions */ public void screenshotIfNeeded(SurfaceControl.Transaction t) { - if (!mShown && mIsResizing && !mOldBounds.equals(mResizingBounds)) { + if (!mShown && mIsResizing && !mOldMainBounds.equals(mResizingBounds)) { if (mScreenshotAnimator != null && mScreenshotAnimator.isRunning()) { mScreenshotAnimator.cancel(); } else if (mScreenshot != null) { t.remove(mScreenshot); } - mTempRect.set(mOldBounds); + mTempRect.set(mOldMainBounds); mTempRect.offsetTo(0, 0); mScreenshot = ScreenshotUtils.takeScreenshot(t, mHostLeash, mTempRect, Integer.MAX_VALUE - 1); @@ -364,7 +383,7 @@ public class SplitDecorManager extends WindowlessWindowManager { public void setScreenshotIfNeeded(SurfaceControl screenshot, SurfaceControl.Transaction t) { if (screenshot == null || !screenshot.isValid()) return; - if (!mShown && mIsResizing && !mOldBounds.equals(mResizingBounds)) { + if (!mShown && mIsResizing && !mOldMainBounds.equals(mResizingBounds)) { if (mScreenshotAnimator != null && mScreenshotAnimator.isRunning()) { mScreenshotAnimator.cancel(); } else if (mScreenshot != null) { @@ -465,9 +484,4 @@ public class SplitDecorManager extends WindowlessWindowManager { mIcon = null; } } - - private static float[] getResizingBackgroundColor(ActivityManager.RunningTaskInfo taskInfo) { - final int taskBgColor = taskInfo.taskDescription.getBackgroundColor(); - return Color.valueOf(taskBgColor == -1 ? Color.WHITE : taskBgColor).getComponents(); - } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java index 2ea32f44a78b..8331654839c9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java @@ -496,10 +496,10 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange * Updates bounds with the passing position. Usually used to update recording bounds while * performing animation or dragging divider bar to resize the splits. */ - void updateDividerBounds(int position) { + void updateDividerBounds(int position, boolean shouldUseParallaxEffect) { updateBounds(position); mSplitLayoutHandler.onLayoutSizeChanging(this, mSurfaceEffectPolicy.mParallaxOffset.x, - mSurfaceEffectPolicy.mParallaxOffset.y); + mSurfaceEffectPolicy.mParallaxOffset.y, shouldUseParallaxEffect); } void setDividerPosition(int position, boolean applyLayoutChange) { @@ -647,7 +647,9 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange .setDuration(duration); mDividerFlingAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); mDividerFlingAnimator.addUpdateListener( - animation -> updateDividerBounds((int) animation.getAnimatedValue())); + animation -> updateDividerBounds( + (int) animation.getAnimatedValue(), false /* shouldUseParallaxEffect */) + ); mDividerFlingAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { @@ -897,7 +899,8 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange * @see #applySurfaceChanges(SurfaceControl.Transaction, SurfaceControl, SurfaceControl, * SurfaceControl, SurfaceControl, boolean) */ - void onLayoutSizeChanging(SplitLayout layout, int offsetX, int offsetY); + void onLayoutSizeChanging(SplitLayout layout, int offsetX, int offsetY, + boolean shouldUseParallaxEffect); /** * Calls when finish resizing the split bounds. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java index f9259e79472e..e8226051b672 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java @@ -16,8 +16,6 @@ package com.android.wm.shell.common.split; -import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_ALL_KINDS_WITH_ALL_PINNED; - import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_ACTIVITY_TYPES; import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_WINDOWING_MODES; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; @@ -26,25 +24,18 @@ import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSIT import android.app.ActivityManager; import android.app.PendingIntent; -import android.content.ComponentName; import android.content.Intent; -import android.content.pm.LauncherApps; -import android.content.pm.ShortcutInfo; import android.content.res.Configuration; import android.content.res.Resources; +import android.graphics.Color; import android.graphics.Rect; -import android.os.UserHandle; -import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.internal.util.ArrayUtils; import com.android.wm.shell.Flags; import com.android.wm.shell.ShellTaskOrganizer; -import java.util.Arrays; -import java.util.List; - /** Helper utility class for split screen components to use. */ public class SplitScreenUtils { /** Reverse the split position. */ @@ -137,4 +128,10 @@ public class SplitScreenUtils { return isLandscape; } } + + /** Returns the specified background color that matches a RunningTaskInfo. */ + public static Color getResizingBackgroundColor(ActivityManager.RunningTaskInfo taskInfo) { + final int taskBgColor = taskInfo.taskDescription.getBackgroundColor(); + return Color.valueOf(taskBgColor == -1 ? Color.WHITE : taskBgColor); + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java index 414a9d1151ac..01364d1de279 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java @@ -133,6 +133,7 @@ public abstract class Pip2Module { PipBoundsAlgorithm pipBoundsAlgorithm, @NonNull PipBoundsState pipBoundsState, @NonNull PipTransitionState pipTransitionState, + @NonNull PipScheduler pipScheduler, @NonNull SizeSpecSource sizeSpecSource, PipMotionHelper pipMotionHelper, FloatingContentCoordinator floatingContentCoordinator, @@ -140,7 +141,7 @@ public abstract class Pip2Module { @ShellMainThread ShellExecutor mainExecutor, Optional<PipPerfHintController> pipPerfHintControllerOptional) { return new PipTouchHandler(context, shellInit, menuPhoneController, pipBoundsAlgorithm, - pipBoundsState, pipTransitionState, sizeSpecSource, pipMotionHelper, + pipBoundsState, pipTransitionState, pipScheduler, sizeSpecSource, pipMotionHelper, floatingContentCoordinator, pipUiEventLogger, mainExecutor, pipPerfHintControllerOptional); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java index 59d696918448..4bb10dfdf8c6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java @@ -22,11 +22,11 @@ import static android.content.pm.ActivityInfo.CONFIG_ASSETS_PATHS; import static android.content.pm.ActivityInfo.CONFIG_UI_MODE; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; -import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME; import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT; +import static com.android.wm.shell.common.split.SplitScreenUtils.getResizingBackgroundColor; import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_BOTTOM; import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_LEFT; import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_RIGHT; @@ -41,7 +41,6 @@ import android.app.StatusBarManager; import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; -import android.graphics.Color; import android.graphics.Insets; import android.graphics.Rect; import android.graphics.Region; @@ -278,7 +277,7 @@ public class DragLayout extends LinearLayout final int activityType = taskInfo1.getActivityType(); if (activityType == ACTIVITY_TYPE_STANDARD) { Drawable icon1 = mIconProvider.getIcon(taskInfo1.topActivityInfo); - int bgColor1 = getResizingBackgroundColor(taskInfo1); + int bgColor1 = getResizingBackgroundColor(taskInfo1).toArgb(); mDropZoneView1.setAppInfo(bgColor1, icon1); mDropZoneView2.setAppInfo(bgColor1, icon1); updateDropZoneSizes(null, null); // passing null splits the views evenly @@ -298,10 +297,10 @@ public class DragLayout extends LinearLayout mSplitScreenController.getTaskInfo(SPLIT_POSITION_BOTTOM_OR_RIGHT); if (topOrLeftTask != null && bottomOrRightTask != null) { Drawable topOrLeftIcon = mIconProvider.getIcon(topOrLeftTask.topActivityInfo); - int topOrLeftColor = getResizingBackgroundColor(topOrLeftTask); + int topOrLeftColor = getResizingBackgroundColor(topOrLeftTask).toArgb(); Drawable bottomOrRightIcon = mIconProvider.getIcon( bottomOrRightTask.topActivityInfo); - int bottomOrRightColor = getResizingBackgroundColor(bottomOrRightTask); + int bottomOrRightColor = getResizingBackgroundColor(bottomOrRightTask).toArgb(); mDropZoneView1.setAppInfo(topOrLeftColor, topOrLeftIcon); mDropZoneView2.setAppInfo(bottomOrRightColor, bottomOrRightIcon); } @@ -556,11 +555,6 @@ public class DragLayout extends LinearLayout } } - private static int getResizingBackgroundColor(ActivityManager.RunningTaskInfo taskInfo) { - final int taskBgColor = taskInfo.taskDescription.getBackgroundColor(); - return Color.valueOf(taskBgColor == -1 ? Color.WHITE : taskBgColor).toArgb(); - } - /** * Dumps information about this drag layout. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index e885262658f4..e1657f99639d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -854,7 +854,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mPipUiEventLoggerLogger.log(uiEventEnum); ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, - "onTaskAppeared: %s, state=%s", mTaskInfo.topActivity, mPipTransitionState); + "onTaskAppeared: %s, state=%s, taskId=%s", mTaskInfo.topActivity, + mPipTransitionState, mTaskInfo.taskId); if (mPipTransitionState.getInSwipePipToHomeTransition()) { if (!mWaitForFixedRotation) { onEndOfSwipePipToHomeTransition(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java index a097a0ffa47d..be10151ca5aa 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java @@ -58,7 +58,8 @@ import java.util.function.Consumer; * A helper to animate and manipulate the PiP. */ public class PipMotionHelper implements PipAppOpsListener.Callback, - FloatingContentCoordinator.FloatingContent { + FloatingContentCoordinator.FloatingContent, + PipTransitionState.PipTransitionStateChangedListener { private static final String TAG = "PipMotionHelper"; private static final String FLING_BOUNDS_CHANGE = "fling_bounds_change"; private static final boolean DEBUG = false; @@ -181,7 +182,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, } }; mPipTransitionState = pipTransitionState; - mPipTransitionState.addPipTransitionStateChangedListener(this::onPipTransitionStateChanged); + mPipTransitionState.addPipTransitionStateChangedListener(this); } void init() { @@ -687,7 +688,8 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, // setAnimatingToBounds(toBounds); } - private void onPipTransitionStateChanged(@PipTransitionState.TransitionState int oldState, + @Override + public void onPipTransitionStateChanged(@PipTransitionState.TransitionState int oldState, @PipTransitionState.TransitionState int newState, @Nullable Bundle extra) { switch (newState) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java index 04cf350ddd3e..b55a41d8808f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java @@ -24,6 +24,7 @@ import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; import android.hardware.input.InputManager; +import android.os.Bundle; import android.os.Looper; import android.view.BatchedInputEventReceiver; import android.view.Choreographer; @@ -32,6 +33,7 @@ import android.view.InputEvent; import android.view.InputEventReceiver; import android.view.InputMonitor; import android.view.MotionEvent; +import android.view.SurfaceControl; import android.view.ViewConfiguration; import androidx.annotation.VisibleForTesting; @@ -51,16 +53,20 @@ import java.util.function.Consumer; * Helper on top of PipTouchHandler that handles inputs OUTSIDE of the PIP window, which is used to * trigger dynamic resize. */ -public class PipResizeGestureHandler { +public class PipResizeGestureHandler implements + PipTransitionState.PipTransitionStateChangedListener { private static final String TAG = "PipResizeGestureHandler"; private static final int PINCH_RESIZE_SNAP_DURATION = 250; private static final float PINCH_RESIZE_AUTO_MAX_RATIO = 0.9f; + private static final String RESIZE_BOUNDS_CHANGE = "resize_bounds_change"; private final Context mContext; private final PipBoundsAlgorithm mPipBoundsAlgorithm; private final PipBoundsState mPipBoundsState; private final PipTouchState mPipTouchState; + private final PipScheduler mPipScheduler; + private final PipTransitionState mPipTransitionState; private final PhonePipMenuController mPhonePipMenuController; private final PipUiEventLogger mPipUiEventLogger; private final PipPinchResizingAlgorithm mPinchResizingAlgorithm; @@ -88,6 +94,7 @@ public class PipResizeGestureHandler { private boolean mIsSysUiStateValid; private boolean mThresholdCrossed; private boolean mOngoingPinchToResize = false; + private boolean mWaitingForBoundsChangeTransition = false; private float mAngle = 0; int mFirstIndex = -1; int mSecondIndex = -1; @@ -104,11 +111,17 @@ public class PipResizeGestureHandler { private int mCtrlType; private int mOhmOffset; - public PipResizeGestureHandler(Context context, PipBoundsAlgorithm pipBoundsAlgorithm, - PipBoundsState pipBoundsState, PipTouchState pipTouchState, + public PipResizeGestureHandler(Context context, + PipBoundsAlgorithm pipBoundsAlgorithm, + PipBoundsState pipBoundsState, + PipTouchState pipTouchState, + PipScheduler pipScheduler, + PipTransitionState pipTransitionState, Runnable updateMovementBoundsRunnable, - PipUiEventLogger pipUiEventLogger, PhonePipMenuController menuActivityController, - ShellExecutor mainExecutor, @Nullable PipPerfHintController pipPerfHintController) { + PipUiEventLogger pipUiEventLogger, + PhonePipMenuController menuActivityController, + ShellExecutor mainExecutor, + @Nullable PipPerfHintController pipPerfHintController) { mContext = context; mDisplayId = context.getDisplayId(); mMainExecutor = mainExecutor; @@ -116,6 +129,11 @@ public class PipResizeGestureHandler { mPipBoundsAlgorithm = pipBoundsAlgorithm; mPipBoundsState = pipBoundsState; mPipTouchState = pipTouchState; + mPipScheduler = pipScheduler; + + mPipTransitionState = pipTransitionState; + mPipTransitionState.addPipTransitionStateChangedListener(this); + mUpdateMovementBoundsRunnable = updateMovementBoundsRunnable; mPhonePipMenuController = menuActivityController; mPipUiEventLogger = pipUiEventLogger; @@ -125,6 +143,7 @@ public class PipResizeGestureHandler { mUserResizeBounds.set(rect); // mMotionHelper.synchronizePinnedStackBounds(); mUpdateMovementBoundsRunnable.run(); + mPipBoundsState.setBounds(rect); resetState(); }; } @@ -202,7 +221,7 @@ public class PipResizeGestureHandler { @VisibleForTesting void onInputEvent(InputEvent ev) { if (!mEnablePinchResize) { - // No need to handle anything if neither form of resizing is enabled. + // No need to handle anything if resizing isn't enabled. return; } @@ -227,7 +246,7 @@ public class PipResizeGestureHandler { } } - if (mEnablePinchResize && mOngoingPinchToResize) { + if (mOngoingPinchToResize) { onPinchResize(mv); } } @@ -249,13 +268,11 @@ public class PipResizeGestureHandler { } boolean willStartResizeGesture(MotionEvent ev) { - if (isInValidSysUiState()) { - if (ev.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) { - if (mEnablePinchResize && ev.getPointerCount() == 2) { - onPinchResize(ev); - mOngoingPinchToResize = mAllowGesture; - return mAllowGesture; - } + if (ev.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) { + if (mEnablePinchResize && ev.getPointerCount() == 2) { + onPinchResize(ev); + mOngoingPinchToResize = mAllowGesture; + return mAllowGesture; } } return false; @@ -284,7 +301,6 @@ public class PipResizeGestureHandler { mSecondIndex = -1; mAllowGesture = false; finishResize(); - cleanUpHighPerfSessionMaybe(); } if (ev.getPointerCount() != 2) { @@ -347,10 +363,7 @@ public class PipResizeGestureHandler { mDownSecondPoint, mLastPoint, mLastSecondPoint, mMinSize, mMaxSize, mDownBounds, mLastResizeBounds); - /* - mPipTaskOrganizer.scheduleUserResizePip(mDownBounds, mLastResizeBounds, - mAngle, null); - */ + mPipScheduler.scheduleUserResizePip(mLastResizeBounds, mAngle); mPipBoundsState.setHasUserResizedPip(true); } } @@ -399,57 +412,43 @@ public class PipResizeGestureHandler { } private void finishResize() { - if (!mLastResizeBounds.isEmpty()) { - // Pinch-to-resize needs to re-calculate snap fraction and animate to the snapped - // position correctly. Drag-resize does not need to move, so just finalize resize. - if (mOngoingPinchToResize) { - final Rect startBounds = new Rect(mLastResizeBounds); - // If user resize is pretty close to max size, just auto resize to max. - if (mLastResizeBounds.width() >= PINCH_RESIZE_AUTO_MAX_RATIO * mMaxSize.x - || mLastResizeBounds.height() >= PINCH_RESIZE_AUTO_MAX_RATIO * mMaxSize.y) { - resizeRectAboutCenter(mLastResizeBounds, mMaxSize.x, mMaxSize.y); - } + if (mLastResizeBounds.isEmpty()) { + resetState(); + } + if (!mOngoingPinchToResize) { + return; + } + final Rect startBounds = new Rect(mLastResizeBounds); - // If user resize is smaller than min size, auto resize to min - if (mLastResizeBounds.width() < mMinSize.x - || mLastResizeBounds.height() < mMinSize.y) { - resizeRectAboutCenter(mLastResizeBounds, mMinSize.x, mMinSize.y); - } + // If user resize is pretty close to max size, just auto resize to max. + if (mLastResizeBounds.width() >= PINCH_RESIZE_AUTO_MAX_RATIO * mMaxSize.x + || mLastResizeBounds.height() >= PINCH_RESIZE_AUTO_MAX_RATIO * mMaxSize.y) { + resizeRectAboutCenter(mLastResizeBounds, mMaxSize.x, mMaxSize.y); + } - // get the current movement bounds - final Rect movementBounds = mPipBoundsAlgorithm - .getMovementBounds(mLastResizeBounds); - - // snap mLastResizeBounds to the correct edge based on movement bounds - snapToMovementBoundsEdge(mLastResizeBounds, movementBounds); - - final float snapFraction = mPipBoundsAlgorithm.getSnapFraction( - mLastResizeBounds, movementBounds); - mPipBoundsAlgorithm.applySnapFraction(mLastResizeBounds, snapFraction); - - // disable any touch events beyond resizing too - mPipTouchState.setAllowInputEvents(false); - - /* - mPipTaskOrganizer.scheduleAnimateResizePip(startBounds, mLastResizeBounds, - PINCH_RESIZE_SNAP_DURATION, mAngle, mUpdateResizeBoundsCallback, () -> { - // enable touch events - mPipTouchState.setAllowInputEvents(true); - }); - */ - } else { - /* - mPipTaskOrganizer.scheduleFinishResizePip(mLastResizeBounds, - TRANSITION_DIRECTION_USER_RESIZE, - mUpdateResizeBoundsCallback); - */ - } - final float magnetRadiusPercent = (float) mLastResizeBounds.width() / mMinSize.x / 2.f; - mPipUiEventLogger.log( - PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_RESIZE); - } else { - resetState(); + // If user resize is smaller than min size, auto resize to min + if (mLastResizeBounds.width() < mMinSize.x + || mLastResizeBounds.height() < mMinSize.y) { + resizeRectAboutCenter(mLastResizeBounds, mMinSize.x, mMinSize.y); } + + // get the current movement bounds + final Rect movementBounds = mPipBoundsAlgorithm + .getMovementBounds(mLastResizeBounds); + + // snap mLastResizeBounds to the correct edge based on movement bounds + snapToMovementBoundsEdge(mLastResizeBounds, movementBounds); + + final float snapFraction = mPipBoundsAlgorithm.getSnapFraction( + mLastResizeBounds, movementBounds); + mPipBoundsAlgorithm.applySnapFraction(mLastResizeBounds, snapFraction); + + // Update the transition state to schedule a resize transition. + Bundle extra = new Bundle(); + extra.putBoolean(RESIZE_BOUNDS_CHANGE, true); + mPipTransitionState.setState(PipTransitionState.SCHEDULED_BOUNDS_CHANGE, extra); + + mPipUiEventLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_RESIZE); } private void resetState() { @@ -509,6 +508,40 @@ public class PipResizeGestureHandler { rect.set(l, t, r, b); } + @Override + public void onPipTransitionStateChanged(@PipTransitionState.TransitionState int oldState, + @PipTransitionState.TransitionState int newState, @Nullable Bundle extra) { + switch (newState) { + case PipTransitionState.SCHEDULED_BOUNDS_CHANGE: + if (!extra.getBoolean(RESIZE_BOUNDS_CHANGE)) break; + mWaitingForBoundsChangeTransition = true; + mPipScheduler.scheduleAnimateResizePip(mLastResizeBounds); + break; + case PipTransitionState.CHANGING_PIP_BOUNDS: + if (!mWaitingForBoundsChangeTransition) break; + + // If bounds change transition was scheduled from this class, handle leash updates. + mWaitingForBoundsChangeTransition = false; + + SurfaceControl.Transaction startTx = extra.getParcelable( + PipTransition.PIP_START_TX, SurfaceControl.Transaction.class); + Rect destinationBounds = extra.getParcelable( + PipTransition.PIP_DESTINATION_BOUNDS, Rect.class); + startTx.setPosition(mPipTransitionState.mPinnedTaskLeash, + destinationBounds.left, destinationBounds.top); + startTx.apply(); + + // All motion operations have actually finished, so make bounds cache updates. + cleanUpHighPerfSessionMaybe(); + + // Setting state to CHANGED_PIP_BOUNDS applies finishTx and notifies Core. + mPipTransitionState.setState(PipTransitionState.CHANGED_PIP_BOUNDS); + + mUpdateResizeBoundsCallback.accept(destinationBounds); + break; + } + } + /** * Dumps the {@link PipResizeGestureHandler} state. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java index c5b0de31f104..49475077211f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java @@ -24,6 +24,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.graphics.Matrix; import android.graphics.Rect; import android.view.SurfaceControl; import android.window.WindowContainerTransaction; @@ -165,6 +166,16 @@ public class PipScheduler { * {@link WindowContainerTransaction}. */ public void scheduleUserResizePip(Rect toBounds) { + scheduleUserResizePip(toBounds, 0f /* degrees */); + } + + /** + * Directly perform a scaled matrix transformation on the leash. This will not perform any + * {@link WindowContainerTransaction}. + * + * @param degrees the angle to rotate the bounds to. + */ + public void scheduleUserResizePip(Rect toBounds, float degrees) { if (toBounds.isEmpty()) { ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: Attempted to user resize PIP to empty bounds, aborting.", TAG); @@ -172,7 +183,16 @@ public class PipScheduler { } SurfaceControl leash = mPipTransitionState.mPinnedTaskLeash; final SurfaceControl.Transaction tx = new SurfaceControl.Transaction(); - tx.setPosition(leash, toBounds.left, toBounds.top); + + Matrix transformTensor = new Matrix(); + final float[] mMatrixTmp = new float[9]; + final float scale = (float) toBounds.width() / mPipBoundsState.getBounds().width(); + + transformTensor.setScale(scale, scale); + transformTensor.postTranslate(toBounds.left, toBounds.top); + transformTensor.postRotate(degrees, toBounds.centerX(), toBounds.centerY()); + + tx.setMatrix(leash, transformTensor, mMatrixTmp); tx.apply(); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java index 9c6e3ea494fa..319d1999a272 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java @@ -73,7 +73,7 @@ import java.util.Optional; * Manages all the touch handling for PIP on the Phone, including moving, dismissing and expanding * the PIP. */ -public class PipTouchHandler { +public class PipTouchHandler implements PipTransitionState.PipTransitionStateChangedListener { private static final String TAG = "PipTouchHandler"; private static final float DEFAULT_STASH_VELOCITY_THRESHOLD = 18000.f; @@ -84,6 +84,7 @@ public class PipTouchHandler { private final PipBoundsAlgorithm mPipBoundsAlgorithm; @NonNull private final PipBoundsState mPipBoundsState; @NonNull private final PipTransitionState mPipTransitionState; + @NonNull private final PipScheduler mPipScheduler; @NonNull private final SizeSpecSource mSizeSpecSource; private final PipUiEventLogger mPipUiEventLogger; private final PipDismissTargetHandler mPipDismissTargetHandler; @@ -173,6 +174,7 @@ public class PipTouchHandler { PipBoundsAlgorithm pipBoundsAlgorithm, @NonNull PipBoundsState pipBoundsState, @NonNull PipTransitionState pipTransitionState, + @NonNull PipScheduler pipScheduler, @NonNull SizeSpecSource sizeSpecSource, PipMotionHelper pipMotionHelper, FloatingContentCoordinator floatingContentCoordinator, @@ -188,6 +190,7 @@ public class PipTouchHandler { mPipTransitionState = pipTransitionState; mPipTransitionState.addPipTransitionStateChangedListener(this::onPipTransitionStateChanged); + mPipScheduler = pipScheduler; mSizeSpecSource = sizeSpecSource; mMenuController = menuController; mPipUiEventLogger = pipUiEventLogger; @@ -213,10 +216,10 @@ public class PipTouchHandler { }, menuController::hideMenu, mainExecutor); - mPipResizeGestureHandler = - new PipResizeGestureHandler(context, pipBoundsAlgorithm, pipBoundsState, - mTouchState, this::updateMovementBounds, pipUiEventLogger, - menuController, mainExecutor, mPipPerfHintController); + mPipResizeGestureHandler = new PipResizeGestureHandler(context, pipBoundsAlgorithm, + pipBoundsState, mTouchState, mPipScheduler, mPipTransitionState, + this::updateMovementBounds, pipUiEventLogger, menuController, mainExecutor, + mPipPerfHintController); mPipBoundsState.addOnAspectRatioChangedCallback(this::updateMinMaxSize); if (PipUtils.isPip2ExperimentEnabled()) { @@ -1075,7 +1078,8 @@ public class PipTouchHandler { mPipResizeGestureHandler.setOhmOffset(offset); } - private void onPipTransitionStateChanged(@PipTransitionState.TransitionState int oldState, + @Override + public void onPipTransitionStateChanged(@PipTransitionState.TransitionState int oldState, @PipTransitionState.TransitionState int newState, @Nullable Bundle extra) { switch (newState) { 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 b10176df5826..4299088a51f0 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 @@ -43,10 +43,12 @@ import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSIT import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED; import static com.android.wm.shell.common.split.SplitScreenConstants.splitPositionToString; +import static com.android.wm.shell.common.split.SplitScreenUtils.getResizingBackgroundColor; import static com.android.wm.shell.common.split.SplitScreenUtils.reverseSplitPosition; import static com.android.wm.shell.common.split.SplitScreenUtils.splitFailureMessage; import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN; import static com.android.wm.shell.shared.TransitionUtil.isClosingType; +import static com.android.wm.shell.shared.TransitionUtil.isOpeningMode; import static com.android.wm.shell.shared.TransitionUtil.isOpeningType; import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN; import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE; @@ -67,6 +69,7 @@ import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP; import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_UNKNOWN; import static com.android.wm.shell.splitscreen.SplitScreenController.exitReasonToString; +import static com.android.wm.shell.transition.MixedTransitionHelper.getPipReplacingChange; import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS; import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE; import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_PAIR_OPEN; @@ -2386,14 +2389,20 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } @Override - public void onLayoutSizeChanging(SplitLayout layout, int offsetX, int offsetY) { + public void onLayoutSizeChanging(SplitLayout layout, int offsetX, int offsetY, + boolean shouldUseParallaxEffect) { final SurfaceControl.Transaction t = mTransactionPool.acquire(); t.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId()); - updateSurfaceBounds(layout, t, true /* applyResizingOffset */); + updateSurfaceBounds(layout, t, shouldUseParallaxEffect); getMainStageBounds(mTempRect1); getSideStageBounds(mTempRect2); - mMainStage.onResizing(mTempRect1, mTempRect2, t, offsetX, offsetY, mShowDecorImmediately); - mSideStage.onResizing(mTempRect2, mTempRect1, t, offsetX, offsetY, mShowDecorImmediately); + // TODO (b/307490004): "commonColor" below is a temporary fix to ensure the colors on both + // sides match. When b/307490004 is fixed, this code can be reverted. + float[] commonColor = getResizingBackgroundColor(mSideStage.mRootTaskInfo).getComponents(); + mMainStage.onResizing( + mTempRect1, mTempRect2, t, offsetX, offsetY, mShowDecorImmediately, commonColor); + mSideStage.onResizing( + mTempRect2, mTempRect1, t, offsetX, offsetY, mShowDecorImmediately, commonColor); t.apply(); mTransactionPool.release(t); } @@ -2836,7 +2845,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSplitLayout.setFreezeDividerWindow(false); final StageChangeRecord record = new StageChangeRecord(); final int transitType = info.getType(); - boolean hasEnteringPip = false; + TransitionInfo.Change pipChange = null; for (int iC = 0; iC < info.getChanges().size(); ++iC) { final TransitionInfo.Change change = info.getChanges().get(iC); if (change.getMode() == TRANSIT_CHANGE @@ -2847,7 +2856,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } if (mMixedHandler.isEnteringPip(change, transitType)) { - hasEnteringPip = true; + pipChange = change; } final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo(); @@ -2899,9 +2908,19 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } } - if (hasEnteringPip) { + if (pipChange != null) { + TransitionInfo.Change pipReplacingChange = getPipReplacingChange(info, pipChange, + mMainStage.mRootTaskInfo.taskId, mSideStage.mRootTaskInfo.taskId, + getSplitItemStage(pipChange.getLastParent())); + if (pipReplacingChange != null) { + // Set an enter transition for when startAnimation gets called again + mSplitTransitions.setEnterTransition(transition, /*remoteTransition*/ null, + TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE, /*resizeAnim*/ false); + } + mMixedHandler.animatePendingEnterPipFromSplit(transition, info, - startTransaction, finishTransaction, finishCallback); + startTransaction, finishTransaction, finishCallback, + pipReplacingChange != null); notifySplitAnimationFinished(); return true; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java index 1e305c5dbbcf..0f3d6cade95a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java @@ -177,9 +177,11 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { @Override @CallSuper public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) { - ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTaskAppeared: task=%d taskParent=%d rootTask=%d", + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTaskAppeared: taskId=%d taskParent=%d rootTask=%d " + + "taskActivity=%s", taskInfo.taskId, taskInfo.parentTaskId, - mRootTaskInfo != null ? mRootTaskInfo.taskId : -1); + mRootTaskInfo != null ? mRootTaskInfo.taskId : -1, + taskInfo.baseActivity); if (mRootTaskInfo == null) { mRootLeash = leash; mRootTaskInfo = taskInfo; @@ -213,6 +215,8 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { @Override @CallSuper public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTaskInfoChanged: taskId=%d taskAct=%s", + taskInfo.taskId, taskInfo.baseActivity); mWindowDecorViewModel.ifPresent(viewModel -> viewModel.onTaskInfoChanged(taskInfo)); if (mRootTaskInfo.taskId == taskInfo.taskId) { // Inflates split decor view only when the root task is visible. @@ -310,10 +314,10 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { } void onResizing(Rect newBounds, Rect sideBounds, SurfaceControl.Transaction t, int offsetX, - int offsetY, boolean immediately) { + int offsetY, boolean immediately, float[] veilColor) { if (mSplitDecorManager != null && mRootTaskInfo != null) { mSplitDecorManager.onResizing(mRootTaskInfo, newBounds, sideBounds, t, offsetX, - offsetY, immediately); + offsetY, immediately, veilColor); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java index 968b27b62490..bcacecbd8981 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java @@ -77,6 +77,7 @@ public class DefaultMixedHandler implements MixedTransitionHandler, private ActivityEmbeddingController mActivityEmbeddingController; abstract static class MixedTransition { + /** Entering Pip from split, breaks split. */ static final int TYPE_ENTER_PIP_FROM_SPLIT = 1; /** Both the display and split-state (enter/exit) is changing */ @@ -103,6 +104,9 @@ public class DefaultMixedHandler implements MixedTransitionHandler, /** Enter pip from one of the Activity Embedding windows. */ static final int TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING = 9; + /** Entering Pip from split, but replace the Pip stage instead of breaking split. */ + static final int TYPE_ENTER_PIP_REPLACE_FROM_SPLIT = 10; + /** The default animation for this mixed transition. */ static final int ANIM_TYPE_DEFAULT = 0; @@ -484,9 +488,11 @@ public class DefaultMixedHandler implements MixedTransitionHandler, // TODO(b/287704263): Remove when split/mixed are reversed. public boolean animatePendingEnterPipFromSplit(IBinder transition, TransitionInfo info, SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT, - Transitions.TransitionFinishCallback finishCallback) { - final MixedTransition mixed = createDefaultMixedTransition( - MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT, transition); + Transitions.TransitionFinishCallback finishCallback, boolean replacingPip) { + int type = replacingPip + ? MixedTransition.TYPE_ENTER_PIP_REPLACE_FROM_SPLIT + : MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT; + final MixedTransition mixed = createDefaultMixedTransition(type, transition); mActiveTransitions.add(mixed); Transitions.TransitionFinishCallback callback = wct -> { mActiveTransitions.remove(mixed); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java index b028bd65b438..0ada74937df4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java @@ -76,7 +76,12 @@ class DefaultMixedTransition extends DefaultMixedHandler.MixedTransition { info, startTransaction, finishTransaction, finishCallback); case TYPE_ENTER_PIP_FROM_SPLIT -> animateEnterPipFromSplit(this, info, startTransaction, finishTransaction, - finishCallback, mPlayer, mMixedHandler, mPipHandler, mSplitHandler); + finishCallback, mPlayer, mMixedHandler, mPipHandler, mSplitHandler, + /*replacingPip*/ false); + case TYPE_ENTER_PIP_REPLACE_FROM_SPLIT -> + animateEnterPipFromSplit(this, info, startTransaction, finishTransaction, + finishCallback, mPlayer, mMixedHandler, mPipHandler, mSplitHandler, + /*replacingPip*/ true); case TYPE_KEYGUARD -> animateKeyguard(this, info, startTransaction, finishTransaction, finishCallback, mKeyguardHandler, mPipHandler); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHelper.java index ffc0b76b131d..e8b01b5880fb 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHelper.java @@ -23,11 +23,15 @@ import static android.window.TransitionInfo.FLAG_IS_WALLPAPER; import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED; import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA; +import static com.android.wm.shell.shared.TransitionUtil.isOpeningMode; +import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN; +import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE; import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED; import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_CHILD_TASK_ENTER_PIP; import static com.android.wm.shell.transition.DefaultMixedHandler.subCopy; import android.annotation.NonNull; +import android.annotation.Nullable; import android.view.SurfaceControl; import android.window.TransitionInfo; @@ -45,7 +49,8 @@ public class MixedTransitionHelper { @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback, @NonNull Transitions player, @NonNull MixedTransitionHandler mixedHandler, - @NonNull PipTransitionController pipHandler, @NonNull StageCoordinator splitHandler) { + @NonNull PipTransitionController pipHandler, @NonNull StageCoordinator splitHandler, + boolean replacingPip) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Animating a mixed transition for " + "entering PIP while Split-Screen is foreground."); TransitionInfo.Change pipChange = null; @@ -99,7 +104,7 @@ public class MixedTransitionHelper { // we need a separate one to send over to launcher. SurfaceControl.Transaction otherStartT = new SurfaceControl.Transaction(); @SplitScreen.StageType int topStageToKeep = STAGE_TYPE_UNDEFINED; - if (splitHandler.isSplitScreenVisible()) { + if (splitHandler.isSplitScreenVisible() && !replacingPip) { // The non-going home case, we could be pip-ing one of the split stages and keep // showing the other for (int i = info.getChanges().size() - 1; i >= 0; --i) { @@ -115,11 +120,12 @@ public class MixedTransitionHelper { break; } } + + // Let split update internal state for dismiss. + splitHandler.prepareDismissAnimation(topStageToKeep, + EXIT_REASON_CHILD_TASK_ENTER_PIP, everythingElse, otherStartT, + finishTransaction); } - // Let split update internal state for dismiss. - splitHandler.prepareDismissAnimation(topStageToKeep, - EXIT_REASON_CHILD_TASK_ENTER_PIP, everythingElse, otherStartT, - finishTransaction); // We are trying to accommodate launcher's close animation which can't handle the // divider-bar, so if split-handler is closing the divider-bar, just hide it and @@ -152,6 +158,44 @@ public class MixedTransitionHelper { return true; } + /** + * Check to see if we're only closing split to enter pip or if we're replacing pip with + * another task. If we are replacing, this will return the change for the task we are replacing + * pip with + * + * @param info Any number of changes + * @param pipChange TransitionInfo.Change indicating the task that is being pipped + * @param splitMainStageRootId MainStage's rootTaskInfo's id + * @param splitSideStageRootId SideStage's rootTaskInfo's id + * @param lastPipSplitStage The last stage that {@param pipChange} was in + * @return The change from {@param info} that is replacing the {@param pipChange}, {@code null} + * otherwise + */ + @Nullable + public static TransitionInfo.Change getPipReplacingChange(TransitionInfo info, + TransitionInfo.Change pipChange, int splitMainStageRootId, int splitSideStageRootId, + @SplitScreen.StageType int lastPipSplitStage) { + int lastPipParentTask = -1; + if (lastPipSplitStage == STAGE_TYPE_MAIN) { + lastPipParentTask = splitMainStageRootId; + } else if (lastPipSplitStage == STAGE_TYPE_SIDE) { + lastPipParentTask = splitSideStageRootId; + } + + for (int i = info.getChanges().size() - 1; i >= 0; --i) { + TransitionInfo.Change change = info.getChanges().get(i); + if (change == pipChange || !isOpeningMode(change.getMode())) { + // Ignore the change/task that's going into Pip or not opening + continue; + } + + if (change.getTaskInfo().parentTaskId == lastPipParentTask) { + return change; + } + } + return null; + } + private static boolean isHomeOpening(@NonNull TransitionInfo.Change change) { return change.getTaskInfo() != null && change.getTaskInfo().getActivityType() == ACTIVITY_TYPE_HOME; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java index d6e64cfaf4d5..9fc6702562bb 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java @@ -142,7 +142,8 @@ class RecentsMixedTransition extends DefaultMixedHandler.MixedTransition { && mSplitHandler.getSplitItemPosition(change.getLastParent()) != SPLIT_POSITION_UNDEFINED) { return animateEnterPipFromSplit(this, info, startTransaction, finishTransaction, - finishCallback, mPlayer, mMixedHandler, mPipHandler, mSplitHandler); + finishCallback, mPlayer, mMixedHandler, mPipHandler, mSplitHandler, + /*replacingPip*/ false); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java index 4d3c76322fa8..6224543516fa 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java @@ -31,7 +31,6 @@ import static android.view.WindowManager.fixScale; import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED; import static android.window.TransitionInfo.FLAG_IS_BEHIND_STARTING_WINDOW; import static android.window.TransitionInfo.FLAG_IS_OCCLUDED; -import static android.window.TransitionInfo.FLAG_MOVED_TO_TOP; import static android.window.TransitionInfo.FLAG_NO_ANIMATION; import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT; @@ -567,15 +566,15 @@ public class Transitions implements RemoteCallable<Transitions>, final int mode = change.getMode(); // Put all the OPEN/SHOW on top if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT) { - if (isOpening - // This is for when an activity launches while a different transition is - // collecting. - || change.hasFlags(FLAG_MOVED_TO_TOP)) { + if (isOpening) { // put on top return zSplitLine + numChanges - i; - } else { + } else if (isClosing) { // put on bottom return zSplitLine - i; + } else { + // maintain relative ordering (put all changes in the animating layer) + return zSplitLine + numChanges - i; } } else if (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK) { if (isOpening) { diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java index 8de60b7acc91..cfe8e07aa6e5 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java @@ -26,6 +26,7 @@ import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_STA import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.never; @@ -115,9 +116,9 @@ public class SplitLayoutTests extends ShellTestCase { @Test public void testUpdateDivideBounds() { - mSplitLayout.updateDividerBounds(anyInt()); + mSplitLayout.updateDividerBounds(anyInt(), anyBoolean()); verify(mSplitLayoutHandler).onLayoutSizeChanging(any(SplitLayout.class), anyInt(), - anyInt()); + anyInt(), anyBoolean()); } @Test diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java index b43ff63f3fcc..a4887567b075 100644 --- a/media/java/android/media/session/MediaController.java +++ b/media/java/android/media/session/MediaController.java @@ -252,18 +252,14 @@ public final class MediaController { return 0; } - /** - * Get the current playback info for this session. - * - * @return The current playback info or null. - */ - public @Nullable PlaybackInfo getPlaybackInfo() { + /** Returns the current playback info for this session. */ + @NonNull + public PlaybackInfo getPlaybackInfo() { try { return mSessionBinder.getVolumeAttributes(); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling getAudioInfo.", e); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } - return null; } /** diff --git a/nfc/java/android/nfc/cardemulation/HostApduService.java b/nfc/java/android/nfc/cardemulation/HostApduService.java index f674b06ad33d..c3c74a6fd265 100644 --- a/nfc/java/android/nfc/cardemulation/HostApduService.java +++ b/nfc/java/android/nfc/cardemulation/HostApduService.java @@ -20,6 +20,7 @@ import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.SuppressLint; import android.app.Service; import android.content.Intent; import android.content.pm.PackageManager; @@ -404,6 +405,7 @@ public abstract class HostApduService extends Service { * * @param frame A description of the polling frame. */ + @SuppressLint("OnNameExpected") @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP) public void processPollingFrames(@NonNull List<PollingFrame> frame) { } diff --git a/packages/SettingsLib/DataStore/README.md b/packages/SettingsLib/DataStore/README.md index 30cb9932f104..a762ad3fe199 100644 --- a/packages/SettingsLib/DataStore/README.md +++ b/packages/SettingsLib/DataStore/README.md @@ -1,55 +1,93 @@ # Datastore library -This library aims to manage datastore in a consistent way. +This library provides consistent API for data management (including backup, +restore, and metrics logging) on Android platform. + +Notably, it is designed to be flexible and could be utilized for a wide range of +data store besides the settings preferences. ## Overview -A datastore is required to extend the `BackupRestoreStorage` class and implement -either `Observable` or `KeyedObservable` interface, which enforces: - -- Backup and restore: Datastore should support - [data backup](https://developer.android.com/guide/topics/data/backup) to - preserve user experiences on a new device. -- Observer pattern: The - [observer pattern](https://en.wikipedia.org/wiki/Observer_pattern) allows to - monitor data change in the datastore and - - trigger - [BackupManager.dataChanged](https://developer.android.com/reference/android/app/backup/BackupManager#dataChanged\(\)) - automatically. - - track data change event to log metrics. - - update internal state and take action. +In the high-level design, a persistent datastore aims to support two key +characteristics: + +- **observable**: triggers backup and metrics logging whenever data is + changed. +- **transferable**: offers users with a seamless experience by backing up and + restoring data on to new devices. + +More specifically, Android framework supports +[data backup](https://developer.android.com/guide/topics/data/backup) to +preserve user experiences on a new device. And the +[observer pattern](https://en.wikipedia.org/wiki/Observer_pattern) allows to +monitor data change. ### Backup and restore -The Android backup framework provides +Currently, the Android backup framework provides [BackupAgentHelper](https://developer.android.com/reference/android/app/backup/BackupAgentHelper) and [BackupHelper](https://developer.android.com/reference/android/app/backup/BackupHelper) -to back up a datastore. However, there are several caveats when implement -`BackupHelper`: +to facilitate data backup. However, there are several caveats to consider when +implementing `BackupHelper`: -- performBackup: The data is updated incrementally but it is not well +- *performBackup*: The data is updated incrementally but it is not well documented. The `ParcelFileDescriptor` state parameters are normally ignored and data is updated even there is no change. -- restoreEntity: The implementation must take care not to seek or close the - underlying data source, nor read more than size() bytes from the stream when - restore (see +- *restoreEntity*: The implementation must take care not to seek or close the + underlying data source, nor read more than `size()` bytes from the stream + when restore (see [BackupDataInputStream](https://developer.android.com/reference/android/app/backup/BackupDataInputStream)). - It is possible a `BackupHelper` prevents other `BackupHelper`s from - restoring data. -- writeNewStateDescription: Existing implementations rarely notice that this - callback is invoked after all entities are restored, and check if necessary - data are all restored in `restoreEntity` (e.g. + It is possible that a `BackupHelper` interferes with the restore process of + other `BackupHelper`s. +- *writeNewStateDescription*: Existing implementations rarely notice that this + callback is invoked after *all* entities are restored. Instead, they check + if necessary data are all restored in the `restoreEntity` (e.g. [BatteryBackupHelper](https://cs.android.com/android/platform/superproject/main/+/main:packages/apps/Settings/src/com/android/settings/fuelgauge/BatteryBackupHelper.java;l=144;drc=cca804e1ed504e2d477be1e3db00fb881ca32736)), which is not robust sometimes. -This library provides more clear API and offers some improvements: +The datastore library will mitigate these problems by providing alternative +APIs. For instance, library users make use of `InputStream` / `OutputStream` to +back up and restore data directly. + +### Observer pattern + +In the current implementation, the Android backup framework requires a manual +call to +[BackupManager.dataChanged](https://developer.android.com/reference/android/app/backup/BackupManager#dataChanged\(\)). +However, it's often observed that this API call is forgotten when using +`SharedPreferences`. Additionally, there's a common need to log metrics when +data changed. To address these limitations, datastore API employed the observer +pattern. + +### API design and advantages -- The implementation only needs to focus on the `BackupRestoreEntity` - interface. The `InputStream` of restore will ensure bounded data are read, - and close the stream will be no-op. -- The library computes checksum of the backup data automatically, so that - unchanged data will not be sent to Android backup system. +Datastore must extend the `BackupRestoreStorage` class (subclass of +[BackupHelper](https://developer.android.com/reference/android/app/backup/BackupHelper)). +The data in a datastore is group by entity, which is represented by +`BackupRestoreEntity`. Basically, a datastore implementation only needs to focus +on the `BackupRestoreEntity`. + +If the datastore is key-value based (e.g. `SharedPreferences`), implements the +`KeyedObservable` interface to offer fine-grained observer. Otherwise, +implements `Observable`. There are builtin thread-safe implementations of the +two interfaces (`KeyedDataObservable` / `DataObservable`). If it is Kotlin, use +delegation to simplify the code. + +Keep in mind that the implementation should call `KeyedObservable.notifyChange` +/ `Observable.notifyChange` whenever internal data is changed, so that the +registered observer will be notified properly. + +For `SharedPreferences` use case, leverage the `SharedPreferencesStorage` +directly. To back up other file based storage, extend the +`BackupRestoreFileStorage` class. + +Here are some highlights of the library: + +- The restore `InputStream` will ensure bounded data are read, and close the + stream is no-op. That being said, all entities are isolated. +- Data checksum is computed automatically, unchanged data will not be sent to + Android backup system. - Data compression is supported: - ZIP best compression is enabled by default, no extra effort needs to be taken. @@ -67,98 +105,159 @@ This library provides more clear API and offers some improvements: successfully restored in those older versions. This is achieved by extending the `BackupRestoreFileStorage` class, and `BackupRestoreFileArchiver` will treat each file as an entity and do the backup / restore. -- Manual `BackupManager.dataChanged` call is unnecessary now, the library will - do the invocation (see next section). +- Manual `BackupManager.dataChanged` call is unnecessary now, the framework + will invoke the API automatically. -### Observer pattern +## Usages -Manual `BackupManager.dataChanged` call is required by current backup framework. -In practice, it is found that `SharedPreferences` usages foget to invoke the -API. Besides, there are common use cases to log metrics when data is changed. -Consequently, observer pattern is employed to resolve the issues. +This section provides [examples](example/ExampleStorage.kt) of datastore. -If the datastore is key-value based (e.g. `SharedPreferences`), implements the -`KeyedObservable` interface to offer fine-grained observer. Otherwise, -implements `Observable`. The library provides thread-safe implementations -(`KeyedDataObservable` / `DataObservable`), and Kotlin delegation will be -helpful. +Here is a datastore with a string data: -Keep in mind that the implementation should call `KeyedObservable.notifyChange` -/ `Observable.notifyChange` whenever internal data is changed, so that the -registered observer will be notified properly. +```kotlin +class ExampleStorage : ObservableBackupRestoreStorage() { + @Volatile // field is manipulated by multiple threads, synchronization might be needed + var data: String? = null + private set -## Usage and example + @AnyThread + fun setData(data: String?) { + this.data = data + // call notifyChange to trigger backup and metrics logging whenever data is changed + if (data != null) { + notifyChange(ChangeReason.UPDATE) + } else { + notifyChange(ChangeReason.DELETE) + } + } + + override val name: String + get() = "ExampleStorage" + + override fun createBackupRestoreEntities(): List<BackupRestoreEntity> = + listOf(StringEntity("data")) + + override fun enableRestore(): Boolean { + return true // check condition like flag, environment, etc. + } + + override fun enableBackup(backupContext: BackupContext): Boolean { + return true // check condition like flag, environment, etc. + } + + @BinderThread + private inner class StringEntity(override val key: String) : BackupRestoreEntity { + override fun backup(backupContext: BackupContext, outputStream: OutputStream) = + if (data != null) { + outputStream.write(data!!.toByteArray(UTF_8)) + EntityBackupResult.UPDATE + } else { + EntityBackupResult.DELETE // delete existing backup data + } + + override fun restore(restoreContext: RestoreContext, inputStream: InputStream) { + // DO NOT call setData API here, which will trigger notifyChange unexpectedly. + // Under the hood, the datastore library will call notifyChange(ChangeReason.RESTORE) + // later to notify observers. + data = String(inputStream.readBytes(), UTF_8) + // Handle restored data in onRestoreFinished() callback + } + } -For `SharedPreferences` use case, leverage the `SharedPreferencesStorage`. To -back up other file based storage, extend the `BackupRestoreFileStorage` class. + override fun onRestoreFinished() { + // TODO: Update state with the restored data. Use this callback instead of "restore()" in + // case the restore action involves several entities. + // NOTE: The library will call notifyChange(ChangeReason.RESTORE) for you + } +} +``` -Here is an example of customized datastore, which has a string to back up: +And this is a datastore with key value data: ```kotlin -class MyDataStore : ObservableBackupRestoreStorage() { - // Another option is make it a StringEntity type and maintain a String field inside StringEntity - @Volatile // backup/restore happens on Binder thread - var data: String? = null - private set - - fun setData(data: String?) { - this.data = data - notifyChange(ChangeReason.UPDATE) +class ExampleKeyValueStorage : + BackupRestoreStorage(), KeyedObservable<String> by KeyedDataObservable() { + // thread safe data structure + private val map = ConcurrentHashMap<String, String>() + + override val name: String + get() = "ExampleKeyValueStorage" + + fun updateData(key: String, value: String?) { + if (value != null) { + map[key] = value + notifyChange(ChangeReason.UPDATE) + } else { + map.remove(key) + notifyChange(ChangeReason.DELETE) } + } - override val name: String - get() = "MyData" - - override fun createBackupRestoreEntities(): List<BackupRestoreEntity> = - listOf(StringEntity("data")) - - private inner class StringEntity(override val key: String) : BackupRestoreEntity { - override fun backup( - backupContext: BackupContext, - outputStream: OutputStream, - ) = - if (data != null) { - outputStream.write(data!!.toByteArray(UTF_8)) - EntityBackupResult.UPDATE - } else { - EntityBackupResult.DELETE - } - - override fun restore(restoreContext: RestoreContext, inputStream: InputStream) { - data = String(inputStream.readAllBytes(), UTF_8) - // NOTE: The library will call notifyChange(ChangeReason.RESTORE) for you + override fun createBackupRestoreEntities(): List<BackupRestoreEntity> = + listOf(createMapBackupRestoreEntity()) + + private fun createMapBackupRestoreEntity() = + object : BackupRestoreEntity { + override val key: String + get() = "map" + + override fun backup( + backupContext: BackupContext, + outputStream: OutputStream, + ): EntityBackupResult { + // Use TreeMap to achieve predictable and stable order, so that data will not be + // updated to Android backup backend if there is only order change. + val copy = TreeMap(map) + if (copy.isEmpty()) return EntityBackupResult.DELETE + val dataOutputStream = DataOutputStream(outputStream) + dataOutputStream.writeInt(copy.size) + for ((key, value) in copy) { + dataOutputStream.writeUTF(key) + dataOutputStream.writeUTF(value) } - } + return EntityBackupResult.UPDATE + } - override fun onRestoreFinished() { - // TODO: Update state with the restored data. Use this callback instead "restore()" in case - // the restore action involves several entities. - // NOTE: The library will call notifyChange(ChangeReason.RESTORE) for you + override fun restore(restoreContext: RestoreContext, inputStream: InputStream) { + val dataInputString = DataInputStream(inputStream) + repeat(dataInputString.readInt()) { + val key = dataInputString.readUTF() + val value = dataInputString.readUTF() + map[key] = value + } + } } } ``` -In the application class: +All the datastore should be added in the application class: ```kotlin -class MyApplication : Application() { +class ExampleApplication : Application() { override fun onCreate() { - super.onCreate(); - BackupRestoreStorageManager.getInstance(this).add(MyDataStore()); + super.onCreate() + BackupRestoreStorageManager.getInstance(this) + .add(ExampleStorage(), ExampleKeyValueStorage()) } } ``` -In the custom `BackupAgentHelper` class: +Additionally, inject datastore to the custom `BackupAgentHelper` class: ```kotlin -class MyBackupAgentHelper : BackupAgentHelper() { +class ExampleBackupAgent : BackupAgentHelper() { override fun onCreate() { - BackupRestoreStorageManager.getInstance(this).addBackupAgentHelpers(this); + super.onCreate() + BackupRestoreStorageManager.getInstance(this).addBackupAgentHelpers(this) } override fun onRestoreFinished() { - BackupRestoreStorageManager.getInstance(this).onRestoreFinished(); + BackupRestoreStorageManager.getInstance(this).onRestoreFinished() } } ``` + +## Development + +Please preserve the code coverage ratio during development. The current line +coverage is **100% (444/444)** and branch coverage is **93.6% (176/188)**. diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreEntity.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreEntity.kt index 817ee4c56b19..6720e5c6d714 100644 --- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreEntity.kt +++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreEntity.kt @@ -23,7 +23,11 @@ import java.io.IOException import java.io.InputStream import java.io.OutputStream -/** Entity for back up and restore. */ +/** + * Entity for back up and restore. + * + * Note that backup/restore callback is invoked on the binder thread. + */ interface BackupRestoreEntity { /** * Key of the entity. @@ -45,9 +49,12 @@ interface BackupRestoreEntity { /** * Backs up the entity. * + * Back up data in predictable order (e.g. use `TreeMap` instead of `HashMap`), otherwise data + * will be backed up needlessly. + * * @param backupContext context for backup * @param outputStream output stream to back up data - * @return false if backup file is deleted, otherwise true + * @return backup result */ @BinderThread @Throws(IOException::class) diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorage.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorage.kt index 935f9ccf6ed9..284c97b5ad6c 100644 --- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorage.kt +++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorage.kt @@ -22,6 +22,7 @@ import android.app.backup.BackupDataOutput import android.app.backup.BackupHelper import android.os.ParcelFileDescriptor import android.util.Log +import androidx.annotation.BinderThread import androidx.annotation.VisibleForTesting import androidx.collection.MutableScatterMap import com.google.common.io.ByteStreams @@ -38,16 +39,22 @@ import java.util.zip.CRC32 import java.util.zip.CheckedInputStream import java.util.zip.CheckedOutputStream import java.util.zip.Checksum +import javax.annotation.concurrent.ThreadSafe internal const val LOG_TAG = "BackupRestoreStorage" /** - * Storage with backup and restore support. Subclass must implement either [Observable] or - * [KeyedObservable] interface. + * Storage with backup and restore support. + * + * Subclass MUST + * - implement either [Observable] or [KeyedObservable] interface. + * - be thread safe, backup/restore happens on Binder thread, while general data read/write + * operations occur on other threads. * * The storage is identified by a unique string [name] and data set is split into entities * ([BackupRestoreEntity]). */ +@ThreadSafe abstract class BackupRestoreStorage : BackupHelper { /** * A unique string used to disambiguate the various storages within backup agent. @@ -68,7 +75,7 @@ abstract class BackupRestoreStorage : BackupHelper { @VisibleForTesting internal var entities: List<BackupRestoreEntity>? = null /** Entities to back up and restore. */ - abstract fun createBackupRestoreEntities(): List<BackupRestoreEntity> + @BinderThread abstract fun createBackupRestoreEntities(): List<BackupRestoreEntity> /** Default codec used to encode/decode the entity data. */ open fun defaultCodec(): BackupCodec = BackupZipCodec.BEST_COMPRESSION @@ -134,7 +141,11 @@ abstract class BackupRestoreStorage : BackupHelper { Log.i(LOG_TAG, "[$name] Backup end") } - /** Returns if backup is enabled. */ + /** + * Returns if backup is enabled. + * + * If disabled, [performBackup] will be no-op, all entities backup are skipped. + */ open fun enableBackup(backupContext: BackupContext): Boolean = true open fun wrapBackupOutputStream(codec: BackupCodec, outputStream: OutputStream): OutputStream { @@ -172,7 +183,11 @@ abstract class BackupRestoreStorage : BackupHelper { private fun ensureEntities(): List<BackupRestoreEntity> = entities ?: createBackupRestoreEntities().also { entities = it } - /** Returns if restore is enabled. */ + /** + * Returns if restore is enabled. + * + * If disabled, [restoreEntity] will be no-op, all entities restore are skipped. + */ open fun enableRestore(): Boolean = true open fun wrapRestoreInputStream( @@ -188,12 +203,13 @@ abstract class BackupRestoreStorage : BackupHelper { } final override fun writeNewStateDescription(newState: ParcelFileDescriptor) { + if (!enableRestore()) return entities = null // clear to reduce memory footprint newState.writeAndClearEntityStates() onRestoreFinished() } - /** Callbacks when restore finished. */ + /** Callbacks when entity data are all restored. */ open fun onRestoreFinished() {} @VisibleForTesting diff --git a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreStorageTest.kt b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreStorageTest.kt index 99998ffc13ec..26534baaa47d 100644 --- a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreStorageTest.kt +++ b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreStorageTest.kt @@ -248,6 +248,15 @@ class BackupRestoreStorageTest { } @Test + fun writeNewStateDescription_restoreDisabled() { + val storage = spy(TestStorage().apply { enabled = false }) + temporaryFolder.newFile().toParcelFileDescriptor(MODE_WRITE_ONLY or MODE_APPEND).use { + storage.writeNewStateDescription(it) + } + verify(storage, never()).onRestoreFinished() + } + + @Test fun backupAndRestore() { val storage = spy(TestStorage(entity1, entity2)) val backupAgentHelper = BackupAgentHelper() diff --git a/packages/SettingsLib/SettingsTheme/res/color-v35/settingslib_preference_bg_color.xml b/packages/SettingsLib/SettingsTheme/res/color-v35/settingslib_preference_bg_color.xml index 4ced9f2469ab..cece9665b729 100644 --- a/packages/SettingsLib/SettingsTheme/res/color-v35/settingslib_preference_bg_color.xml +++ b/packages/SettingsLib/SettingsTheme/res/color-v35/settingslib_preference_bg_color.xml @@ -16,8 +16,8 @@ --> <selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:state_pressed="true" android:color="@color/settingslib_materialColorSecondaryContainer"/> - <item android:state_selected="true" android:color="@color/settingslib_materialColorSecondaryContainer"/> - <item android:state_activated="true" android:color="@color/settingslib_materialColorSecondaryContainer"/> - <item android:color="@color/settingslib_materialColorSurfaceContainerHighest"/> <!-- not selected --> + <item android:state_pressed="true" android:color="@color/settingslib_materialColorSurfaceContainerHigh"/> + <item android:state_selected="true" android:color="@color/settingslib_materialColorSurfaceContainerHigh"/> + <item android:state_activated="true" android:color="@color/settingslib_materialColorSurfaceContainerHigh"/> + <item android:color="@color/settingslib_materialColorSurfaceBright"/> <!-- not selected --> </selector>
\ No newline at end of file diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background.xml index 285ab7301162..eba9c2ceba70 100644 --- a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background.xml +++ b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background.xml @@ -19,12 +19,12 @@ <item android:left="?android:attr/listPreferredItemPaddingStart" android:right="?android:attr/listPreferredItemPaddingEnd" - android:top="1dp"> + android:top="2dp"> <shape android:shape="rectangle"> <solid android:color="@color/settingslib_preference_bg_color" /> <corners - android:radius="?android:attr/dialogCornerRadius" /> + android:radius="@dimen/settingslib_preference_corner_radius" /> </shape> </item> </layer-list>
\ No newline at end of file diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_bottom.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_bottom.xml index e417307edc3d..5c60f37a7244 100644 --- a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_bottom.xml +++ b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_bottom.xml @@ -19,15 +19,15 @@ <item android:left="?android:attr/listPreferredItemPaddingStart" android:right="?android:attr/listPreferredItemPaddingEnd" - android:top="1dp"> + android:top="2dp"> <shape android:shape="rectangle"> <solid android:color="@color/settingslib_preference_bg_color" /> <corners - android:topLeftRadius="0dp" - android:bottomLeftRadius="?android:attr/dialogCornerRadius" - android:topRightRadius="0dp" - android:bottomRightRadius="?android:attr/dialogCornerRadius" /> + android:topLeftRadius="4dp" + android:bottomLeftRadius="@dimen/settingslib_preference_corner_radius" + android:topRightRadius="4dp" + android:bottomRightRadius="@dimen/settingslib_preference_corner_radius" /> </shape> </item> </layer-list>
\ No newline at end of file diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_bottom_selected.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_bottom_selected.xml new file mode 100644 index 000000000000..de64efd23a0d --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_bottom_selected.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> + <item + android:left="?android:attr/listPreferredItemPaddingStart" + android:right="?android:attr/listPreferredItemPaddingEnd" + android:top="2dp"> + <shape android:shape="rectangle"> + <solid + android:color="@color/settingslib_materialColorSurfaceContainerHigh" /> + <corners + android:topLeftRadius="4dp" + android:bottomLeftRadius="@dimen/settingslib_preference_corner_radius" + android:topRightRadius="4dp" + android:bottomRightRadius="@dimen/settingslib_preference_corner_radius" /> + </shape> + </item> +</layer-list>
\ No newline at end of file diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_center.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_center.xml index e9646575663d..dd70f4f7a146 100644 --- a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_center.xml +++ b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_center.xml @@ -19,12 +19,12 @@ <item android:left="?android:attr/listPreferredItemPaddingStart" android:right="?android:attr/listPreferredItemPaddingEnd" - android:top="1dp"> + android:top="2dp"> <shape android:shape="rectangle"> <solid android:color="@color/settingslib_preference_bg_color" /> <corners - android:radius="1dp" /> + android:radius="4dp" /> </shape> </item> </layer-list>
\ No newline at end of file diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_center_selected.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_center_selected.xml new file mode 100644 index 000000000000..fffc6c8c4bef --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_center_selected.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> + <item + android:left="?android:attr/listPreferredItemPaddingStart" + android:right="?android:attr/listPreferredItemPaddingEnd" + android:top="2dp"> + <shape android:shape="rectangle"> + <solid + android:color="@color/settingslib_materialColorSurfaceContainerHigh" /> + <corners + android:radius="4dp" /> + </shape> + </item> +</layer-list>
\ No newline at end of file diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_selected.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_selected.xml new file mode 100644 index 000000000000..f83e3b177200 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_selected.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> + <item + android:left="?android:attr/listPreferredItemPaddingStart" + android:right="?android:attr/listPreferredItemPaddingEnd" + android:top="2dp"> + <shape android:shape="rectangle"> + <solid + android:color="@color/settingslib_materialColorSurfaceContainerHigh" /> + <corners + android:radius="@dimen/settingslib_preference_corner_radius" /> + </shape> + </item> +</layer-list>
\ No newline at end of file diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_top.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_top.xml index a9d69c264a2c..ab79d18a1ab3 100644 --- a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_top.xml +++ b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_top.xml @@ -19,15 +19,15 @@ <item android:left="?android:attr/listPreferredItemPaddingStart" android:right="?android:attr/listPreferredItemPaddingEnd" - android:top="1dp"> + android:top="2dp"> <shape android:shape="rectangle"> <solid android:color="@color/settingslib_preference_bg_color" /> <corners - android:topLeftRadius="?android:attr/dialogCornerRadius" - android:bottomLeftRadius="0dp" - android:topRightRadius="?android:attr/dialogCornerRadius" - android:bottomRightRadius="0dp" /> + android:topLeftRadius="@dimen/settingslib_preference_corner_radius" + android:bottomLeftRadius="4dp" + android:topRightRadius="@dimen/settingslib_preference_corner_radius" + android:bottomRightRadius="4dp" /> </shape> </item> </layer-list>
\ No newline at end of file diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_top_selected.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_top_selected.xml new file mode 100644 index 000000000000..112ec735aa27 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_top_selected.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> + <item + android:left="?android:attr/listPreferredItemPaddingStart" + android:right="?android:attr/listPreferredItemPaddingEnd" + android:top="2dp"> + <shape android:shape="rectangle"> + <solid + android:color="@color/settingslib_materialColorSurfaceContainerHigh" /> + <corners + android:topLeftRadius="@dimen/settingslib_preference_corner_radius" + android:bottomLeftRadius="4dp" + android:topRightRadius="@dimen/settingslib_preference_corner_radius" + android:bottomRightRadius="4dp" /> + </shape> + </item> +</layer-list>
\ No newline at end of file diff --git a/packages/SettingsLib/SettingsTheme/res/values-v35/dimens.xml b/packages/SettingsLib/SettingsTheme/res/values-v35/dimens.xml new file mode 100644 index 000000000000..d783956ee240 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-v35/dimens.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources> + <dimen name="settingslib_preference_corner_radius">20dp</dimen> +</resources>
\ No newline at end of file diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index 4ea746007f76..363045ec1d83 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -214,6 +214,10 @@ <string name="bluetooth_battery_level_untethered_left">Left: <xliff:g id="battery_level_as_percentage" example="25%">%1$s</xliff:g> battery</string> <!-- Connected devices settings. Message when Bluetooth is connected but not in use, showing remote device battery level for the right part of the untethered headset. [CHAR LIMIT=NONE] --> <string name="bluetooth_battery_level_untethered_right">Right: <xliff:g id="battery_level_as_percentage" example="25%">%1$s</xliff:g> battery</string> + <!-- Connected devices settings. Message when Bluetooth is connected, showing remote device battery level for the left part of the untethered headset. [CHAR LIMIT=NONE] --> + <string name="tv_bluetooth_battery_level_untethered_left">Left <xliff:g id="battery_level_as_percentage" example="25%">%1$s</xliff:g></string> + <!-- Connected devices settings. Message when Bluetooth is connected, showing remote device battery level for the right part of the untethered headset. [CHAR LIMIT=NONE] --> + <string name="tv_bluetooth_battery_level_untethered_right">Right <xliff:g id="battery_level_as_percentage" example="25%">%1$s</xliff:g></string> <!-- Connected devices settings. Message when Bluetooth is connected and active but no battery information, showing remote device status. [CHAR LIMIT=NONE] --> <string name="bluetooth_active_no_battery_level">Active</string> <!-- Connected devices settings. Message shown when bluetooth device is disconnected but is a known, previously connected device [CHAR LIMIT=NONE] --> diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java index c2a83b1e772f..0fec61c5affe 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java @@ -1532,7 +1532,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> // the left. if (leftBattery >= 0) { String left = res.getString( - R.string.bluetooth_battery_level_untethered_left, + R.string.tv_bluetooth_battery_level_untethered_left, Utils.formatPercentage(leftBattery)); addBatterySpan(spannableBuilder, left, isBatteryLow(leftBattery, BluetoothDevice.METADATA_UNTETHERED_LEFT_LOW_BATTERY_THRESHOLD), @@ -1543,7 +1543,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> spannableBuilder.append(" "); } String right = res.getString( - R.string.bluetooth_battery_level_untethered_right, + R.string.tv_bluetooth_battery_level_untethered_right, Utils.formatPercentage(rightBattery)); addBatterySpan(spannableBuilder, right, isBatteryLow(rightBattery, BluetoothDevice.METADATA_UNTETHERED_RIGHT_LOW_BATTERY_THRESHOLD), diff --git a/packages/SettingsLib/src/com/android/settingslib/satellite/SatelliteDialogUtils.kt b/packages/SettingsLib/src/com/android/settingslib/satellite/SatelliteDialogUtils.kt new file mode 100644 index 000000000000..d69c87b318e2 --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/satellite/SatelliteDialogUtils.kt @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.satellite + +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import android.os.OutcomeReceiver +import android.telephony.satellite.SatelliteManager +import android.util.Log +import android.view.WindowManager +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.lifecycleScope +import com.android.settingslib.wifi.WifiUtils +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Dispatchers.Default +import kotlinx.coroutines.Job +import kotlinx.coroutines.asExecutor +import kotlinx.coroutines.launch +import kotlinx.coroutines.suspendCancellableCoroutine +import kotlinx.coroutines.withContext +import java.util.concurrent.ExecutionException +import java.util.concurrent.TimeoutException +import kotlin.coroutines.resume + +/** A util for Satellite dialog */ +object SatelliteDialogUtils { + + /** + * Uses to start Satellite dialog to prevent users from using the BT, Airplane Mode, and + * Wifi during the satellite mode is on. + */ + @JvmStatic + fun mayStartSatelliteWarningDialog( + context: Context, + lifecycleOwner: LifecycleOwner, + type: Int, + allowClick: (isAllowed: Boolean) -> Unit + ): Job { + return mayStartSatelliteWarningDialog( + context, lifecycleOwner.lifecycleScope, type, allowClick) + } + + /** + * Uses to start Satellite dialog to prevent users from using the BT, Airplane Mode, and + * Wifi during the satellite mode is on. + */ + @JvmStatic + fun mayStartSatelliteWarningDialog( + context: Context, + coroutineScope: CoroutineScope, + type: Int, + allowClick: (isAllowed: Boolean) -> Unit + ): Job = + coroutineScope.launch { + var isSatelliteModeOn = false + try { + isSatelliteModeOn = requestIsEnabled(context) + } catch (e: InterruptedException) { + Log.w(TAG, "Error to get satellite status : $e") + } catch (e: ExecutionException) { + Log.w(TAG, "Error to get satellite status : $e") + } catch (e: TimeoutException) { + Log.w(TAG, "Error to get satellite status : $e") + } + + if (isSatelliteModeOn) { + startSatelliteWarningDialog(context, type) + } + withContext(Dispatchers.Main) { + allowClick(!isSatelliteModeOn) + } + } + + private fun startSatelliteWarningDialog(context: Context, type: Int) { + context.startActivity(Intent(Intent.ACTION_MAIN).apply { + component = ComponentName( + "com.android.settings", + "com.android.settings.network.SatelliteWarningDialogActivity" + ) + putExtra(WifiUtils.DIALOG_WINDOW_TYPE, + WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG) + putExtra(EXTRA_TYPE_OF_SATELLITE_WARNING_DIALOG, type) + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP) + }) + } + + /** + * Checks if the satellite modem is enabled. + * + * @param executor The executor to run the asynchronous operation on + * @return A ListenableFuture that will resolve to `true` if the satellite modem enabled, + * `false` otherwise. + */ + private suspend fun requestIsEnabled( + context: Context, + ): Boolean = withContext(Default) { + val satelliteManager: SatelliteManager? = + context.getSystemService(SatelliteManager::class.java) + if (satelliteManager == null) { + Log.w(TAG, "SatelliteManager is null") + return@withContext false + } + + suspendCancellableCoroutine {continuation -> + satelliteManager?.requestIsEnabled(Default.asExecutor(), + object : OutcomeReceiver<Boolean, SatelliteManager.SatelliteException> { + override fun onResult(result: Boolean) { + Log.i(TAG, "Satellite modem enabled status: $result") + continuation.resume(result) + } + + override fun onError(error: SatelliteManager.SatelliteException) { + super.onError(error) + Log.w(TAG, "Can't get satellite modem enabled status", error) + continuation.resume(false) + } + }) + } + } + + const val TAG = "SatelliteDialogUtils" + + const val EXTRA_TYPE_OF_SATELLITE_WARNING_DIALOG: String = + "extra_type_of_satellite_warning_dialog" + const val TYPE_IS_UNKNOWN = -1 + const val TYPE_IS_WIFI = 0 + const val TYPE_IS_BLUETOOTH = 1 + const val TYPE_IS_AIRPLANE_MODE = 2 +}
\ No newline at end of file diff --git a/packages/SettingsLib/tests/robotests/Android.bp b/packages/SettingsLib/tests/robotests/Android.bp index f87b5190c7a1..e125083488ed 100644 --- a/packages/SettingsLib/tests/robotests/Android.bp +++ b/packages/SettingsLib/tests/robotests/Android.bp @@ -41,7 +41,10 @@ android_app { //########################################################### android_robolectric_test { name: "SettingsLibRoboTests", - srcs: ["src/**/*.java"], + srcs: [ + "src/**/*.java", + "src/**/*.kt", + ], static_libs: [ "Settings_robolectric_meta_service_file", "Robolectric_shadows_androidx_fragment_upstream", diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java index b9bf9caddac7..0d814947527c 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java @@ -780,9 +780,8 @@ public class CachedBluetoothDeviceTest { mBatteryLevel = 10; // Act & Assert: - // Get "Left: 10% battery" result with Battery Level 10. - assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo( - "Left: 10% battery"); + // Get "Left 10%" result with Battery Level 10. + assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo("Left 10%"); } @Test @@ -815,9 +814,9 @@ public class CachedBluetoothDeviceTest { mBatteryLevel = 10; // Act & Assert: - // Get "Left: 10% battery" result with Battery Level 10. + // Get "Left 10%" result with Battery Level 10. assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo( - "Left: 10% battery"); + "Left 10%"); } @Test @@ -925,9 +924,9 @@ public class CachedBluetoothDeviceTest { mBatteryLevel = 10; // Act & Assert: - // Get "Left: 10% battery Right: 10% battery" result with Battery Level 10. + // Get "Left 10% Right 10%" result with Battery Level 10. assertThat(mCachedDevice.getTvConnectionSummary().toString()) - .isEqualTo("Left: 10% battery Right: 10% battery"); + .isEqualTo("Left 10% Right 10%"); } @Test @@ -1226,7 +1225,7 @@ public class CachedBluetoothDeviceTest { TWS_BATTERY_RIGHT.getBytes()); assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo( - "Left: 15% battery Right: 25% battery"); + "Left 15% Right 25%"); } @Test @@ -1262,11 +1261,7 @@ public class CachedBluetoothDeviceTest { TWS_BATTERY_RIGHT.getBytes()); assertThat(mCachedDevice.getTvConnectionSummary().toString()) - .isEqualTo( - mContext.getString(R.string.bluetooth_battery_level_untethered_left, "15%") - + " " - + mContext.getString( - R.string.bluetooth_battery_level_untethered_right, "25%")); + .isEqualTo("Left 15% Right 25%"); } @Test @@ -1283,10 +1278,8 @@ public class CachedBluetoothDeviceTest { .thenReturn(TWS_BATTERY_RIGHT.getBytes()); int lowBatteryColor = mContext.getColor(LOW_BATTERY_COLOR); - String leftBattery = - mContext.getString(R.string.bluetooth_battery_level_untethered_left, "15%"); - String rightBattery = - mContext.getString(R.string.bluetooth_battery_level_untethered_right, "25%"); + String leftBattery = "Left 15%"; + String rightBattery = "Right 25%"; // Default low battery threshold, only left battery is low CharSequence summary = mCachedDevice.getTvConnectionSummary(LOW_BATTERY_COLOR); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/satellite/SatelliteDialogUtilsTest.kt b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/satellite/SatelliteDialogUtilsTest.kt new file mode 100644 index 000000000000..aeda1ed66d16 --- /dev/null +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/satellite/SatelliteDialogUtilsTest.kt @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.satellite + +import android.content.Context +import android.content.Intent +import android.os.OutcomeReceiver +import android.platform.test.annotations.RequiresFlagsEnabled +import android.telephony.satellite.SatelliteManager +import android.telephony.satellite.SatelliteManager.SatelliteException +import android.util.AndroidRuntimeException +import androidx.test.core.app.ApplicationProvider +import com.android.internal.telephony.flags.Flags +import com.android.settingslib.satellite.SatelliteDialogUtils.TYPE_IS_WIFI +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.runBlocking +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.any +import org.mockito.Mockito.`when` +import org.mockito.Spy +import org.mockito.junit.MockitoJUnit +import org.mockito.junit.MockitoRule +import org.mockito.Mockito.verify +import org.mockito.internal.verification.Times +import org.robolectric.RobolectricTestRunner + +@RunWith(RobolectricTestRunner::class) +class SatelliteDialogUtilsTest { + @JvmField + @Rule + val mockitoRule: MockitoRule = MockitoJUnit.rule() + + @Spy + var context: Context = ApplicationProvider.getApplicationContext() + @Mock + private lateinit var satelliteManager: SatelliteManager + + private val coroutineScope = CoroutineScope(Dispatchers.Main) + + @Before + fun setUp() { + `when`(context.getSystemService(SatelliteManager::class.java)) + .thenReturn(satelliteManager) + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) + fun mayStartSatelliteWarningDialog_satelliteIsOn_showWarningDialog() = runBlocking { + `when`( + satelliteManager.requestIsEnabled( + any(), any<OutcomeReceiver<Boolean, SatelliteManager.SatelliteException>>() + ) + ) + .thenAnswer { invocation -> + val receiver = invocation + .getArgument< + OutcomeReceiver<Boolean, SatelliteManager.SatelliteException>>( + 1 + ) + receiver.onResult(true) + null + } + + try { + SatelliteDialogUtils.mayStartSatelliteWarningDialog( + context, coroutineScope, TYPE_IS_WIFI, allowClick = { + assertTrue(it) + }) + } catch (e: AndroidRuntimeException) { + // Catch exception of starting activity . + } + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) + fun mayStartSatelliteWarningDialog_satelliteIsOff_notShowWarningDialog() = runBlocking { + `when`( + satelliteManager.requestIsEnabled( + any(), any<OutcomeReceiver<Boolean, SatelliteManager.SatelliteException>>() + ) + ) + .thenAnswer { invocation -> + val receiver = invocation + .getArgument< + OutcomeReceiver<Boolean, SatelliteManager.SatelliteException>>( + 1 + ) + receiver.onResult(false) + null + } + + + SatelliteDialogUtils.mayStartSatelliteWarningDialog( + context, coroutineScope, TYPE_IS_WIFI, allowClick = { + assertFalse(it) + }) + + verify(context, Times(0)).startActivity(any<Intent>()) + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) + fun mayStartSatelliteWarningDialog_noSatelliteManager_notShowWarningDialog() = runBlocking { + `when`(context.getSystemService(SatelliteManager::class.java)) + .thenReturn(null) + + SatelliteDialogUtils.mayStartSatelliteWarningDialog( + context, coroutineScope, TYPE_IS_WIFI, allowClick = { + assertFalse(it) + }) + + verify(context, Times(0)).startActivity(any<Intent>()) + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) + fun mayStartSatelliteWarningDialog_satelliteErrorResult_notShowWarningDialog() = runBlocking { + `when`( + satelliteManager.requestIsEnabled( + any(), any<OutcomeReceiver<Boolean, SatelliteManager.SatelliteException>>() + ) + ) + .thenAnswer { invocation -> + val receiver = invocation + .getArgument< + OutcomeReceiver<Boolean, SatelliteManager.SatelliteException>>( + 1 + ) + receiver.onError(SatelliteException(SatelliteManager.SATELLITE_RESULT_ERROR)) + null + } + + + SatelliteDialogUtils.mayStartSatelliteWarningDialog( + context, coroutineScope, TYPE_IS_WIFI, allowClick = { + assertFalse(it) + }) + + verify(context, Times(0)).startActivity(any<Intent>()) + } +} diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 46bf494f2b1a..374240bb7262 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -347,6 +347,7 @@ <uses-permission android:name="android.permission.REQUEST_COMPANION_SELF_MANAGED" /> <uses-permission android:name="android.permission.USE_COMPANION_TRANSPORTS" /> <uses-permission android:name="android.permission.REQUEST_OBSERVE_DEVICE_UUID_PRESENCE" /> + <uses-permission android:name="android.permission.DELIVER_COMPANION_MESSAGES" /> <uses-permission android:name="android.permission.MANAGE_APPOPS" /> <uses-permission android:name="android.permission.WATCH_APPOPS" /> diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index d2ca11207084..8b60ed035d07 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -87,6 +87,52 @@ filegroup { "tests/src/**/systemui/util/sensors/AsyncManagerTest.java", "tests/src/**/systemui/util/sensors/ThresholdSensorImplTest.java", "tests/src/**/systemui/util/wakelock/KeepAwakeAnimationListenerTest.java", + "tests/src/**/systemui/statusbar/KeyboardShortcutListSearchTest.java", + "tests/src/**/systemui/statusbar/KeyboardShortcutsTest.java", + "tests/src/**/systemui/statusbar/KeyguardIndicationControllerWithCoroutinesTest.kt", + "tests/src/**/systemui/statusbar/notification/AssistantFeedbackControllerTest.java", + "tests/src/**/systemui/statusbar/notification/collection/NotifCollectionTest.java", + "tests/src/**/systemui/statusbar/notification/collection/NotificationEntryTest.java", + "tests/src/**/systemui/statusbar/notification/collection/render/GroupExpansionManagerTest.kt", + "tests/src/**/systemui/statusbar/notification/collection/ShadeListBuilderTest.java", + "tests/src/**/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java", + "tests/src/**/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java", + "tests/src/**/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt", + "tests/src/**/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt", + "tests/src/**/systemui/statusbar/NotificationLockscreenUserManagerTest.java", + "tests/src/**/systemui/statusbar/notification/logging/NotificationLoggerTest.java", + "tests/src/**/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java", + "tests/src/**/systemui/statusbar/notification/row/NotificationContentInflaterTest.java", + "tests/src/**/systemui/statusbar/notification/row/NotificationContentViewTest.kt", + "tests/src/**/systemui/statusbar/notification/row/NotificationConversationInfoTest.java", + "tests/src/**/systemui/statusbar/notification/row/NotificationGutsManagerTest.java", + "tests/src/**/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt", + "tests/src/**/systemui/statusbar/notification/row/NotifLayoutInflaterFactoryTest.kt", + "tests/src/**/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapperTest.kt", + "tests/src/**/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java", + "tests/src/**/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java", + "tests/src/**/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt", + "tests/src/**/systemui/statusbar/phone/AutoTileManagerTest.java", + "tests/src/**/systemui/statusbar/phone/CentralSurfacesImplTest.java", + "tests/src/**/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java", + "tests/src/**/systemui/statusbar/phone/PhoneStatusBarTransitionsTest.kt", + "tests/src/**/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt", + "tests/src/**/systemui/statusbar/phone/PhoneStatusBarView.java", + "tests/src/**/systemui/statusbar/phone/PhoneStatusBarViewTest.kt", + "tests/src/**/systemui/statusbar/phone/StatusBarBoundsProviderTest.kt", + "tests/src/**/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationControllerTest.kt", + "tests/src/**/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModelImplTest.kt", + "tests/src/**/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt", + "tests/src/**/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileViewTest.kt", + "tests/src/**/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt", + "tests/src/**/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt", + "tests/src/**/systemui/statusbar/policy/CallbackControllerTest.java", + "tests/src/**/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java", + "tests/src/**/systemui/statusbar/policy/InflatedSmartRepliesTest.java", + "tests/src/**/systemui/statusbar/policy/LocationControllerImplTest.java", + "tests/src/**/systemui/statusbar/policy/RemoteInputViewTest.java", + "tests/src/**/systemui/statusbar/policy/SmartReplyViewTest.java", + "tests/src/**/systemui/statusbar/StatusBarStateControllerImplTest.kt", ], } diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index b9e70ef14007..9c58371a387d 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -826,20 +826,6 @@ </intent-filter> </activity> - <activity - android:name=".contrast.ContrastDialogActivity" - android:label="@string/quick_settings_contrast_label" - android:theme="@style/Theme.SystemUI.ContrastDialog" - android:finishOnCloseSystemDialogs="true" - android:launchMode="singleInstance" - android:excludeFromRecents="true" - android:exported="true"> - <intent-filter> - <action android:name="com.android.intent.action.SHOW_CONTRAST_DIALOG" /> - <category android:name="android.intent.category.DEFAULT" /> - </intent-filter> - </activity> - <activity android:name=".ForegroundServicesDialog" android:process=":fgservices" android:excludeFromRecents="true" diff --git a/packages/SystemUI/aconfig/accessibility.aconfig b/packages/SystemUI/aconfig/accessibility.aconfig index 755fe2a4476a..55edff6d9518 100644 --- a/packages/SystemUI/aconfig/accessibility.aconfig +++ b/packages/SystemUI/aconfig/accessibility.aconfig @@ -4,6 +4,13 @@ container: "system" # NOTE: Keep alphabetized to help limit merge conflicts from multiple simultaneous editors. flag { + name: "create_windowless_window_magnifier" + namespace: "accessibility" + description: "Uses SurfaceControlViewHost to create the magnifier for window magnification." + bug: "280992417" +} + +flag { name: "delay_show_magnification_button" namespace: "accessibility" description: "Delays the showing of magnification mode switch button." @@ -66,8 +73,11 @@ flag { } flag { - name: "create_windowless_window_magnifier" + name: "save_and_restore_magnification_settings_buttons" namespace: "accessibility" - description: "Uses SurfaceControlViewHost to create the magnifier for window magnification." - bug: "280992417" + description: "Saves the selected button status in magnification settings and restore the status when revisiting the same smallest screen DP." + bug: "325567876" + metadata { + purpose: PURPOSE_BUGFIX + } } diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig index aff86e8a14c4..f9e955bf0366 100644 --- a/packages/SystemUI/aconfig/systemui.aconfig +++ b/packages/SystemUI/aconfig/systemui.aconfig @@ -26,14 +26,6 @@ flag { } flag { - - name: "notification_heads_up_cycling" - namespace: "systemui" - description: "Heads-up notification cycling animation for the Notification Avalanche feature." - bug: "316404716" -} - -flag { name: "priority_people_section" namespace: "systemui" description: "Add a new section for priority people (aka important conversations)." @@ -204,7 +196,16 @@ flag { description: "Re-enable the codepath that removed tinting of notifications when the" " standard background color is desired. This was the behavior before we discovered" " a resources threading issue, which we worked around by tinting the notification" - " backgrounds and footer buttons." + " backgrounds." + bug: "294830092" +} + +flag { + name: "notification_footer_background_tint_optimization" + namespace: "systemui" + description: "Remove duplicative tinting of notification footer buttons. This was the behavior" + " before we discovered a resources threading issue, which we worked around by applying the" + " same color as a tint to the background drawable of footer buttons." bug: "294830092" } @@ -355,6 +356,14 @@ flag { } flag { + name: "status_bar_screen_sharing_chips" + namespace: "systemui" + description: "Show chips on the left side of the status bar when a user is screen sharing, " + "recording, or casting" + bug: "332662551" +} + +flag { name: "compose_bouncer" namespace: "systemui" description: "Use the new compose bouncer in SystemUI" @@ -496,6 +505,15 @@ flag { } } +flag { + name: "screenshot_scroll_crop_view_crash_fix" + namespace: "systemui" + description: "Mitigate crash on invalid computed range in CropView" + bug: "232633995" + metadata { + purpose: PURPOSE_BUGFIX + } +} flag { name: "screenshot_private_profile_behavior_fix" @@ -943,3 +961,10 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "glanceable_hub_gesture_handle" + namespace: "systemui" + description: "Shows a vertical bar at the right edge to indicate the user can swipe to open the glanceable hub" + bug: "339667383" +} diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt index 8ee8ea4b4f4c..feb1f5b17bef 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt @@ -2,16 +2,24 @@ package com.android.systemui.communal.ui.compose import androidx.compose.animation.core.tween import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.dimensionResource +import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.compose.animation.scene.Edge import com.android.compose.animation.scene.ElementKey @@ -26,6 +34,7 @@ import com.android.compose.animation.scene.Swipe import com.android.compose.animation.scene.SwipeDirection import com.android.compose.animation.scene.observableTransitionState import com.android.compose.animation.scene.transitions +import com.android.systemui.Flags import com.android.systemui.communal.shared.model.CommunalScenes import com.android.systemui.communal.shared.model.CommunalTransitionKeys import com.android.systemui.communal.ui.compose.extensions.allowGestures @@ -88,6 +97,8 @@ fun CommunalContainer( val currentSceneKey: SceneKey by viewModel.currentScene.collectAsStateWithLifecycle(CommunalScenes.Blank) val touchesAllowed by viewModel.touchesAllowed.collectAsStateWithLifecycle(initialValue = false) + val showGestureIndicator by + viewModel.showGestureIndicator.collectAsStateWithLifecycle(initialValue = false) val state: MutableSceneTransitionLayoutState = remember { MutableSceneTransitionLayoutState( initialScene = currentSceneKey, @@ -126,7 +137,19 @@ fun CommunalContainer( ) ) { // This scene shows nothing only allowing for transitions to the communal scene. - Box(modifier = Modifier.fillMaxSize()) + // TODO(b/339667383): remove this temporary swipe gesture handle + Row(modifier = Modifier.fillMaxSize(), horizontalArrangement = Arrangement.End) { + if (showGestureIndicator && Flags.glanceableHubGestureHandle()) { + Box( + modifier = + Modifier.height(220.dp) + .width(4.dp) + .align(Alignment.CenterVertically) + .background(color = Color.White, RoundedCornerShape(4.dp)) + ) + Spacer(modifier = Modifier.width(12.dp)) + } + } } scene( diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt index 6d8c47d84850..ca4ff837daa6 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt @@ -24,6 +24,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalView import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.compose.animation.scene.SceneScope +import com.android.systemui.compose.modifiers.sysuiResTag import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor import com.android.systemui.keyguard.ui.composable.blueprint.ComposableLockscreenSceneBlueprint import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel @@ -60,6 +61,6 @@ constructor( } val blueprint = blueprintByBlueprintId[blueprintId] ?: return - with(blueprint) { Content(modifier) } + with(blueprint) { Content(modifier.sysuiResTag("keyguard_root_view")) } } } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt index abff93d15c55..a39fa64dd45b 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt @@ -31,6 +31,7 @@ import androidx.compose.ui.unit.IntRect import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.compose.animation.scene.SceneScope import com.android.compose.modifiers.padding +import com.android.systemui.compose.modifiers.sysuiResTag import com.android.systemui.keyguard.ui.composable.LockscreenLongPress import com.android.systemui.keyguard.ui.composable.section.AmbientIndicationSection import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection @@ -129,7 +130,7 @@ constructor( with(lockSection) { LockIcon() } // Aligned to bottom and constrained to below the lock icon. - Column(modifier = Modifier.fillMaxWidth()) { + Column(modifier = Modifier.fillMaxWidth().sysuiResTag("keyguard_bottom_area")) { if (isUdfpsVisible && ambientIndicationSectionOptional.isPresent) { with(ambientIndicationSectionOptional.get()) { AmbientIndication(modifier = Modifier.fillMaxWidth()) diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt index 88b8298335aa..067315381773 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt @@ -36,7 +36,6 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.compose.animation.scene.SceneScope import com.android.compose.animation.scene.SceneTransitionLayout import com.android.compose.modifiers.thenIf -import com.android.systemui.compose.modifiers.sysuiResTag import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor import com.android.systemui.keyguard.ui.composable.blueprint.ClockScenes.largeClockScene import com.android.systemui.keyguard.ui.composable.blueprint.ClockScenes.smallClockScene @@ -80,7 +79,7 @@ constructor( } SceneTransitionLayout( - modifier = modifier.sysuiResTag("keyguard_clock_container"), + modifier = modifier, currentScene = currentScene, onChangeScene = {}, transitions = ClockTransition.defaultClockTransitions, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/data/repository/BrightnessPolicyRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/data/repository/BrightnessPolicyRepositoryImplTest.kt index 312c14df9505..fec56ed919de 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/data/repository/BrightnessPolicyRepositoryImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/data/repository/BrightnessPolicyRepositoryImplTest.kt @@ -18,9 +18,13 @@ package com.android.systemui.brightness.data.repository import android.content.applicationContext import android.os.UserManager -import androidx.test.ext.junit.runners.AndroidJUnit4 +import android.platform.test.annotations.DisableFlags +import android.platform.test.annotations.EnableFlags +import android.platform.test.flag.junit.FlagsParameterization +import android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf import androidx.test.filters.SmallTest import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin +import com.android.systemui.Flags.FLAG_ENFORCE_BRIGHTNESS_BASE_USER_RESTRICTION import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.kosmos.testDispatcher @@ -40,14 +44,18 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.anyInt import org.mockito.ArgumentMatchers.anyString +import platform.test.runner.parameterized.ParameterizedAndroidJunit4 +import platform.test.runner.parameterized.Parameters @SmallTest -@RunWith(AndroidJUnit4::class) -class BrightnessPolicyRepositoryImplTest : SysuiTestCase() { +@RunWith(ParameterizedAndroidJunit4::class) +class BrightnessPolicyRepositoryImplTest(flags: FlagsParameterization) : SysuiTestCase() { - private val kosmos = testKosmos() + init { + mSetFlagsRule.setFlagsParameterization(flags) + } - private val fakeUserRepository = kosmos.fakeUserRepository + private val kosmos = testKosmos() private val mockUserRestrictionChecker: UserRestrictionChecker = mock { whenever(checkIfRestrictionEnforced(any(), anyString(), anyInt())).thenReturn(null) @@ -130,7 +138,83 @@ class BrightnessPolicyRepositoryImplTest : SysuiTestCase() { } } - private companion object { - val RESTRICTION = UserManager.DISALLOW_CONFIG_BRIGHTNESS + @Test + @DisableFlags(FLAG_ENFORCE_BRIGHTNESS_BASE_USER_RESTRICTION) + fun brightnessBaseUserRestriction_flagOff_noRestriction() = + with(kosmos) { + testScope.runTest { + whenever( + mockUserRestrictionChecker.hasBaseUserRestriction( + any(), + eq(RESTRICTION), + eq(userRepository.getSelectedUserInfo().id) + ) + ) + .thenReturn(true) + + val restrictions by collectLastValue(underTest.restrictionPolicy) + + assertThat(restrictions).isEqualTo(PolicyRestriction.NoRestriction) + } + } + + @Test + fun bothRestrictions_returnsSetEnforcedAdminFromCheck() = + with(kosmos) { + testScope.runTest { + val enforcedAdmin: EnforcedAdmin = + EnforcedAdmin.createDefaultEnforcedAdminWithRestriction(RESTRICTION) + + whenever( + mockUserRestrictionChecker.checkIfRestrictionEnforced( + any(), + eq(RESTRICTION), + eq(userRepository.getSelectedUserInfo().id) + ) + ) + .thenReturn(enforcedAdmin) + + whenever( + mockUserRestrictionChecker.hasBaseUserRestriction( + any(), + eq(RESTRICTION), + eq(userRepository.getSelectedUserInfo().id) + ) + ) + .thenReturn(true) + + val restrictions by collectLastValue(underTest.restrictionPolicy) + + assertThat(restrictions).isEqualTo(PolicyRestriction.Restricted(enforcedAdmin)) + } + } + + @Test + @EnableFlags(FLAG_ENFORCE_BRIGHTNESS_BASE_USER_RESTRICTION) + fun brightnessBaseUserRestriction_flagOn_emptyRestriction() = + with(kosmos) { + testScope.runTest { + whenever( + mockUserRestrictionChecker.hasBaseUserRestriction( + any(), + eq(RESTRICTION), + eq(userRepository.getSelectedUserInfo().id) + ) + ) + .thenReturn(true) + + val restrictions by collectLastValue(underTest.restrictionPolicy) + + assertThat(restrictions).isEqualTo(PolicyRestriction.Restricted(EnforcedAdmin())) + } + } + + companion object { + private const val RESTRICTION = UserManager.DISALLOW_CONFIG_BRIGHTNESS + @JvmStatic + @Parameters(name = "{0}") + fun getParams(): List<FlagsParameterization> { + return allCombinationsOf(FLAG_ENFORCE_BRIGHTNESS_BASE_USER_RESTRICTION) + } } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/domain/interactor/BrightnessPolicyEnforcementInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/domain/interactor/BrightnessPolicyEnforcementInteractorTest.kt index 85a4bcf62223..11f523846268 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/domain/interactor/BrightnessPolicyEnforcementInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/domain/interactor/BrightnessPolicyEnforcementInteractorTest.kt @@ -48,7 +48,6 @@ class BrightnessPolicyEnforcementInteractorTest : SysuiTestCase() { private val kosmos = testKosmos() private val mockActivityStarter = kosmos.activityStarter - private val fakeBrightnessPolicyEnforcementInteractor = kosmos.fakeBrightnessPolicyRepository private val underTest = with(kosmos) { @@ -70,7 +69,18 @@ class BrightnessPolicyEnforcementInteractorTest : SysuiTestCase() { fakeBrightnessPolicyRepository.setCurrentUserRestricted() - assertThat(restriction).isInstanceOf(PolicyRestriction.Restricted::class.java) + assertThat(restriction) + .isEqualTo( + PolicyRestriction.Restricted( + EnforcedAdmin.createDefaultEnforcedAdminWithRestriction( + BrightnessPolicyRepository.RESTRICTION + ) + ) + ) + + fakeBrightnessPolicyRepository.setBaseUserRestriction() + + assertThat(restriction).isEqualTo(PolicyRestriction.Restricted(EnforcedAdmin())) } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt index 766798c2c2c3..83227e1fc597 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt @@ -204,14 +204,14 @@ class CommunalInteractorTest : SysuiTestCase() { } @Test - fun isCommunalAvailable_whenDreaming_true() = + fun isCommunalAvailable_whenKeyguardShowing_true() = testScope.runTest { val isAvailable by collectLastValue(underTest.isCommunalAvailable) assertThat(isAvailable).isFalse() keyguardRepository.setIsEncryptedOrLockdown(false) userRepository.setSelectedUserInfo(mainUser) - keyguardRepository.setDreaming(true) + keyguardRepository.setKeyguardShowing(true) assertThat(isAvailable).isTrue() } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt index 2d079d7488d0..be44339bab8d 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt @@ -51,6 +51,7 @@ import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository +import com.android.systemui.keyguard.domain.interactor.keyguardInteractor import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.StatusBarState @@ -141,6 +142,7 @@ class CommunalViewModelTest(flags: FlagsParameterization) : SysuiTestCase() { testScope, context.resources, kosmos.keyguardTransitionInteractor, + kosmos.keyguardInteractor, kosmos.communalInteractor, kosmos.communalTutorialInteractor, kosmos.shadeInteractor, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java index 8bfa5cff8b97..f5c86e092a26 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java @@ -31,8 +31,11 @@ import android.app.DreamManager; import android.content.res.Resources; import android.graphics.Region; import android.os.Handler; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; import android.testing.TestableLooper.RunWithLooper; import android.view.AttachedSurfaceControl; +import android.view.View; import android.view.ViewGroup; import android.view.ViewRootImpl; import android.view.ViewTreeObserver; @@ -42,6 +45,7 @@ import androidx.test.filters.SmallTest; import com.android.dream.lowlight.LowLightTransitionCoordinator; import com.android.keyguard.BouncerPanelExpansionCalculator; +import com.android.systemui.Flags; import com.android.systemui.SysuiTestCase; import com.android.systemui.ambient.touch.scrim.BouncerlessScrimController; import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor; @@ -94,6 +98,9 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase { ViewGroup mDreamOverlayContentView; @Mock + View mHubGestureIndicatorView; + + @Mock Handler mHandler; @Mock @@ -142,6 +149,7 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase { mDreamOverlayContainerView, mComplicationHostViewController, mDreamOverlayContentView, + mHubGestureIndicatorView, mDreamOverlayStatusBarViewController, mLowLightTransitionCoordinator, mBlurUtils, @@ -161,6 +169,18 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase { mDreamManager); } + @DisableFlags(Flags.FLAG_COMMUNAL_HUB) + @Test + public void testHubGestureIndicatorGoneWhenFlagOff() { + verify(mHubGestureIndicatorView, never()).setVisibility(View.VISIBLE); + } + + @EnableFlags({Flags.FLAG_COMMUNAL_HUB, Flags.FLAG_GLANCEABLE_HUB_GESTURE_HANDLE}) + @Test + public void testHubGestureIndicatorVisibleWhenFlagOn() { + verify(mHubGestureIndicatorView).setVisibility(View.VISIBLE); + } + @Test public void testRootSurfaceControlInsetSetOnAttach() { mController.onViewAttached(); diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/FingerprintPropertyRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/FingerprintPropertyRepositoryTest.kt new file mode 100644 index 000000000000..1e7ed6316f7f --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/FingerprintPropertyRepositoryTest.kt @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.systemui.keyguard.data.repository + +import android.hardware.fingerprint.FingerprintManager +import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepositoryImpl +import com.android.systemui.coroutines.collectLastValue +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.Captor +import org.mockito.Mock +import org.mockito.Mockito.verify +import org.mockito.junit.MockitoJUnit +import org.mockito.junit.MockitoRule + +@ExperimentalCoroutinesApi +@RunWith(AndroidJUnit4::class) +@SmallTest +class FingerprintPropertyRepositoryTest : SysuiTestCase() { + @JvmField @Rule val mockitoRule: MockitoRule = MockitoJUnit.rule() + private val testScope = TestScope() + private lateinit var underTest: FingerprintPropertyRepositoryImpl + @Mock private lateinit var fingerprintManager: FingerprintManager + @Captor + private lateinit var fingerprintCallbackCaptor: + ArgumentCaptor<IFingerprintAuthenticatorsRegisteredCallback> + + @Before + fun setup() { + underTest = + FingerprintPropertyRepositoryImpl( + testScope.backgroundScope, + Dispatchers.Main.immediate, + fingerprintManager, + ) + } + + @Test + fun propertiesInitialized_onStartFalse() = + testScope.runTest { + val propertiesInitialized by collectLastValue(underTest.propertiesInitialized) + assertThat(propertiesInitialized).isFalse() + } + + @Test + fun propertiesInitialized_onStartTrue() = + testScope.runTest { + // // collect sensorType to update fingerprintCallback before + // propertiesInitialized + // // is listened for + val sensorType by collectLastValue(underTest.sensorType) + runCurrent() + captureFingerprintCallback() + + fingerprintCallbackCaptor.value.onAllAuthenticatorsRegistered(emptyList()) + val propertiesInitialized by collectLastValue(underTest.propertiesInitialized) + assertThat(propertiesInitialized).isTrue() + } + + @Test + fun propertiesInitialized_updatedToTrue() = + testScope.runTest { + val propertiesInitialized by collectLastValue(underTest.propertiesInitialized) + assertThat(propertiesInitialized).isFalse() + + captureFingerprintCallback() + fingerprintCallbackCaptor.value.onAllAuthenticatorsRegistered(emptyList()) + assertThat(propertiesInitialized).isTrue() + } + + private fun captureFingerprintCallback() { + verify(fingerprintManager) + .addAuthenticatorsRegisteredCallback(fingerprintCallbackCaptor.capture()) + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModelTest.kt index 4226a9d89ad1..0551bfb89865 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModelTest.kt @@ -109,7 +109,7 @@ class MediaCarouselViewModelTest : SysuiTestCase() { assertThat(mediaControl2.instanceId).isEqualTo(instanceId2) assertThat(mediaControl1.instanceId).isEqualTo(instanceId1) - underTest.onAttached() + underTest.onReorderingAllowed() mediaControl1 = sortedMedia?.get(0) as MediaCommonViewModel.MediaControl mediaControl2 = sortedMedia?.get(1) as MediaCommonViewModel.MediaControl diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerTest.kt index bf48784407b8..02a81419ea78 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerTest.kt @@ -69,6 +69,15 @@ class QSTileIntentUserInputHandlerTest : SysuiTestCase() { } @Test + fun testPassesIntentToStarter_dismissShadeAndShowOverLockScreenWhenLocked() { + val intent = Intent("test.ACTION") + + underTest.handle(null, intent, true) + + verify(activityStarter).startActivity(eq(intent), eq(true), any(), eq(true)) + } + + @Test fun testPassesActivityPendingIntentToStarterAsPendingIntent() { val pendingIntent = mock<PendingIntent> { whenever(isActivity).thenReturn(true) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileDataInteractorTest.kt new file mode 100644 index 000000000000..c41ce2f7854c --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileDataInteractorTest.kt @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.impl.qr.domain.interactor + +import android.content.Intent +import android.os.UserHandle +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.qrcodescanner.controller.QRCodeScannerController +import com.android.systemui.qrcodescanner.controller.QRCodeScannerController.Callback +import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger +import com.android.systemui.qs.tiles.impl.qr.domain.model.QRCodeScannerTileModel +import com.android.systemui.util.mockito.argumentCaptor +import com.android.systemui.util.mockito.mock +import com.android.systemui.util.mockito.whenever +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.verify + +@SmallTest +@RunWith(AndroidJUnit4::class) +class QRCodeScannerTileDataInteractorTest : SysuiTestCase() { + + private val testUser = UserHandle.of(1)!! + private val testDispatcher = StandardTestDispatcher() + private val scope = TestScope(testDispatcher) + private val testIntent = mock<Intent>() + private val qrCodeScannerController = + mock<QRCodeScannerController> { + whenever(intent).thenReturn(testIntent) + whenever(isAbleToLaunchScannerActivity).thenReturn(false) + } + private val testAvailableModel = QRCodeScannerTileModel.Available(testIntent) + private val testUnavailableModel = QRCodeScannerTileModel.TemporarilyUnavailable + + private val underTest: QRCodeScannerTileDataInteractor = + QRCodeScannerTileDataInteractor( + testDispatcher, + scope.backgroundScope, + qrCodeScannerController, + ) + + @Test + fun availability_matchesController_cameraNotAvailable() = + scope.runTest { + val expectedAvailability = false + whenever(qrCodeScannerController.isCameraAvailable).thenReturn(false) + + val availability by collectLastValue(underTest.availability(testUser)) + + assertThat(availability).isEqualTo(expectedAvailability) + } + + @Test + fun availability_matchesController_cameraIsAvailable() = + scope.runTest { + val expectedAvailability = true + whenever(qrCodeScannerController.isCameraAvailable).thenReturn(true) + + val availability by collectLastValue(underTest.availability(testUser)) + + assertThat(availability).isEqualTo(expectedAvailability) + } + + @Test + fun data_matchesController() = + scope.runTest { + val captor = argumentCaptor<Callback>() + val lastData by + collectLastValue( + underTest.tileData(testUser, flowOf(DataUpdateTrigger.InitialRequest)) + ) + runCurrent() + + verify(qrCodeScannerController).addCallback(captor.capture()) + val callback = captor.value + + assertThat(lastData!!).isEqualTo(testUnavailableModel) + + whenever(qrCodeScannerController.isAbleToLaunchScannerActivity).thenReturn(true) + callback.onQRCodeScannerActivityChanged() + runCurrent() + assertThat(lastData!!).isEqualTo(testAvailableModel) + + whenever(qrCodeScannerController.isAbleToLaunchScannerActivity).thenReturn(false) + callback.onQRCodeScannerActivityChanged() + runCurrent() + assertThat(lastData!!).isEqualTo(testUnavailableModel) + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractorTest.kt new file mode 100644 index 000000000000..312f18029570 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractorTest.kt @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.impl.qr.domain.interactor + +import android.content.Intent +import android.platform.test.annotations.EnabledOnRavenwood +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject +import com.android.systemui.qs.tiles.base.actions.qsTileIntentUserInputHandler +import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx +import com.android.systemui.qs.tiles.impl.qr.domain.model.QRCodeScannerTileModel +import com.android.systemui.qs.tiles.impl.qr.qrCodeScannerTileUserActionInteractor +import com.android.systemui.util.mockito.mock +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@EnabledOnRavenwood +@RunWith(AndroidJUnit4::class) +class QRCodeScannerTileUserActionInteractorTest : SysuiTestCase() { + val kosmos = Kosmos() + private val inputHandler = kosmos.qsTileIntentUserInputHandler + private val underTest = kosmos.qrCodeScannerTileUserActionInteractor + private val intent = mock<Intent>() + + @Test + fun handleClick_available() = runTest { + val inputModel = QRCodeScannerTileModel.Available(intent) + + underTest.handleInput(QSTileInputTestKtx.click(inputModel)) + + QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput { + intent + } + } + + @Test + fun handleClick_temporarilyUnavailable() = runTest { + val inputModel = QRCodeScannerTileModel.TemporarilyUnavailable + + underTest.handleInput(QSTileInputTestKtx.click(inputModel)) + + QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledNoInputs() + } + + @Test + fun handleLongClick_available() = runTest { + val inputModel = QRCodeScannerTileModel.Available(intent) + + underTest.handleInput(QSTileInputTestKtx.longClick(inputModel)) + + QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledNoInputs() + } + + @Test + fun handleLongClick_temporarilyUnavailable() = runTest { + val inputModel = QRCodeScannerTileModel.TemporarilyUnavailable + + underTest.handleInput(QSTileInputTestKtx.longClick(inputModel)) + + QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledNoInputs() + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapperTest.kt new file mode 100644 index 000000000000..d26a21365f54 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapperTest.kt @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.impl.qr.ui + +import android.content.Intent +import android.graphics.drawable.TestStubDrawable +import android.widget.Switch +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.common.shared.model.Icon +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject +import com.android.systemui.qs.tiles.impl.qr.domain.model.QRCodeScannerTileModel +import com.android.systemui.qs.tiles.impl.qr.qsQRCodeScannerTileConfig +import com.android.systemui.qs.tiles.viewmodel.QSTileState +import com.android.systemui.util.mockito.mock +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class QRCodeScannerTileMapperTest : SysuiTestCase() { + private val kosmos = Kosmos() + private val config = kosmos.qsQRCodeScannerTileConfig + + private lateinit var mapper: QRCodeScannerTileMapper + + @Before + fun setup() { + mapper = + QRCodeScannerTileMapper( + context.orCreateTestableResources + .apply { + addOverride( + com.android.systemui.res.R.drawable.ic_qr_code_scanner, + TestStubDrawable() + ) + } + .resources, + context.theme + ) + } + + @Test + fun availableModel() { + val mockIntent = mock<Intent>() + val inputModel = QRCodeScannerTileModel.Available(mockIntent) + + val outputState = mapper.map(config, inputModel) + + val expectedState = + createQRCodeScannerTileState( + QSTileState.ActivationState.INACTIVE, + null, + ) + QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState) + } + + @Test + fun temporarilyUnavailableModel() { + val inputModel = QRCodeScannerTileModel.TemporarilyUnavailable + + val outputState = mapper.map(config, inputModel) + + val expectedState = + createQRCodeScannerTileState( + QSTileState.ActivationState.UNAVAILABLE, + context.getString( + com.android.systemui.res.R.string.qr_code_scanner_updating_secondary_label + ) + ) + QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState) + } + + private fun createQRCodeScannerTileState( + activationState: QSTileState.ActivationState, + secondaryLabel: String?, + ): QSTileState { + val label = context.getString(com.android.systemui.res.R.string.qr_code_scanner_title) + return QSTileState( + { + Icon.Loaded( + context.getDrawable(com.android.systemui.res.R.drawable.ic_qr_code_scanner)!!, + null + ) + }, + label, + activationState, + secondaryLabel, + setOf(QSTileState.UserAction.CLICK), + label, + null, + QSTileState.SideViewIcon.Chevron, + QSTileState.EnabledState.ENABLED, + Switch::class.qualifiedName + ) + } +} diff --git a/packages/SystemUI/res-keyguard/drawable/status_bar_user_chip_bg.xml b/packages/SystemUI/res-keyguard/drawable/status_bar_user_chip_bg.xml index a751f58344a9..370677ac0890 100644 --- a/packages/SystemUI/res-keyguard/drawable/status_bar_user_chip_bg.xml +++ b/packages/SystemUI/res-keyguard/drawable/status_bar_user_chip_bg.xml @@ -16,5 +16,5 @@ <shape xmlns:android="http://schemas.android.com/apk/res/android"> <solid android:color="@color/material_dynamic_neutral20" /> - <corners android:radius="@dimen/ongoing_call_chip_corner_radius" /> + <corners android:radius="@dimen/ongoing_activity_chip_corner_radius" /> </shape> diff --git a/packages/SystemUI/res/drawable/contrast_dialog_button_background.xml b/packages/SystemUI/res/drawable/contrast_dialog_button_background.xml deleted file mode 100644 index 4181220ed68c..000000000000 --- a/packages/SystemUI/res/drawable/contrast_dialog_button_background.xml +++ /dev/null @@ -1,46 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -* Copyright 2023, The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ ---> -<selector - xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"> - - <item android:state_selected="true"> - <shape android:shape="rectangle"> - <solid android:color="?androidprv:attr/colorSurfaceHighlight" /> - <stroke - android:color="?androidprv:attr/colorAccentPrimary" - android:width="@dimen/contrast_dialog_button_stroke_width" /> - <corners android:radius="@dimen/contrast_dialog_button_radius"/> - </shape> - </item> - - <item> - <layer-list> - <item android:top="@dimen/contrast_dialog_button_stroke_width" - android:bottom="@dimen/contrast_dialog_button_stroke_width" - android:left="@dimen/contrast_dialog_button_stroke_width" - android:right="@dimen/contrast_dialog_button_stroke_width"> - <shape android:shape="rectangle"> - <solid android:color="?androidprv:attr/colorSurfaceHighlight" /> - <corners android:radius="@dimen/contrast_dialog_button_radius"/> - </shape> - </item> - </layer-list> - </item> -</selector> diff --git a/packages/SystemUI/res/drawable/hub_handle.xml b/packages/SystemUI/res/drawable/hub_handle.xml new file mode 100644 index 000000000000..8bc276ffed9e --- /dev/null +++ b/packages/SystemUI/res/drawable/hub_handle.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<shape xmlns:android="http://schemas.android.com/apk/res/android"> + <corners android:radius="4dp" /> + <solid android:color="#FFFFFF" /> +</shape>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/ic_contrast_high.xml b/packages/SystemUI/res/drawable/ic_contrast_high.xml deleted file mode 100644 index aa5b5abc33aa..000000000000 --- a/packages/SystemUI/res/drawable/ic_contrast_high.xml +++ /dev/null @@ -1,25 +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. - --> -<vector android:autoMirrored="true" android:height="20dp" - android:viewportHeight="20" android:viewportWidth="66" - android:width="66dp" xmlns:android="http://schemas.android.com/apk/res/android"> - <path android:fillColor="#F2F1E8" - android:pathData="M0.5,8C0.5,3.858 3.858,0.5 8,0.5H58C62.142,0.5 65.5,3.858 65.5,8V12C65.5,16.142 62.142,19.5 58,19.5H8C3.858,19.5 0.5,16.142 0.5,12V8Z" - android:strokeColor="#1B1C17" android:strokeWidth="1"/> - <path android:fillColor="#1B1C17" android:pathData="M11,10m-6,0a6,6 0,1 1,12 0a6,6 0,1 1,-12 0"/> - <path android:fillColor="#1B1C17" android:pathData="M23,5L43,5A2,2 0,0 1,45 7L45,7A2,2 0,0 1,43 9L23,9A2,2 0,0 1,21 7L21,7A2,2 0,0 1,23 5z"/> - <path android:fillColor="#1B1C17" android:pathData="M23,11L55,11A2,2 0,0 1,57 13L57,13A2,2 0,0 1,55 15L23,15A2,2 0,0 1,21 13L21,13A2,2 0,0 1,23 11z"/> -</vector> diff --git a/packages/SystemUI/res/drawable/ic_contrast_medium.xml b/packages/SystemUI/res/drawable/ic_contrast_medium.xml deleted file mode 100644 index 89519b86b974..000000000000 --- a/packages/SystemUI/res/drawable/ic_contrast_medium.xml +++ /dev/null @@ -1,23 +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. - --> -<vector android:autoMirrored="true" android:height="20dp" - android:viewportHeight="20" android:viewportWidth="66" - android:width="66dp" xmlns:android="http://schemas.android.com/apk/res/android"> - <path android:fillColor="#F2F1E8" android:pathData="M0,8C0,3.582 3.582,0 8,0H58C62.418,0 66,3.582 66,8V12C66,16.418 62.418,20 58,20H8C3.582,20 0,16.418 0,12V8Z"/> - <path android:fillColor="#919283" android:pathData="M11,10m-6,0a6,6 0,1 1,12 0a6,6 0,1 1,-12 0"/> - <path android:fillColor="#919283" android:pathData="M23,5L43,5A2,2 0,0 1,45 7L45,7A2,2 0,0 1,43 9L23,9A2,2 0,0 1,21 7L21,7A2,2 0,0 1,23 5z"/> - <path android:fillColor="#919283" android:pathData="M23,11L55,11A2,2 0,0 1,57 13L57,13A2,2 0,0 1,55 15L23,15A2,2 0,0 1,21 13L21,13A2,2 0,0 1,23 11z"/> -</vector> diff --git a/packages/SystemUI/res/drawable/ic_contrast_standard.xml b/packages/SystemUI/res/drawable/ic_contrast_standard.xml deleted file mode 100644 index f914975823da..000000000000 --- a/packages/SystemUI/res/drawable/ic_contrast_standard.xml +++ /dev/null @@ -1,23 +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. - --> -<vector android:autoMirrored="true" android:height="20dp" - android:viewportHeight="20" android:viewportWidth="66" - android:width="66dp" xmlns:android="http://schemas.android.com/apk/res/android"> - <path android:fillColor="#C7C8B7" android:pathData="M0,8C0,3.582 3.582,0 8,0H58C62.418,0 66,3.582 66,8V12C66,16.418 62.418,20 58,20H8C3.582,20 0,16.418 0,12V8Z"/> - <path android:fillColor="#919283" android:pathData="M11,10m-6,0a6,6 0,1 1,12 0a6,6 0,1 1,-12 0"/> - <path android:fillColor="#919283" android:pathData="M23,5L43,5A2,2 0,0 1,45 7L45,7A2,2 0,0 1,43 9L23,9A2,2 0,0 1,21 7L21,7A2,2 0,0 1,23 5z"/> - <path android:fillColor="#919283" android:pathData="M23,11L55,11A2,2 0,0 1,57 13L57,13A2,2 0,0 1,55 15L23,15A2,2 0,0 1,21 13L21,13A2,2 0,0 1,23 11z"/> -</vector> diff --git a/packages/SystemUI/res/drawable/ongoing_call_chip_bg.xml b/packages/SystemUI/res/drawable/ongoing_activity_chip_bg.xml index bdd6270bb50b..b9a4cbfc683e 100644 --- a/packages/SystemUI/res/drawable/ongoing_call_chip_bg.xml +++ b/packages/SystemUI/res/drawable/ongoing_activity_chip_bg.xml @@ -16,5 +16,5 @@ <shape xmlns:android="http://schemas.android.com/apk/res/android"> <solid android:color="?android:attr/colorAccent" /> - <corners android:radius="@dimen/ongoing_call_chip_corner_radius" /> + <corners android:radius="@dimen/ongoing_activity_chip_corner_radius" /> </shape>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout-land/biometric_prompt_constraint_layout.xml b/packages/SystemUI/res/layout-land/biometric_prompt_constraint_layout.xml index 5755dcd70a82..01b9f7e2e38a 100644 --- a/packages/SystemUI/res/layout-land/biometric_prompt_constraint_layout.xml +++ b/packages/SystemUI/res/layout-land/biometric_prompt_constraint_layout.xml @@ -26,8 +26,8 @@ android:layout_height="match_parent"> android:paddingVertical="16dp" android:visibility="visible" app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toStartOf="@+id/rightGuideline" - app:layout_constraintStart_toStartOf="@+id/leftGuideline" + app:layout_constraintRight_toLeftOf="@+id/rightGuideline" + app:layout_constraintLeft_toLeftOf="@+id/leftGuideline" app:layout_constraintTop_toTopOf="@+id/topGuideline" /> <com.android.systemui.biometrics.BiometricPromptLottieViewWrapper @@ -35,8 +35,8 @@ android:layout_height="match_parent"> android:layout_width="wrap_content" android:layout_height="wrap_content" app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" + app:layout_constraintRight_toRightOf="parent" + app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintTop_toTopOf="parent" tools:srcCompat="@tools:sample/avatars" /> @@ -63,8 +63,8 @@ android:layout_height="match_parent"> android:paddingTop="24dp" android:fadeScrollbars="false" app:layout_constraintBottom_toTopOf="@+id/button_bar" - app:layout_constraintEnd_toStartOf="@+id/midGuideline" - app:layout_constraintStart_toStartOf="@id/leftGuideline" + app:layout_constraintRight_toLeftOf="@+id/midGuideline" + app:layout_constraintLeft_toLeftOf="@id/leftGuideline" app:layout_constraintTop_toTopOf="@+id/topGuideline"> <androidx.constraintlayout.widget.ConstraintLayout @@ -89,7 +89,7 @@ android:layout_height="match_parent"> android:layout_width="0dp" android:layout_height="wrap_content" android:textAlignment="viewStart" - android:paddingLeft="16dp" + android:paddingStart="16dp" app:layout_constraintBottom_toBottomOf="@+id/logo" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@+id/logo" @@ -209,6 +209,7 @@ android:layout_height="match_parent"> android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical" + app:guidelineUseRtl="false" app:layout_constraintGuide_begin="@dimen/biometric_dialog_border_padding" /> <androidx.constraintlayout.widget.Guideline @@ -216,6 +217,7 @@ android:layout_height="match_parent"> android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical" + app:guidelineUseRtl="false" app:layout_constraintGuide_end="@dimen/biometric_dialog_border_padding" /> <androidx.constraintlayout.widget.Guideline @@ -223,6 +225,7 @@ android:layout_height="match_parent"> android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical" + app:guidelineUseRtl="false" app:layout_constraintGuide_begin="406dp" /> <androidx.constraintlayout.widget.Guideline diff --git a/packages/SystemUI/res/layout/biometric_prompt_button_bar.xml b/packages/SystemUI/res/layout/biometric_prompt_button_bar.xml index 4d2310a2a6ca..0bbe73c36fc7 100644 --- a/packages/SystemUI/res/layout/biometric_prompt_button_bar.xml +++ b/packages/SystemUI/res/layout/biometric_prompt_button_bar.xml @@ -27,7 +27,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" - android:layout_marginLeft="24dp" + android:layout_marginStart="24dp" android:ellipsize="end" android:maxLines="2" android:visibility="invisible" @@ -41,7 +41,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" - android:layout_marginLeft="24dp" + android:layout_marginStart="24dp" android:text="@string/cancel" android:visibility="invisible" app:layout_constraintBottom_toBottomOf="parent" @@ -54,7 +54,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" - android:layout_marginLeft="24dp" + android:layout_marginStart="24dp" android:visibility="invisible" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" /> @@ -66,7 +66,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" - android:layout_marginRight="24dp" + android:layout_marginEnd="24dp" android:ellipsize="end" android:maxLines="2" android:text="@string/biometric_dialog_confirm" @@ -81,7 +81,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" - android:layout_marginRight="24dp" + android:layout_marginEnd="24dp" android:ellipsize="end" android:maxLines="2" android:text="@string/biometric_dialog_try_again" diff --git a/packages/SystemUI/res/layout/contrast_dialog.xml b/packages/SystemUI/res/layout/contrast_dialog.xml deleted file mode 100644 index 8e885cf39e2b..000000000000 --- a/packages/SystemUI/res/layout/contrast_dialog.xml +++ /dev/null @@ -1,127 +0,0 @@ -<?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. - --> - -<LinearLayout - xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="horizontal"> - - <Space - android:layout_width="0dp" - android:layout_height="match_parent" - android:layout_weight="1"/> - - <LinearLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:orientation="vertical"> - - <FrameLayout - android:id="@+id/contrast_button_standard" - android:layout_width="@dimen/contrast_dialog_button_total_size" - android:layout_height="@dimen/contrast_dialog_button_total_size" - android:background="@drawable/contrast_dialog_button_background"> - - <ImageView - android:layout_gravity="center" - android:layout_height="wrap_content" - android:layout_width="wrap_content" - android:src="@drawable/ic_contrast_standard"/> - </FrameLayout> - - <TextView - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="@dimen/contrast_dialog_button_text_spacing" - android:gravity="center_horizontal|top" - android:textSize="@dimen/contrast_dialog_button_text_size" - android:text="@string/quick_settings_contrast_standard" - android:textColor="?androidprv:attr/textColorPrimary"/> - </LinearLayout> - - <Space - android:layout_width="@dimen/contrast_dialog_button_horizontal_spacing" - android:layout_height="match_parent" /> - - <LinearLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:orientation="vertical"> - - <FrameLayout - android:id="@+id/contrast_button_medium" - android:layout_width="@dimen/contrast_dialog_button_total_size" - android:layout_height="@dimen/contrast_dialog_button_total_size" - android:background="@drawable/contrast_dialog_button_background"> - - <ImageView - android:layout_gravity="center" - android:layout_height="wrap_content" - android:layout_width="wrap_content" - android:src="@drawable/ic_contrast_medium"/> - </FrameLayout> - - <TextView - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="@dimen/contrast_dialog_button_text_spacing" - android:gravity="center_horizontal|top" - android:textSize="@dimen/contrast_dialog_button_text_size" - android:text="@string/quick_settings_contrast_medium" - android:textColor="?androidprv:attr/textColorPrimary"/> - </LinearLayout> - - <Space - android:layout_width="@dimen/contrast_dialog_button_horizontal_spacing" - android:layout_height="match_parent" /> - - <LinearLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:orientation="vertical"> - - <FrameLayout - android:id="@+id/contrast_button_high" - android:layout_width="@dimen/contrast_dialog_button_total_size" - android:layout_height="@dimen/contrast_dialog_button_total_size" - android:background="@drawable/contrast_dialog_button_background"> - - <ImageView - android:layout_gravity="center" - android:layout_height="wrap_content" - android:layout_width="wrap_content" - android:src="@drawable/ic_contrast_high"/> - - </FrameLayout> - - <TextView - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="@dimen/contrast_dialog_button_text_spacing" - android:gravity="center_horizontal|top" - android:textSize="@dimen/contrast_dialog_button_text_size" - android:text="@string/quick_settings_contrast_high" - android:textColor="?androidprv:attr/textColorPrimary"/> - </LinearLayout> - - <Space - android:layout_width="0dp" - android:layout_height="match_parent" - android:layout_weight="1"/> -</LinearLayout> diff --git a/packages/SystemUI/res/layout/dream_overlay_container.xml b/packages/SystemUI/res/layout/dream_overlay_container.xml index 19fb874ea2be..4234fca55e3c 100644 --- a/packages/SystemUI/res/layout/dream_overlay_container.xml +++ b/packages/SystemUI/res/layout/dream_overlay_container.xml @@ -21,6 +21,19 @@ android:layout_width="match_parent" android:layout_height="match_parent"> + <ImageView + android:id="@+id/glanceable_hub_handle" + android:layout_width="4dp" + android:layout_height="220dp" + android:layout_centerVertical="true" + android:layout_marginEnd="12dp" + android:background="@drawable/hub_handle" + android:visibility="gone" + android:contentDescription="UI indicator for swiping open the glanceable hub" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + <androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/dream_overlay_content" android:layout_width="match_parent" diff --git a/packages/SystemUI/res/layout/ongoing_call_chip.xml b/packages/SystemUI/res/layout/ongoing_activity_chip.xml index 6a0217ec5fe8..a33be12a655a 100644 --- a/packages/SystemUI/res/layout/ongoing_call_chip.xml +++ b/packages/SystemUI/res/layout/ongoing_activity_chip.xml @@ -17,43 +17,45 @@ the chip. --> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/ongoing_call_chip" + android:id="@+id/ongoing_activity_chip" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_gravity="center_vertical|start" android:layout_marginStart="5dp" > - <com.android.systemui.statusbar.phone.ongoingcall.OngoingCallBackgroundContainer - android:id="@+id/ongoing_call_chip_background" + <!-- TODO(b/332662551): Update this content description when this supports more than just + phone calls. --> + <com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer + android:id="@+id/ongoing_activity_chip_background" android:layout_width="wrap_content" android:layout_height="@dimen/ongoing_appops_chip_height" android:layout_gravity="center_vertical" android:gravity="center" - android:background="@drawable/ongoing_call_chip_bg" - android:paddingStart="@dimen/ongoing_call_chip_side_padding" - android:paddingEnd="@dimen/ongoing_call_chip_side_padding" + android:background="@drawable/ongoing_activity_chip_bg" + android:paddingStart="@dimen/ongoing_activity_chip_side_padding" + android:paddingEnd="@dimen/ongoing_activity_chip_side_padding" android:contentDescription="@string/ongoing_phone_call_content_description" android:minWidth="@dimen/min_clickable_item_size" > <ImageView android:src="@*android:drawable/ic_phone" - android:layout_width="@dimen/ongoing_call_chip_icon_size" - android:layout_height="@dimen/ongoing_call_chip_icon_size" + android:layout_width="@dimen/ongoing_activity_chip_icon_size" + android:layout_height="@dimen/ongoing_activity_chip_icon_size" android:tint="?android:attr/colorPrimary" /> - <com.android.systemui.statusbar.phone.ongoingcall.OngoingCallChronometer - android:id="@+id/ongoing_call_chip_time" + <com.android.systemui.statusbar.chips.ui.view.ChipChronometer + android:id="@+id/ongoing_activity_chip_time" android:layout_width="wrap_content" android:layout_height="wrap_content" android:singleLine="true" android:gravity="center|start" - android:paddingStart="@dimen/ongoing_call_chip_icon_text_padding" + android:paddingStart="@dimen/ongoing_activity_chip_icon_text_padding" android:textAppearance="@android:style/TextAppearance.Material.Small" android:fontFamily="@*android:string/config_headlineFontFamily" android:textColor="?android:attr/colorPrimary" /> - </com.android.systemui.statusbar.phone.ongoingcall.OngoingCallBackgroundContainer> + </com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer> </FrameLayout> diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml index 452bc317e2d5..4247c7eef0d0 100644 --- a/packages/SystemUI/res/layout/status_bar.xml +++ b/packages/SystemUI/res/layout/status_bar.xml @@ -99,7 +99,7 @@ android:gravity="center_vertical|start" /> - <include layout="@layout/ongoing_call_chip" /> + <include layout="@layout/ongoing_activity_chip" /> <com.android.systemui.statusbar.AlphaOptimizedFrameLayout android:id="@+id/notification_icon_area" diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 873ddd0179bf..9d0319c2471b 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -1713,12 +1713,12 @@ <dimen name="wallet_button_horizontal_padding">24dp</dimen> <dimen name="wallet_button_vertical_padding">8dp</dimen> - <!-- Ongoing call chip --> - <dimen name="ongoing_call_chip_side_padding">12dp</dimen> - <dimen name="ongoing_call_chip_icon_size">16dp</dimen> + <!-- Ongoing activity chip --> + <dimen name="ongoing_activity_chip_side_padding">12dp</dimen> + <dimen name="ongoing_activity_chip_icon_size">16dp</dimen> <!-- The padding between the icon and the text. --> - <dimen name="ongoing_call_chip_icon_text_padding">4dp</dimen> - <dimen name="ongoing_call_chip_corner_radius">28dp</dimen> + <dimen name="ongoing_activity_chip_icon_text_padding">4dp</dimen> + <dimen name="ongoing_activity_chip_corner_radius">28dp</dimen> <!-- Status bar user chip --> <dimen name="status_bar_user_chip_avatar_size">16dp</dimen> @@ -1963,15 +1963,6 @@ <dimen name="broadcast_dialog_btn_minHeight">44dp</dimen> <dimen name="broadcast_dialog_margin">16dp</dimen> - <!-- Contrast dialog --> - <dimen name="contrast_dialog_button_total_size">90dp</dimen> - <dimen name="contrast_dialog_button_inner_size">82dp</dimen> - <dimen name="contrast_dialog_button_radius">20dp</dimen> - <dimen name="contrast_dialog_button_stroke_width">4dp</dimen> - <dimen name="contrast_dialog_button_text_size">14sp</dimen> - <dimen name="contrast_dialog_button_text_spacing">4dp</dimen> - <dimen name="contrast_dialog_button_horizontal_spacing">16dp</dimen> - <!-- Shadow for dream overlay clock complication --> <dimen name="dream_overlay_clock_key_text_shadow_dx">0dp</dimen> <dimen name="dream_overlay_clock_key_text_shadow_dy">0dp</dimen> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index aecc9066b552..8da8316f624b 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -902,15 +902,6 @@ <!-- QuickSettings: Label for the toggle that controls whether One-handed mode is enabled. [CHAR LIMIT=NONE] --> <string name="quick_settings_onehanded_label">One-handed mode</string> - <!-- QuickSettings: Contrast tile [CHAR LIMIT=NONE] --> - <string name="quick_settings_contrast_label">Contrast</string> - <!-- QuickSettings: Contrast tile description: standard [CHAR LIMIT=NONE] --> - <string name="quick_settings_contrast_standard">Standard</string> - <!-- QuickSettings: Contrast tile description: medium [CHAR LIMIT=NONE] --> - <string name="quick_settings_contrast_medium">Medium</string> - <!-- QuickSettings: Contrast tile description: high [CHAR LIMIT=NONE] --> - <string name="quick_settings_contrast_high">High</string> - <!-- Hearing devices --> <!-- QuickSettings: Hearing devices [CHAR LIMIT=NONE] --> <string name="quick_settings_hearing_devices_label">Hearing devices</string> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 2c4cdb9ee796..393a1aaec74a 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -523,10 +523,6 @@ <item name="android:windowExitAnimation">@anim/instant_fade_out</item> </style> - <style name="Theme.SystemUI.ContrastDialog" parent="@android:style/Theme.DeviceDefault.Dialog"> - <item name="android:windowBackground">@android:color/transparent</item> - </style> - <style name="Theme.SystemUI.QuickSettings.Dialog" parent="@style/Theme.SystemUI.Dialog.QuickSettings"> </style> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java deleted file mode 100644 index 0b0df833e916..000000000000 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (C) 2016 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.shared.recents.model; - -import static android.app.WindowConfiguration.ROTATION_UNDEFINED; -import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; -import static android.content.res.Configuration.ORIENTATION_UNDEFINED; -import static android.graphics.Bitmap.Config.ARGB_8888; - -import android.graphics.Bitmap; -import android.graphics.Color; -import android.graphics.Point; -import android.graphics.Rect; -import android.hardware.HardwareBuffer; -import android.util.Log; -import android.view.WindowInsetsController.Appearance; -import android.window.TaskSnapshot; - -import java.util.HashMap; - -/** - * Data for a single thumbnail. - */ -public class ThumbnailData { - - public final Bitmap thumbnail; - public int orientation; - public int rotation; - public Rect insets; - public Rect letterboxInsets; - public boolean reducedResolution; - public boolean isRealSnapshot; - public boolean isTranslucent; - public int windowingMode; - public @Appearance int appearance; - public float scale; - public long snapshotId; - - public ThumbnailData() { - thumbnail = null; - orientation = ORIENTATION_UNDEFINED; - rotation = ROTATION_UNDEFINED; - insets = new Rect(); - letterboxInsets = new Rect(); - reducedResolution = false; - scale = 1f; - isRealSnapshot = true; - isTranslucent = false; - windowingMode = WINDOWING_MODE_UNDEFINED; - snapshotId = 0; - } - - public void recycleBitmap() { - if (thumbnail != null) { - thumbnail.recycle(); - } - } - - private static Bitmap makeThumbnail(TaskSnapshot snapshot) { - Bitmap thumbnail = null; - try (final HardwareBuffer buffer = snapshot.getHardwareBuffer()) { - if (buffer != null) { - thumbnail = Bitmap.wrapHardwareBuffer(buffer, snapshot.getColorSpace()); - } - } catch (IllegalArgumentException ex) { - // TODO(b/157562905): Workaround for a crash when we get a snapshot without this state - Log.e("ThumbnailData", "Unexpected snapshot without USAGE_GPU_SAMPLED_IMAGE: " - + snapshot.getHardwareBuffer(), ex); - } - if (thumbnail == null) { - Point taskSize = snapshot.getTaskSize(); - thumbnail = Bitmap.createBitmap(taskSize.x, taskSize.y, ARGB_8888); - thumbnail.eraseColor(Color.BLACK); - } - return thumbnail; - } - - public static HashMap<Integer, ThumbnailData> wrap(int[] taskIds, TaskSnapshot[] snapshots) { - HashMap<Integer, ThumbnailData> temp = new HashMap<>(); - if (taskIds == null || snapshots == null || taskIds.length != snapshots.length) { - return temp; - } - - for (int i = snapshots.length - 1; i >= 0; i--) { - temp.put(taskIds[i], new ThumbnailData(snapshots[i])); - } - return temp; - } - - public ThumbnailData(TaskSnapshot snapshot) { - thumbnail = makeThumbnail(snapshot); - insets = new Rect(snapshot.getContentInsets()); - letterboxInsets = new Rect(snapshot.getLetterboxInsets()); - orientation = snapshot.getOrientation(); - rotation = snapshot.getRotation(); - reducedResolution = snapshot.isLowResolution(); - // TODO(b/149579527): Pass task size instead of computing scale. - // Assume width and height were scaled the same; compute scale only for width - scale = (float) thumbnail.getWidth() / snapshot.getTaskSize().x; - isRealSnapshot = snapshot.isRealSnapshot(); - isTranslucent = snapshot.isTranslucent(); - windowingMode = snapshot.getWindowingMode(); - appearance = snapshot.getAppearance(); - snapshotId = snapshot.getId(); - } -} diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.kt new file mode 100644 index 000000000000..dcf7754221bb --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.kt @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2016 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.shared.recents.model + +import android.app.WindowConfiguration +import android.content.res.Configuration +import android.graphics.Bitmap +import android.graphics.Bitmap.Config.ARGB_8888 +import android.graphics.Color +import android.graphics.Rect +import android.util.Log +import android.view.WindowInsetsController.Appearance +import android.window.TaskSnapshot + +/** Data for a single thumbnail. */ +data class ThumbnailData( + val thumbnail: Bitmap? = null, + var orientation: Int = Configuration.ORIENTATION_UNDEFINED, + @JvmField var rotation: Int = WindowConfiguration.ROTATION_UNDEFINED, + @JvmField var insets: Rect = Rect(), + @JvmField var letterboxInsets: Rect = Rect(), + @JvmField var reducedResolution: Boolean = false, + @JvmField var isRealSnapshot: Boolean = true, + var isTranslucent: Boolean = false, + @JvmField var windowingMode: Int = WindowConfiguration.WINDOWING_MODE_UNDEFINED, + @JvmField @Appearance var appearance: Int = 0, + @JvmField var scale: Float = 1f, + var snapshotId: Long = 0, +) { + fun recycleBitmap() { + thumbnail?.recycle() + } + + companion object { + private fun makeThumbnail(snapshot: TaskSnapshot): Bitmap { + var thumbnail: Bitmap? = null + try { + snapshot.hardwareBuffer?.use { buffer -> + thumbnail = Bitmap.wrapHardwareBuffer(buffer, snapshot.colorSpace) + } + } catch (ex: IllegalArgumentException) { + // TODO(b/157562905): Workaround for a crash when we get a snapshot without this + // state + Log.e( + "ThumbnailData", + "Unexpected snapshot without USAGE_GPU_SAMPLED_IMAGE: " + + "${snapshot.hardwareBuffer}", + ex + ) + } + + return thumbnail + ?: Bitmap.createBitmap(snapshot.taskSize.x, snapshot.taskSize.y, ARGB_8888).apply { + eraseColor(Color.BLACK) + } + } + + @JvmStatic + fun wrap(taskIds: IntArray?, snapshots: Array<TaskSnapshot>?): HashMap<Int, ThumbnailData> { + return if (taskIds == null || snapshots == null || taskIds.size != snapshots.size) { + HashMap() + } else { + HashMap(taskIds.associateWith { taskId -> fromSnapshot(snapshots[taskId]) }) + } + } + + @JvmStatic + fun fromSnapshot(snapshot: TaskSnapshot): ThumbnailData { + val thumbnail = makeThumbnail(snapshot) + return ThumbnailData( + thumbnail = thumbnail, + insets = Rect(snapshot.contentInsets), + letterboxInsets = Rect(snapshot.letterboxInsets), + orientation = snapshot.orientation, + rotation = snapshot.rotation, + reducedResolution = snapshot.isLowResolution, + // TODO(b/149579527): Pass task size instead of computing scale. + // Assume width and height were scaled the same; compute scale only for width + scale = thumbnail.width.toFloat() / snapshot.taskSize.x, + isRealSnapshot = snapshot.isRealSnapshot, + isTranslucent = snapshot.isTranslucent, + windowingMode = snapshot.windowingMode, + appearance = snapshot.appearance, + snapshotId = snapshot.id, + ) + } + } +} diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java index ca63483f656a..845ca5e8b9ec 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java @@ -147,7 +147,7 @@ public class ActivityManagerWrapper { Log.w(TAG, "Failed to retrieve task snapshot", e); } if (snapshot != null) { - return new ThumbnailData(snapshot); + return ThumbnailData.fromSnapshot(snapshot); } else { return new ThumbnailData(); } @@ -167,7 +167,7 @@ public class ActivityManagerWrapper { Log.w(TAG, "Failed to take task snapshot", e); } if (snapshot != null) { - return new ThumbnailData(snapshot); + return ThumbnailData.fromSnapshot(snapshot); } else { return new ThumbnailData(); } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java index a6e04cec5f86..bbf46984208f 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java @@ -42,7 +42,7 @@ public class RecentsAnimationControllerCompat { try { final TaskSnapshot snapshot = mAnimationController.screenshotTask(taskId); if (snapshot != null) { - return new ThumbnailData(snapshot); + return ThumbnailData.fromSnapshot(snapshot); } } catch (RemoteException e) { Log.e(TAG, "Failed to screenshot task", e); diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java index 473719fa76df..cf8ec62f19ef 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java @@ -351,7 +351,7 @@ public class TaskStackChangeListeners { case ON_TASK_SNAPSHOT_CHANGED: { Trace.beginSection("onTaskSnapshotChanged"); final TaskSnapshot snapshot = (TaskSnapshot) msg.obj; - final ThumbnailData thumbnail = new ThumbnailData(snapshot); + final ThumbnailData thumbnail = ThumbnailData.fromSnapshot(snapshot); boolean snapshotConsumed = false; for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) { boolean consumed = mTaskStackListeners.get(i).onTaskSnapshotChanged( diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java index 1f0459978c3c..d5e911efe570 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java @@ -37,7 +37,6 @@ import androidx.dynamicanimation.animation.SpringForce; import androidx.recyclerview.widget.RecyclerView; import com.android.internal.annotations.VisibleForTesting; -import com.android.systemui.Flags; import java.util.HashMap; @@ -339,15 +338,11 @@ class MenuAnimationController { mMenuView.updateMenuMoveToTucked(/* isMoveToTucked= */ true); final PointF position = mMenuView.getMenuPosition(); final PointF tuckedPosition = getTuckedMenuPosition(); - if (Flags.floatingMenuAnimatedTuck()) { - flingThenSpringMenuWith(DynamicAnimation.TRANSLATION_X, - Math.signum(tuckedPosition.x - position.x) * ESCAPE_VELOCITY, - FLING_FRICTION_SCALAR, - createDefaultSpringForce(), - tuckedPosition.x); - } else { - moveToPosition(tuckedPosition); - } + flingThenSpringMenuWith(DynamicAnimation.TRANSLATION_X, + Math.signum(tuckedPosition.x - position.x) * ESCAPE_VELOCITY, + FLING_FRICTION_SCALAR, + createDefaultSpringForce(), + tuckedPosition.x); // Keep the touch region let users could click extra space to pop up the menu view // from the screen edge @@ -359,23 +354,19 @@ class MenuAnimationController { void moveOutEdgeAndShow() { mMenuView.updateMenuMoveToTucked(/* isMoveToTucked= */ false); - if (Flags.floatingMenuAnimatedTuck()) { - PointF position = mMenuView.getMenuPosition(); - springMenuWith(DynamicAnimation.TRANSLATION_X, - createDefaultSpringForce(), - 0, - position.x, - true - ); - springMenuWith(DynamicAnimation.TRANSLATION_Y, - createDefaultSpringForce(), - 0, - position.y, - true - ); - } else { - mMenuView.onPositionChanged(); - } + PointF position = mMenuView.getMenuPosition(); + springMenuWith(DynamicAnimation.TRANSLATION_X, + createDefaultSpringForce(), + 0, + position.x, + true + ); + springMenuWith(DynamicAnimation.TRANSLATION_Y, + createDefaultSpringForce(), + 0, + position.y, + true + ); mMenuView.onEdgeChangedIfNeeded(); } diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java index be75e1035ea6..9d9e7dfb7032 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java @@ -321,22 +321,6 @@ class MenuView extends FrameLayout implements if (mMoveToTuckedListener != null) { mMoveToTuckedListener.onMoveToTuckedChanged(isMoveToTucked); } - - if (!Flags.floatingMenuAnimatedTuck()) { - if (isMoveToTucked) { - final float halfWidth = getMenuWidth() / 2.0f; - final boolean isOnLeftSide = mMenuAnimationController.isOnLeftSide(); - final Rect clipBounds = new Rect( - (int) (!isOnLeftSide ? 0 : halfWidth), - 0, - (int) (!isOnLeftSide ? halfWidth : getMenuWidth()), - getMenuHeight() - ); - setClipBounds(clipBounds); - } else { - setClipBounds(null); - } - } } diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java index 6dce1bb22921..0c67c5093faf 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java @@ -322,9 +322,8 @@ class MenuViewLayer extends FrameLayout implements } addView(mMessageView, LayerIndex.MESSAGE_VIEW); - if (Flags.floatingMenuAnimatedTuck()) { - setClipChildren(true); - } + setClipChildren(true); + setClickable(false); setFocusable(false); setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO); @@ -476,10 +475,8 @@ class MenuViewLayer extends FrameLayout implements mMenuAnimationController.startTuckedAnimationPreview(); } - if (Flags.floatingMenuAnimatedTuck()) { - if (!mMenuView.isMoveToTucked()) { - setClipBounds(null); - } + if (!mMenuView.isMoveToTucked()) { + setClipBounds(null); } mMenuView.onArrivalAtPosition(false); } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepository.kt index cc524840947b..ca03a00cca0c 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepository.kt @@ -34,6 +34,7 @@ import android.hardware.biometrics.events.AuthenticationStoppedInfo import android.hardware.biometrics.events.AuthenticationSucceededInfo import android.hardware.face.FaceManager import android.hardware.fingerprint.FingerprintManager +import android.util.Log import com.android.systemui.biometrics.shared.model.AuthenticationReason import com.android.systemui.biometrics.shared.model.AuthenticationReason.SettingsOperations import com.android.systemui.biometrics.shared.model.AuthenticationState @@ -52,6 +53,7 @@ import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filterIsInstance import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.shareIn /** A repository for the state of biometric authentication. */ @@ -85,6 +87,7 @@ constructor( private val authenticationState: Flow<AuthenticationState> = conflatedCallbackFlow { val updateAuthenticationState = { state: AuthenticationState -> + Log.d(TAG, "authenticationState updated: $state") trySendWithFailureLogging(state, TAG, "Error sending AuthenticationState state") } @@ -187,6 +190,7 @@ constructor( it.biometricSourceType == BiometricSourceType.FINGERPRINT) } .map { it.requestReason } + .onEach { Log.d(TAG, "fingerprintAuthenticationReason updated: $it") } override val fingerprintAcquiredStatus: Flow<FingerprintAuthenticationStatus> = authenticationState diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FingerprintPropertyRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FingerprintPropertyRepository.kt index 40d38dd83154..6b61adce3c84 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FingerprintPropertyRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FingerprintPropertyRepository.kt @@ -30,10 +30,10 @@ import com.android.systemui.biometrics.shared.model.SensorStrength import com.android.systemui.biometrics.shared.model.toSensorStrength import com.android.systemui.biometrics.shared.model.toSensorType import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging -import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope @@ -42,6 +42,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.withContext @@ -52,7 +53,7 @@ import kotlinx.coroutines.withContext */ interface FingerprintPropertyRepository { /** Whether the fingerprint properties have been initialized yet. */ - val propertiesInitialized: StateFlow<Boolean> + val propertiesInitialized: Flow<Boolean> /** The id of fingerprint sensor. */ val sensorId: Flow<Int> @@ -110,14 +111,8 @@ constructor( initialValue = UNINITIALIZED_PROPS, ) - override val propertiesInitialized: StateFlow<Boolean> = - props - .map { it != UNINITIALIZED_PROPS } - .stateIn( - applicationScope, - started = SharingStarted.WhileSubscribed(), - initialValue = props.value != UNINITIALIZED_PROPS, - ) + override val propertiesInitialized: Flow<Boolean> = + props.map { it != UNINITIALIZED_PROPS }.onStart { emit(props.value != UNINITIALIZED_PROPS) } override val sensorId: Flow<Int> = props.map { it.sensorId } @@ -141,7 +136,7 @@ constructor( companion object { private const val TAG = "FingerprintPropertyRepositoryImpl" - private val UNINITIALIZED_PROPS = + val UNINITIALIZED_PROPS = FingerprintSensorPropertiesInternal( -2 /* sensorId */, SensorProperties.STRENGTH_CONVENIENCE, diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractor.kt index 6e79e4693728..83aefcaac36d 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractor.kt @@ -17,6 +17,7 @@ package com.android.systemui.biometrics.domain.interactor import android.app.ActivityTaskManager +import android.util.Log import com.android.systemui.biometrics.data.repository.BiometricStatusRepository import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository import com.android.systemui.biometrics.shared.model.AuthenticationReason @@ -26,6 +27,7 @@ import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.onEach /** Encapsulates business logic for interacting with biometric authentication state. */ interface BiometricStatusInteractor { @@ -49,15 +51,20 @@ constructor( override val sfpsAuthenticationReason: Flow<AuthenticationReason> = combine( - biometricStatusRepository.fingerprintAuthenticationReason, - fingerprintPropertyRepository.sensorType - ) { reason: AuthenticationReason, sensorType -> - if (sensorType.isPowerButton() && reason.isReasonToAlwaysUpdateSfpsOverlay(activityTaskManager)) { - reason - } else { - AuthenticationReason.NotRunning + biometricStatusRepository.fingerprintAuthenticationReason, + fingerprintPropertyRepository.sensorType + ) { reason: AuthenticationReason, sensorType -> + if ( + sensorType.isPowerButton() && + reason.isReasonToAlwaysUpdateSfpsOverlay(activityTaskManager) + ) { + reason + } else { + AuthenticationReason.NotRunning + } } - }.distinctUntilChanged() + .distinctUntilChanged() + .onEach { Log.d(TAG, "sfpsAuthenticationReason updated: $it") } override val fingerprintAcquiredStatus: Flow<FingerprintAuthenticationStatus> = biometricStatusRepository.fingerprintAcquiredStatus diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/FingerprintPropertyInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/FingerprintPropertyInteractor.kt index 3112b673d724..d5b450d1e2a8 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/FingerprintPropertyInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/FingerprintPropertyInteractor.kt @@ -46,7 +46,7 @@ constructor( displayStateInteractor: DisplayStateInteractor, udfpsOverlayInteractor: UdfpsOverlayInteractor, ) { - val propertiesInitialized: StateFlow<Boolean> = repository.propertiesInitialized + val propertiesInitialized: Flow<Boolean> = repository.propertiesInitialized val isUdfps: StateFlow<Boolean> = repository.sensorType .map { it.isUdfps() } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt index f0969eda4029..13ea3f56d911 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt @@ -199,29 +199,32 @@ object BiometricViewSizeBinder { iconParams.leftMargin = position.left mediumConstraintSet.clear( R.id.biometric_icon, - ConstraintSet.END + ConstraintSet.RIGHT ) mediumConstraintSet.connect( R.id.biometric_icon, - ConstraintSet.START, + ConstraintSet.LEFT, ConstraintSet.PARENT_ID, - ConstraintSet.START + ConstraintSet.LEFT ) mediumConstraintSet.setMargin( R.id.biometric_icon, - ConstraintSet.START, + ConstraintSet.LEFT, position.left ) - smallConstraintSet.clear(R.id.biometric_icon, ConstraintSet.END) + smallConstraintSet.clear( + R.id.biometric_icon, + ConstraintSet.RIGHT + ) smallConstraintSet.connect( R.id.biometric_icon, - ConstraintSet.START, + ConstraintSet.LEFT, ConstraintSet.PARENT_ID, - ConstraintSet.START + ConstraintSet.LEFT ) smallConstraintSet.setMargin( R.id.biometric_icon, - ConstraintSet.START, + ConstraintSet.LEFT, position.left ) } @@ -252,32 +255,32 @@ object BiometricViewSizeBinder { iconParams.rightMargin = position.right mediumConstraintSet.clear( R.id.biometric_icon, - ConstraintSet.START + ConstraintSet.LEFT ) mediumConstraintSet.connect( R.id.biometric_icon, - ConstraintSet.END, + ConstraintSet.RIGHT, ConstraintSet.PARENT_ID, - ConstraintSet.END + ConstraintSet.RIGHT ) mediumConstraintSet.setMargin( R.id.biometric_icon, - ConstraintSet.END, + ConstraintSet.RIGHT, position.right ) smallConstraintSet.clear( R.id.biometric_icon, - ConstraintSet.START + ConstraintSet.LEFT ) smallConstraintSet.connect( R.id.biometric_icon, - ConstraintSet.END, + ConstraintSet.RIGHT, ConstraintSet.PARENT_ID, - ConstraintSet.END + ConstraintSet.RIGHT ) smallConstraintSet.setMargin( R.id.biometric_icon, - ConstraintSet.END, + ConstraintSet.RIGHT, position.right ) } @@ -383,15 +386,15 @@ object BiometricViewSizeBinder { // Move all content to other panel flipConstraintSet.connect( R.id.scrollView, - ConstraintSet.START, + ConstraintSet.LEFT, R.id.midGuideline, - ConstraintSet.START + ConstraintSet.LEFT ) flipConstraintSet.connect( R.id.scrollView, - ConstraintSet.END, + ConstraintSet.RIGHT, R.id.rightGuideline, - ConstraintSet.END + ConstraintSet.RIGHT ) } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt index 4bdbfa272ecc..ff7ac35ba56b 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt @@ -20,6 +20,7 @@ package com.android.systemui.biometrics.ui.binder import android.content.Context import android.graphics.PorterDuff import android.graphics.PorterDuffColorFilter +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.WindowManager @@ -91,6 +92,13 @@ constructor( showIndicatorForDeviceEntry, progressBarIsVisible) = combinedFlows + Log.d( + TAG, + "systemServerAuthReason = $systemServerAuthReason, " + + "showIndicatorForDeviceEntry = " + + "$showIndicatorForDeviceEntry, " + + "progressBarIsVisible = $progressBarIsVisible" + ) if (!isInRearDisplayMode) { if (progressBarIsVisible) { hide() @@ -114,6 +122,10 @@ constructor( /** Show the side fingerprint sensor indicator */ private fun show() { if (overlayView?.isAttachedToWindow == true) { + Log.d( + TAG, + "show(): overlayView $overlayView isAttachedToWindow already, ignoring show request" + ) return } @@ -128,6 +140,7 @@ constructor( ) bind(overlayView!!, overlayViewModel, fpsUnlockTracker.get(), windowManager.get()) overlayView!!.visibility = View.INVISIBLE + Log.d(TAG, "show(): adding overlayView $overlayView") windowManager.get().addView(overlayView, overlayViewModel.defaultOverlayViewParams) } @@ -137,6 +150,7 @@ constructor( val lottie = overlayView!!.requireViewById<LottieAnimationView>(R.id.sidefps_animation) lottie.pauseAnimation() lottie.removeAllLottieOnCompositionLoadedListener() + Log.d(TAG, "hide(): removing overlayView $overlayView, setting to null") windowManager.get().removeView(overlayView) overlayView = null } diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegate.kt index f0230beaa967..911145b62661 100644 --- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegate.kt +++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegate.kt @@ -419,8 +419,7 @@ internal constructor( const val ACTION_PREVIOUSLY_CONNECTED_DEVICE = "com.android.settings.PREVIOUSLY_CONNECTED_DEVICE" const val ACTION_PAIR_NEW_DEVICE = "android.settings.BLUETOOTH_PAIRING_SETTINGS" - const val ACTION_AUDIO_SHARING = - "com.google.android.settings.BLUETOOTH_AUDIO_SHARING_SETTINGS" + const val ACTION_AUDIO_SHARING = "com.android.settings.BLUETOOTH_AUDIO_SHARING_SETTINGS" const val DISABLED_ALPHA = 0.3f const val ENABLED_ALPHA = 1f const val PROGRESS_BAR_ANIMATION_DURATION_MS = 1500L diff --git a/packages/SystemUI/src/com/android/systemui/brightness/data/repository/BrightnessPolicyRepository.kt b/packages/SystemUI/src/com/android/systemui/brightness/data/repository/BrightnessPolicyRepository.kt index c018ecb25835..0544a4f66295 100644 --- a/packages/SystemUI/src/com/android/systemui/brightness/data/repository/BrightnessPolicyRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/brightness/data/repository/BrightnessPolicyRepository.kt @@ -18,6 +18,8 @@ package com.android.systemui.brightness.data.repository import android.content.Context import android.os.UserManager +import com.android.settingslib.RestrictedLockUtils +import com.android.systemui.Flags.enforceBrightnessBaseUserRestriction import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background @@ -66,7 +68,18 @@ constructor( user.id ) ?.let { PolicyRestriction.Restricted(it) } - ?: PolicyRestriction.NoRestriction + ?: if ( + enforceBrightnessBaseUserRestriction() && + userRestrictionChecker.hasBaseUserRestriction( + applicationContext, + UserManager.DISALLOW_CONFIG_BRIGHTNESS, + user.id + ) + ) { + PolicyRestriction.Restricted(RestrictedLockUtils.EnforcedAdmin()) + } else { + PolicyRestriction.NoRestriction + } } .flowOn(backgroundDispatcher) } diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt index 06c83962df6b..9599a8864bcc 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt @@ -61,7 +61,6 @@ import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.settings.UserTracker import com.android.systemui.smartspace.data.repository.SmartspaceRepository import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf -import com.android.systemui.util.kotlin.BooleanFlowOperators.anyOf import com.android.systemui.util.kotlin.BooleanFlowOperators.not import com.android.systemui.util.kotlin.emitOnStart import javax.inject.Inject @@ -130,7 +129,7 @@ constructor( allOf( communalSettingsInteractor.isCommunalEnabled, not(keyguardInteractor.isEncryptedOrLockdown), - anyOf(keyguardInteractor.isKeyguardShowing, keyguardInteractor.isDreaming) + keyguardInteractor.isKeyguardShowing ) .distinctUntilChanged() .onEach { available -> diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt index f6122ad48300..c0dc313e14f7 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt @@ -96,6 +96,8 @@ constructor( uiEventLogger.log(CommunalUiEvent.COMMUNAL_HUB_REORDER_WIDGET_CANCEL) } + val isIdleOnCommunal: StateFlow<Boolean> = communalInteractor.isIdleOnCommunal + /** Launch the widget picker activity using the given {@link ActivityResultLauncher}. */ suspend fun onOpenWidgetPicker( resources: Resources, diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt index 656e5cbafa97..97db43bdf0f8 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt @@ -25,6 +25,7 @@ import com.android.systemui.communal.domain.model.CommunalContentModel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.log.LogBuffer @@ -63,6 +64,7 @@ constructor( @Application private val scope: CoroutineScope, @Main private val resources: Resources, keyguardTransitionInteractor: KeyguardTransitionInteractor, + keyguardInteractor: KeyguardInteractor, private val communalInteractor: CommunalInteractor, tutorialInteractor: CommunalTutorialInteractor, private val shadeInteractor: ShadeInteractor, @@ -236,6 +238,14 @@ constructor( */ val touchesAllowed: Flow<Boolean> = not(shadeInteractor.isAnyFullyExpanded) + // TODO(b/339667383): remove this temporary swipe gesture handle + /** + * The dream overlay has its own gesture handle as the SysUI window is not visible above the + * dream. This flow will be false when dreaming so that we don't show a duplicate handle when + * opening the hub over the dream. + */ + val showGestureIndicator: Flow<Boolean> = not(keyguardInteractor.isDreaming) + companion object { const val POPUP_AUTO_HIDE_TIMEOUT_MS = 12000L } diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt index f20fafccfd19..426f484e4d02 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt @@ -44,6 +44,7 @@ import com.android.systemui.log.LogBuffer import com.android.systemui.log.core.Logger import com.android.systemui.log.dagger.CommunalLog import javax.inject.Inject +import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch /** An Activity for editing the widgets that appear in hub mode. */ @@ -69,6 +70,8 @@ constructor( private var shouldOpenWidgetPickerOnStart = false + private var lockOnDestroy = false + private val addWidgetActivityLauncher: ActivityResultLauncher<Intent> = registerForActivityResult(StartActivityForResult()) { result -> when (result.resultCode) { @@ -149,15 +152,18 @@ constructor( } private fun onEditDone() { - try { + lifecycleScope.launch { communalViewModel.changeScene( CommunalScenes.Communal, CommunalTransitionKeys.SimpleFade ) - checkNotNull(windowManagerService).lockNow(/* options */ null) + + // Wait for the current scene to be idle on communal. + communalViewModel.isIdleOnCommunal.first { it } + // Then finish the activity (this helps to avoid a flash of lockscreen when locking + // in onDestroy()). + lockOnDestroy = true finish() - } catch (e: RemoteException) { - Log.e(TAG, "Couldn't lock the device as WindowManager is dead.") } } @@ -190,5 +196,15 @@ constructor( override fun onDestroy() { super.onDestroy() communalViewModel.setEditModeOpen(false) + + if (lockOnDestroy) lockNow() + } + + private fun lockNow() { + try { + checkNotNull(windowManagerService).lockNow(/* options */ null) + } catch (e: RemoteException) { + Log.e(TAG, "Couldn't lock the device as WindowManager is dead.") + } } } diff --git a/packages/SystemUI/src/com/android/systemui/contrast/ContrastDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/contrast/ContrastDialogDelegate.kt deleted file mode 100644 index 0daa058720ba..000000000000 --- a/packages/SystemUI/src/com/android/systemui/contrast/ContrastDialogDelegate.kt +++ /dev/null @@ -1,111 +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.contrast - -import android.app.UiModeManager -import android.app.UiModeManager.ContrastUtils.CONTRAST_LEVEL_HIGH -import android.app.UiModeManager.ContrastUtils.CONTRAST_LEVEL_MEDIUM -import android.app.UiModeManager.ContrastUtils.CONTRAST_LEVEL_STANDARD -import android.app.UiModeManager.ContrastUtils.fromContrastLevel -import android.app.UiModeManager.ContrastUtils.toContrastLevel -import android.os.Bundle -import android.provider.Settings -import android.view.View -import android.widget.FrameLayout -import com.android.internal.annotations.VisibleForTesting -import com.android.systemui.dagger.qualifiers.Main -import com.android.systemui.res.R -import com.android.systemui.settings.UserTracker -import com.android.systemui.statusbar.phone.SystemUIDialog -import com.android.systemui.util.settings.SecureSettings -import java.util.concurrent.Executor -import javax.inject.Inject - -/** Dialog to select contrast options */ -class ContrastDialogDelegate -@Inject -constructor( - private val sysuiDialogFactory: SystemUIDialog.Factory, - @Main private val mainExecutor: Executor, - private val uiModeManager: UiModeManager, - private val userTracker: UserTracker, - private val secureSettings: SecureSettings, -) : SystemUIDialog.Delegate, UiModeManager.ContrastChangeListener { - - @VisibleForTesting lateinit var contrastButtons: Map<Int, FrameLayout> - lateinit var dialogView: View - @VisibleForTesting var initialContrast: Float = fromContrastLevel(CONTRAST_LEVEL_STANDARD) - - override fun createDialog(): SystemUIDialog { - val dialog = sysuiDialogFactory.create(this) - dialogView = dialog.layoutInflater.inflate(R.layout.contrast_dialog, null) - with(dialog) { - setView(dialogView) - - setTitle(R.string.quick_settings_contrast_label) - setNeutralButton(R.string.cancel) { _, _ -> - secureSettings.putFloatForUser( - Settings.Secure.CONTRAST_LEVEL, - initialContrast, - userTracker.userId - ) - dialog.dismiss() - } - setPositiveButton(com.android.settingslib.R.string.done) { _, _ -> dialog.dismiss() } - } - - return dialog - } - - override fun onCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) { - contrastButtons = - mapOf( - CONTRAST_LEVEL_STANDARD to dialog.requireViewById(R.id.contrast_button_standard), - CONTRAST_LEVEL_MEDIUM to dialog.requireViewById(R.id.contrast_button_medium), - CONTRAST_LEVEL_HIGH to dialog.requireViewById(R.id.contrast_button_high) - ) - - contrastButtons.forEach { (contrastLevel, contrastButton) -> - contrastButton.setOnClickListener { - val contrastValue = fromContrastLevel(contrastLevel) - secureSettings.putFloatForUser( - Settings.Secure.CONTRAST_LEVEL, - contrastValue, - userTracker.userId - ) - } - } - - initialContrast = uiModeManager.contrast - highlightContrast(toContrastLevel(initialContrast)) - } - - override fun onStart(dialog: SystemUIDialog) { - uiModeManager.addContrastChangeListener(mainExecutor, this) - } - - override fun onStop(dialog: SystemUIDialog) { - uiModeManager.removeContrastChangeListener(this) - } - - override fun onContrastChanged(contrast: Float) { - highlightContrast(toContrastLevel(contrast)) - } - - private fun highlightContrast(contrast: Int) { - contrastButtons.forEach { (level, button) -> button.isSelected = level == contrast } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java index d2df276002cc..c2e1e33f5318 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java @@ -20,7 +20,6 @@ import android.app.Activity; import com.android.systemui.ForegroundServicesDialog; import com.android.systemui.communal.widgets.EditWidgetsActivity; -import com.android.systemui.contrast.ContrastDialogActivity; import com.android.systemui.keyguard.WorkLockActivity; import com.android.systemui.people.PeopleSpaceActivity; import com.android.systemui.people.widget.LaunchConversationActivity; @@ -72,12 +71,6 @@ public abstract class DefaultActivityBinder { @ClassKey(BrightnessDialog.class) public abstract Activity bindBrightnessDialog(BrightnessDialog activity); - /** Inject into ContrastDialogActivity. */ - @Binds - @IntoMap - @ClassKey(ContrastDialogActivity.class) - public abstract Activity bindContrastDialogActivity(ContrastDialogActivity activity); - /** Inject into UsbDebuggingActivity. */ @Binds @IntoMap diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java index 60006c68639d..1e725eb71dde 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java @@ -21,6 +21,8 @@ import static android.service.dreams.Flags.dreamHandlesBeingObscured; import static com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerProgress; import static com.android.keyguard.BouncerPanelExpansionCalculator.getDreamAlphaScaledExpansion; import static com.android.keyguard.BouncerPanelExpansionCalculator.getDreamYPositionScaledExpansion; +import static com.android.systemui.Flags.communalHub; +import static com.android.systemui.Flags.glanceableHubGestureHandle; import static com.android.systemui.complication.ComplicationLayoutParams.POSITION_BOTTOM; import static com.android.systemui.complication.ComplicationLayoutParams.POSITION_TOP; import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset; @@ -185,6 +187,7 @@ public class DreamOverlayContainerViewController extends DreamOverlayContainerView containerView, ComplicationHostViewController complicationHostViewController, @Named(DreamOverlayModule.DREAM_OVERLAY_CONTENT_VIEW) ViewGroup contentView, + @Named(DreamOverlayModule.HUB_GESTURE_INDICATOR_VIEW) View hubGestureIndicatorView, DreamOverlayStatusBarViewController statusBarViewController, LowLightTransitionCoordinator lowLightTransitionCoordinator, BlurUtils blurUtils, @@ -220,6 +223,12 @@ public class DreamOverlayContainerViewController extends mComplicationHostViewController = complicationHostViewController; mDreamOverlayMaxTranslationY = resources.getDimensionPixelSize( R.dimen.dream_overlay_y_offset); + + if (communalHub() && glanceableHubGestureHandle()) { + // TODO(b/339667383): remove this temporary swipe gesture handle + hubGestureIndicatorView.setVisibility(View.VISIBLE); + } + final View view = mComplicationHostViewController.getView(); mDreamOverlayContentView.addView(view, diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java index 999e6813ea55..789b7f8550d7 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java @@ -18,6 +18,7 @@ package com.android.systemui.dreams.dagger; import android.content.res.Resources; import android.view.LayoutInflater; +import android.view.View; import android.view.ViewGroup; import androidx.lifecycle.Lifecycle; @@ -39,6 +40,7 @@ import javax.inject.Named; @Module public abstract class DreamOverlayModule { public static final String DREAM_OVERLAY_CONTENT_VIEW = "dream_overlay_content_view"; + public static final String HUB_GESTURE_INDICATOR_VIEW = "hub_gesture_indicator_view"; public static final String MAX_BURN_IN_OFFSET = "max_burn_in_offset"; public static final String BURN_IN_PROTECTION_UPDATE_INTERVAL = "burn_in_protection_update_interval"; @@ -71,6 +73,18 @@ public abstract class DreamOverlayModule { "R.id.dream_overlay_content must not be null"); } + /** + * Gesture indicator bar on the right edge of the screen to indicate to users that they can + * swipe to see their widgets on lock screen. + */ + @Provides + @DreamOverlayComponent.DreamOverlayScope + @Named(HUB_GESTURE_INDICATOR_VIEW) + public static View providesHubGestureIndicatorView(DreamOverlayContainerView view) { + return Preconditions.checkNotNull(view.findViewById(R.id.glanceable_hub_handle), + "R.id.glanceable_hub_handle must not be null"); + } + /** */ @Provides public static TouchInsetManager.TouchInsetSession providesTouchInsetSession( diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt index 7655d7a89dc3..f488d3b67fc7 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt @@ -34,6 +34,7 @@ import java.util.UUID import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.channels.BufferOverflow +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow @@ -42,6 +43,7 @@ import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.sync.Mutex /** * The source of truth for all keyguard transitions. @@ -129,6 +131,7 @@ constructor( private var lastStep: TransitionStep = TransitionStep() private var lastAnimator: ValueAnimator? = null + private val _currentTransitionMutex = Mutex() private val _currentTransitionInfo: MutableStateFlow<TransitionInfo> = MutableStateFlow( TransitionInfo( @@ -146,6 +149,9 @@ constructor( */ private var updateTransitionId: UUID? = null + // Only used in a test environment + var forceDelayForRaceConditionTest = false + init { // Start with a FINISHED transition in OFF. KeyguardBootInteractor will transition from OFF // to either GONE or LOCKSCREEN once we're booted up and can determine which state we should @@ -162,9 +168,21 @@ constructor( override suspend fun startTransition(info: TransitionInfo): UUID? { _currentTransitionInfo.value = info + Log.d(TAG, "(Internal) Setting current transition info: $info") + + // There is no fairness guarantee with 'withContext', which means that transitions could + // be processed out of order. Use a Mutex to guarantee ordering. + _currentTransitionMutex.lock() + + // Only used in a test environment + if (forceDelayForRaceConditionTest) { + delay(50L) + } // Animators must be started on the main thread. return withContext("$TAG#startTransition", mainDispatcher) { + _currentTransitionMutex.unlock() + if (lastStep.from == info.from && lastStep.to == info.to) { Log.i(TAG, "Duplicate call to start the transition, rejecting: $info") return@withContext null diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt index eef4b97ae34d..96260770d89f 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt @@ -17,6 +17,7 @@ package com.android.systemui.keyguard.domain.interactor import android.content.Context +import android.util.Log import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor @@ -39,6 +40,7 @@ import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.merge +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch /** @@ -96,10 +98,13 @@ constructor( keyguardUpdateMonitor.isFingerprintDetectionRunning && keyguardUpdateMonitor.isUnlockingWithFingerprintAllowed } + .onEach { Log.d(TAG, "showIndicatorForPrimaryBouncer updated: $it") } private val showIndicatorForAlternateBouncer: Flow<Boolean> = // Note: this interactor internally verifies that SideFPS is enabled and running. - alternateBouncerInteractor.isVisible + alternateBouncerInteractor.isVisible.onEach { + Log.d(TAG, "showIndicatorForAlternateBouncer updated: $it") + } /** * Indicates whether the primary or alternate bouncers request showing the side fingerprint @@ -112,6 +117,7 @@ constructor( showForPrimaryBouncer || showForAlternateBouncer } .distinctUntilChanged() + .onEach { Log.d(TAG, "showIndicatorForDeviceEntry updated: $it") } private fun isBouncerActive(): Boolean { if (SceneContainerFlag.isEnabled) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt index 857096e1c03b..b1ef76eba481 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt @@ -20,6 +20,7 @@ package com.android.systemui.keyguard.domain.interactor import android.content.Context +import android.util.Log import com.android.systemui.biometrics.domain.interactor.FingerprintPropertyInteractor import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor import com.android.systemui.dagger.SysUISingleton @@ -42,6 +43,7 @@ import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.merge +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch @SysUISingleton @@ -78,7 +80,15 @@ constructor( private val refreshEvents: Flow<Unit> = merge( configurationInteractor.onAnyConfigurationChange, - fingerprintPropertyInteractor.propertiesInitialized.filter { it }.map { Unit }, + fingerprintPropertyInteractor.propertiesInitialized + .filter { it } + .map { Unit } + .onEach { + Log.d( + "KeyguardBlueprintInteractor", + "triggering refreshEvent from fpPropertiesInitialized" + ) + }, ) init { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt index 75c4d6f6fea6..cf6942e2f245 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt @@ -56,12 +56,6 @@ constructor( } scope.launch { - sharedNotificationContainerViewModel - .getMaxNotifications { height, useExtraShelfSpace -> height.toInt() } - .collect { logger.log(TAG, VERBOSE, "Notif: max height in px", it) } - } - - scope.launch { sharedNotificationContainerViewModel.isOnLockscreen.collect { logger.log(TAG, VERBOSE, "Notif: isOnLockscreen", it) } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt index b2a24ca85b07..323ceef06a97 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt @@ -229,6 +229,7 @@ sealed class TransitionInteractor( startTransitionTo( toState = KeyguardState.OCCLUDED, modeOnCanceled = TransitionModeOnCanceled.RESET, + ownerReason = "keyguardInteractor.onCameraLaunchDetected", ) } } diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt index 229e592a6a0c..19e3e0715989 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt @@ -308,7 +308,11 @@ constructor( * It will be called when the container is out of view. */ lateinit var updateUserVisibility: () -> Unit - lateinit var updateHostVisibility: () -> Unit + var updateHostVisibility: () -> Unit = {} + set(value) { + field = value + mediaCarouselViewModel.updateHostVisibility = value + } private val isReorderingAllowed: Boolean get() = visualStabilityProvider.isReorderingAllowed @@ -345,6 +349,20 @@ constructor( configurationController.addCallback(configListener) if (!mediaFlags.isMediaControlsRefactorEnabled()) { setUpListeners() + } else { + val visualStabilityCallback = OnReorderingAllowedListener { + mediaCarouselViewModel.onReorderingAllowed() + + // Update user visibility so that no extra impression will be logged when + // activeMediaIndex resets to 0 + if (this::updateUserVisibility.isInitialized) { + updateUserVisibility() + } + + // Let's reset our scroll position + mediaCarouselScrollHandler.scrollToStart() + } + visualStabilityProvider.addPersistentReorderingAllowedListener(visualStabilityCallback) } mediaFrame.addOnLayoutChangeListener { _, _, _, _, _, _, _, _, _ -> // The pageIndicator is not laid out yet when we get the current state update, @@ -366,10 +384,6 @@ constructor( ) keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback) mediaCarousel.repeatWhenAttached { - if (mediaFlags.isMediaControlsRefactorEnabled()) { - mediaCarouselViewModel.onAttached() - mediaCarouselScrollHandler.scrollToStart() - } repeatOnLifecycle(Lifecycle.State.STARTED) { listenForAnyStateToGoneKeyguardTransition(this) listenForAnyStateToLockscreenTransition(this) @@ -592,9 +606,7 @@ constructor( if (!immediately) { // Although it wasn't requested, we were able to process the removal // immediately since reordering is allowed. So, notify hosts to update - if (this@MediaCarouselController::updateHostVisibility.isInitialized) { - updateHostVisibility() - } + updateHostVisibility() } } else { keysNeedRemoval.add(key) @@ -751,6 +763,7 @@ constructor( } } viewController.setListening(mediaCarouselScrollHandler.visibleToUser && currentlyExpanded) + controllerByViewModel[commonViewModel] = viewController updateViewControllerToState(viewController, noAnimation = true) updatePageIndicator() if ( @@ -764,7 +777,6 @@ constructor( mediaCarouselScrollHandler.onPlayersChanged() mediaFrame.requiresRemeasuring = true commonViewModel.onAdded(commonViewModel) - controllerByViewModel[commonViewModel] = viewController } private fun onUpdated(commonViewModel: MediaCommonViewModel) { diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt index fd5f44594ae8..4e9093642c6b 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt @@ -57,12 +57,12 @@ constructor( val mediaItems: StateFlow<List<MediaCommonViewModel>> = interactor.currentMedia .map { sortedItems -> - buildList { + val mediaList = buildList { sortedItems.forEach { commonModel -> // When view is started we should make sure to clean models that are pending // removal. // This action should only be triggered once. - if (!isAttached || !modelsPendingRemoval.contains(commonModel)) { + if (!allowReorder || !modelsPendingRemoval.contains(commonModel)) { when (commonModel) { is MediaCommonModel.MediaControl -> add(toViewModel(commonModel)) is MediaCommonModel.MediaRecommendations -> @@ -70,11 +70,16 @@ constructor( } } } - if (isAttached) { - modelsPendingRemoval.clear() + } + if (allowReorder) { + if (modelsPendingRemoval.size > 0) { + updateHostVisibility() } - isAttached = false + modelsPendingRemoval.clear() } + allowReorder = false + + mediaList } .stateIn( scope = applicationScope, @@ -82,6 +87,8 @@ constructor( initialValue = emptyList(), ) + var updateHostVisibility: () -> Unit = {} + private val mediaControlByInstanceId = mutableMapOf<InstanceId, MediaCommonViewModel.MediaControl>() @@ -89,15 +96,15 @@ constructor( private var modelsPendingRemoval: MutableSet<MediaCommonModel> = mutableSetOf() - private var isAttached = false + private var allowReorder = false fun onSwipeToDismiss() { logger.logSwipeDismiss() interactor.onSwipeToDismiss() } - fun onAttached() { - isAttached = true + fun onReorderingAllowed() { + allowReorder = true interactor.reorderMedia() } @@ -194,7 +201,11 @@ constructor( ) { if (immediatelyRemove || isReorderingAllowed()) { interactor.dismissSmartspaceRecommendation(commonModel.recsLoadingModel.key, 0L) - // TODO if not immediate remove update host visibility + if (!immediatelyRemove) { + // Although it wasn't requested, we were able to process the removal + // immediately since reordering is allowed. So, notify hosts to update + updateHostVisibility() + } } else { modelsPendingRemoval.add(commonModel) } diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt index 412c006806bf..9265bfb2f66b 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt @@ -140,10 +140,11 @@ constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 val bitmapShader = bitmapShader ?: return val thumbnailData = thumbnailData ?: return + val thumbnail = thumbnailData.thumbnail ?: return val display = context.display ?: return val windowMetrics = windowManager.maximumWindowMetrics - previewRect.set(0, 0, thumbnailData.thumbnail.width, thumbnailData.thumbnail.height) + previewRect.set(0, 0, thumbnail.width, thumbnail.height) val currentRotation: Int = display.rotation val isRtl = layoutDirection == LAYOUT_DIRECTION_RTL diff --git a/packages/SystemUI/src/com/android/systemui/qrcodescanner/dagger/QRCodeScannerModule.kt b/packages/SystemUI/src/com/android/systemui/qrcodescanner/dagger/QRCodeScannerModule.kt index 3907a7240258..5e6ee4d3c700 100644 --- a/packages/SystemUI/src/com/android/systemui/qrcodescanner/dagger/QRCodeScannerModule.kt +++ b/packages/SystemUI/src/com/android/systemui/qrcodescanner/dagger/QRCodeScannerModule.kt @@ -16,12 +16,20 @@ package com.android.systemui.qrcodescanner.dagger +import com.android.systemui.Flags import com.android.systemui.qs.QsEventLogger import com.android.systemui.qs.pipeline.shared.TileSpec import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.qs.tiles.QRCodeScannerTile +import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelFactory +import com.android.systemui.qs.tiles.impl.qr.domain.interactor.QRCodeScannerTileDataInteractor +import com.android.systemui.qs.tiles.impl.qr.domain.interactor.QRCodeScannerTileUserActionInteractor +import com.android.systemui.qs.tiles.impl.qr.domain.model.QRCodeScannerTileModel +import com.android.systemui.qs.tiles.impl.qr.ui.QRCodeScannerTileMapper import com.android.systemui.qs.tiles.viewmodel.QSTileConfig import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig +import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel +import com.android.systemui.qs.tiles.viewmodel.StubQSTileViewModel import com.android.systemui.res.R import dagger.Binds import dagger.Module @@ -54,5 +62,24 @@ interface QRCodeScannerModule { ), instanceId = uiEventLogger.getNewInstanceId(), ) + + /** Inject QR Code Scanner Tile into tileViewModelMap in QSModule. */ + @Provides + @IntoMap + @StringKey(QR_CODE_SCANNER_TILE_SPEC) + fun provideQRCodeScannerTileViewModel( + factory: QSTileViewModelFactory.Static<QRCodeScannerTileModel>, + mapper: QRCodeScannerTileMapper, + stateInteractor: QRCodeScannerTileDataInteractor, + userActionInteractor: QRCodeScannerTileUserActionInteractor + ): QSTileViewModel = + if (Flags.qsNewTilesFuture()) + factory.create( + TileSpec.create(QR_CODE_SCANNER_TILE_SPEC), + userActionInteractor, + stateInteractor, + mapper, + ) + else StubQSTileViewModel } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileStateToProto.kt b/packages/SystemUI/src/com/android/systemui/qs/TileStateToProto.kt index 2c8a5a4981d0..1336d640a9e5 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/TileStateToProto.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/TileStateToProto.kt @@ -18,6 +18,7 @@ package com.android.systemui.qs import android.service.quicksettings.Tile import android.text.TextUtils +import android.widget.Switch import com.android.systemui.plugins.qs.QSTile import com.android.systemui.qs.external.CustomTile import com.android.systemui.qs.nano.QsTileState @@ -44,8 +45,8 @@ fun QSTile.State.toProto(): QsTileState? { } label?.let { state.label = it.toString() } secondaryLabel?.let { state.secondaryLabel = it.toString() } - if (this is QSTile.BooleanState) { - state.booleanState = value + if (expandedAccessibilityClassName == Switch::class.java.name) { + state.booleanState = state.state == QsTileState.ACTIVE } return state } diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java index d26ae0a4dac8..5d35a69be910 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java @@ -42,6 +42,7 @@ import android.text.format.DateUtils; import android.util.Log; import android.view.IWindowManager; import android.view.WindowManagerGlobal; +import android.widget.Button; import android.widget.Switch; import androidx.annotation.Nullable; @@ -502,6 +503,8 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener, if (state instanceof BooleanState) { state.expandedAccessibilityClassName = Switch.class.getName(); ((BooleanState) state).value = (state.state == Tile.STATE_ACTIVE); + } else { + state.expandedAccessibilityClassName = Button.class.getName(); } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java index 206879905782..71b69c92b87d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java @@ -16,6 +16,8 @@ package com.android.systemui.qs.tiles; +import static com.android.settingslib.satellite.SatelliteDialogUtils.TYPE_IS_AIRPLANE_MODE; + import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -32,9 +34,12 @@ import android.telephony.TelephonyManager; import android.widget.Switch; import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.internal.telephony.flags.Flags; +import com.android.settingslib.satellite.SatelliteDialogUtils; import com.android.systemui.animation.Expandable; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.qualifiers.Background; @@ -54,6 +59,8 @@ import com.android.systemui.util.settings.GlobalSettings; import dagger.Lazy; +import kotlinx.coroutines.Job; + import javax.inject.Inject; /** Quick settings tile: Airplane mode **/ @@ -66,6 +73,9 @@ public class AirplaneModeTile extends QSTileImpl<BooleanState> { private final Lazy<ConnectivityManager> mLazyConnectivityManager; private boolean mListening; + @Nullable + @VisibleForTesting + Job mClickJob; @Inject public AirplaneModeTile( @@ -111,6 +121,21 @@ public class AirplaneModeTile extends QSTileImpl<BooleanState> { new Intent(TelephonyManager.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS), 0); return; } + + if (Flags.oemEnabledSatelliteFlag()) { + if (mClickJob != null && !mClickJob.isCompleted()) { + return; + } + mClickJob = SatelliteDialogUtils.mayStartSatelliteWarningDialog( + mContext, this, TYPE_IS_AIRPLANE_MODE, isAllowClick -> { + if (isAllowClick) { + setEnabled(!airplaneModeEnabled); + } + return null; + }); + return; + } + setEnabled(!airplaneModeEnabled); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java index 9af34f6c9918..9f41d98b6969 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java @@ -16,6 +16,7 @@ package com.android.systemui.qs.tiles; +import static com.android.settingslib.satellite.SatelliteDialogUtils.TYPE_IS_BLUETOOTH; import static com.android.systemui.util.PluralMessageFormaterKt.icuMessageFormat; import android.annotation.Nullable; @@ -33,11 +34,14 @@ import android.text.TextUtils; import android.util.Log; import android.widget.Switch; +import androidx.annotation.VisibleForTesting; + import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settingslib.Utils; import com.android.settingslib.bluetooth.BluetoothUtils; import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.satellite.SatelliteDialogUtils; import com.android.systemui.animation.Expandable; import com.android.systemui.bluetooth.qsdialog.BluetoothTileDialogViewModel; import com.android.systemui.dagger.qualifiers.Background; @@ -55,6 +59,8 @@ import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.res.R; import com.android.systemui.statusbar.policy.BluetoothController; +import kotlinx.coroutines.Job; + import java.util.List; import java.util.concurrent.Executor; @@ -78,6 +84,9 @@ public class BluetoothTile extends QSTileImpl<BooleanState> { private final BluetoothTileDialogViewModel mDialogViewModel; private final FeatureFlags mFeatureFlags; + @Nullable + @VisibleForTesting + Job mClickJob; @Inject public BluetoothTile( @@ -110,6 +119,24 @@ public class BluetoothTile extends QSTileImpl<BooleanState> { @Override protected void handleClick(@Nullable Expandable expandable) { + if (com.android.internal.telephony.flags.Flags.oemEnabledSatelliteFlag()) { + if (mClickJob != null && !mClickJob.isCompleted()) { + return; + } + mClickJob = SatelliteDialogUtils.mayStartSatelliteWarningDialog( + mContext, this, TYPE_IS_BLUETOOTH, isAllowClick -> { + if (!isAllowClick) { + return null; + } + handleClickEvent(expandable); + return null; + }); + return; + } + handleClickEvent(expandable); + } + + private void handleClickEvent(@Nullable Expandable expandable) { if (mFeatureFlags.isEnabled(Flags.BLUETOOTH_QS_TILE_DIALOG)) { mDialogViewModel.showDialog(expandable); } else { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandler.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandler.kt index 2d3120a1dcce..972b20e138d9 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandler.kt @@ -29,11 +29,15 @@ import javax.inject.Inject /** * Provides a shortcut to start an activity from [QSTileUserActionInteractor]. It supports keyguard - * dismissing and tile from-view animations. + * dismissing and tile from-view animations, as well as the option to show over lockscreen. */ interface QSTileIntentUserInputHandler { - fun handle(expandable: Expandable?, intent: Intent) + fun handle( + expandable: Expandable?, + intent: Intent, + dismissShadeShowOverLockScreenWhenLocked: Boolean = false + ) /** @param requestLaunchingDefaultActivity used in case !pendingIndent.isActivity */ fun handle( @@ -52,12 +56,25 @@ constructor( private val userHandle: UserHandle, ) : QSTileIntentUserInputHandler { - override fun handle(expandable: Expandable?, intent: Intent) { + override fun handle( + expandable: Expandable?, + intent: Intent, + dismissShadeShowOverLockScreenWhenLocked: Boolean + ) { val animationController: ActivityTransitionAnimator.Controller? = expandable?.activityTransitionController( InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE ) - activityStarter.postStartActivityDismissingKeyguard(intent, 0, animationController) + if (dismissShadeShowOverLockScreenWhenLocked) { + activityStarter.startActivity( + intent, + true /* dismissShade */, + animationController, + true /* showOverLockscreenWhenLocked */ + ) + } else { + activityStarter.postStartActivityDismissingKeyguard(intent, 0, animationController) + } } // TODO(b/249804373): make sure to allow showing activities over the lockscreen. See b/292112939 diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java index c9c44434361d..c971f547c302 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java @@ -15,6 +15,7 @@ */ package com.android.systemui.qs.tiles.dialog; +import static com.android.settingslib.satellite.SatelliteDialogUtils.TYPE_IS_WIFI; import static com.android.systemui.Prefs.Key.QS_HAS_TURNED_OFF_MOBILE_DATA; import static com.android.systemui.qs.tiles.dialog.InternetDialogController.MAX_WIFI_ENTRY_COUNT; @@ -57,6 +58,8 @@ import androidx.recyclerview.widget.RecyclerView; import com.android.internal.logging.UiEvent; import com.android.internal.logging.UiEventLogger; +import com.android.internal.telephony.flags.Flags; +import com.android.settingslib.satellite.SatelliteDialogUtils; import com.android.settingslib.wifi.WifiEnterpriseRestrictionUtils; import com.android.systemui.Prefs; import com.android.systemui.accessibility.floatingmenu.AnnotationLinkSpan; @@ -73,6 +76,7 @@ import dagger.assisted.AssistedFactory; import dagger.assisted.AssistedInject; import kotlinx.coroutines.CoroutineScope; +import kotlinx.coroutines.Job; import java.util.List; import java.util.concurrent.Executor; @@ -161,6 +165,9 @@ public class InternetDialogDelegate implements // Wi-Fi scanning progress bar protected boolean mIsProgressBarVisible; private SystemUIDialog mDialog; + private final CoroutineScope mCoroutineScope; + @Nullable + private Job mClickJob; @AssistedFactory public interface Factory { @@ -203,7 +210,7 @@ public class InternetDialogDelegate implements mCanConfigWifi = canConfigWifi; mCanChangeWifiState = WifiEnterpriseRestrictionUtils.isChangeWifiStateAllowed(context); mKeyguard = keyguardStateController; - + mCoroutineScope = coroutineScope; mUiEventLogger = uiEventLogger; mDialogTransitionAnimator = dialogTransitionAnimator; mAdapter = new InternetAdapter(mInternetDialogController, coroutineScope); @@ -388,11 +395,9 @@ public class InternetDialogDelegate implements }); mConnectedWifListLayout.setOnClickListener(this::onClickConnectedWifi); mSeeAllLayout.setOnClickListener(this::onClickSeeMoreButton); - mWiFiToggle.setOnCheckedChangeListener( - (buttonView, isChecked) -> { - if (mInternetDialogController.isWifiEnabled() == isChecked) return; - mInternetDialogController.setWifiEnabled(isChecked); - }); + mWiFiToggle.setOnClickListener(v -> { + handleWifiToggleClicked(mWiFiToggle.isChecked()); + }); mDoneButton.setOnClickListener(v -> dialog.dismiss()); mShareWifiButton.setOnClickListener(v -> { if (mInternetDialogController.mayLaunchShareWifiSettings(mConnectedWifiEntry, v)) { @@ -404,6 +409,32 @@ public class InternetDialogDelegate implements }); } + private void handleWifiToggleClicked(boolean isChecked) { + if (Flags.oemEnabledSatelliteFlag()) { + if (mClickJob != null && !mClickJob.isCompleted()) { + return; + } + mClickJob = SatelliteDialogUtils.mayStartSatelliteWarningDialog( + mDialog.getContext(), mCoroutineScope, TYPE_IS_WIFI, isAllowClick -> { + if (isAllowClick) { + setWifiEnable(isChecked); + } else { + mWiFiToggle.setChecked(!isChecked); + } + return null; + }); + return; + } + setWifiEnable(isChecked); + } + + private void setWifiEnable(boolean isChecked) { + if (mInternetDialogController.isWifiEnabled() == isChecked) { + return; + } + mInternetDialogController.setWifiEnabled(isChecked); + } + @MainThread private void updateEthernet() { mEthernetLayout.setVisibility( diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileDataInteractor.kt new file mode 100644 index 000000000000..1e8ce588b4e0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileDataInteractor.kt @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.impl.qr.domain.interactor + +import android.os.UserHandle +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.qrcodescanner.controller.QRCodeScannerController +import com.android.systemui.qrcodescanner.controller.QRCodeScannerController.DEFAULT_QR_CODE_SCANNER_CHANGE +import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger +import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor +import com.android.systemui.qs.tiles.impl.qr.domain.model.QRCodeScannerTileModel +import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow +import javax.inject.Inject +import kotlin.coroutines.CoroutineContext +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.flow.stateIn + +/** Observes one qr scanner state changes providing the [QRCodeScannerTileModel]. */ +class QRCodeScannerTileDataInteractor +@Inject +constructor( + @Background private val bgCoroutineContext: CoroutineContext, + @Application private val scope: CoroutineScope, + private val qrController: QRCodeScannerController, +) : QSTileDataInteractor<QRCodeScannerTileModel> { + override fun tileData( + user: UserHandle, + triggers: Flow<DataUpdateTrigger> + ): Flow<QRCodeScannerTileModel> = + conflatedCallbackFlow { + qrController.registerQRCodeScannerChangeObservers(DEFAULT_QR_CODE_SCANNER_CHANGE) + val callback = + object : QRCodeScannerController.Callback { + override fun onQRCodeScannerActivityChanged() { + trySend(generateModel()) + } + } + qrController.addCallback(callback) + awaitClose { + qrController.removeCallback(callback) + qrController.unregisterQRCodeScannerChangeObservers( + DEFAULT_QR_CODE_SCANNER_CHANGE + ) + } + } + .onStart { emit(generateModel()) } + .flowOn(bgCoroutineContext) + .stateIn( + scope, + SharingStarted.WhileSubscribed(), + QRCodeScannerTileModel.TemporarilyUnavailable + ) + + override fun availability(user: UserHandle): Flow<Boolean> = + flowOf(qrController.isCameraAvailable) + + private fun generateModel(): QRCodeScannerTileModel { + val intent = qrController.intent + + return if (qrController.isAbleToLaunchScannerActivity && intent != null) + QRCodeScannerTileModel.Available(intent) + else QRCodeScannerTileModel.TemporarilyUnavailable + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractor.kt new file mode 100644 index 000000000000..7c0c41eca4bc --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractor.kt @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.impl.qr.domain.interactor + +import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler +import com.android.systemui.qs.tiles.base.interactor.QSTileInput +import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor +import com.android.systemui.qs.tiles.impl.qr.domain.model.QRCodeScannerTileModel +import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction +import javax.inject.Inject + +/** Handles qr tile clicks. */ +class QRCodeScannerTileUserActionInteractor +@Inject +constructor( + private val qsTileIntentUserActionHandler: QSTileIntentUserInputHandler, +) : QSTileUserActionInteractor<QRCodeScannerTileModel> { + + override suspend fun handleInput(input: QSTileInput<QRCodeScannerTileModel>): Unit = + with(input) { + when (action) { + is QSTileUserAction.Click -> { + when (data) { + is QRCodeScannerTileModel.Available -> + qsTileIntentUserActionHandler.handle( + action.expandable, + data.intent, + true + ) + is QRCodeScannerTileModel.TemporarilyUnavailable -> {} // no-op + } + } + is QSTileUserAction.LongClick -> {} // no-op + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/contrast/ContrastDialogActivity.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/model/QRCodeScannerTileModel.kt index 4e40042a49b0..22c9b66b2806 100644 --- a/packages/SystemUI/src/com/android/systemui/contrast/ContrastDialogActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/model/QRCodeScannerTileModel.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 The Android Open Source Project + * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,22 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.systemui.contrast -import android.app.Activity -import android.os.Bundle -import javax.inject.Inject +package com.android.systemui.qs.tiles.impl.qr.domain.model -/** Trampoline activity responsible for creating a [ContrastDialogDelegate] */ -class ContrastDialogActivity -@Inject -constructor( - private val contrastDialogDelegate : ContrastDialogDelegate -) : Activity() { +import android.content.Intent - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - contrastDialogDelegate.createDialog().show() - finish() - } +/** qr scanner tile model. */ +sealed interface QRCodeScannerTileModel { + data class Available(val intent: Intent) : QRCodeScannerTileModel + data object TemporarilyUnavailable : QRCodeScannerTileModel } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapper.kt new file mode 100644 index 000000000000..45a77179fb3f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapper.kt @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.impl.qr.ui + +import android.content.res.Resources +import com.android.systemui.common.shared.model.Icon +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper +import com.android.systemui.qs.tiles.impl.qr.domain.model.QRCodeScannerTileModel +import com.android.systemui.qs.tiles.viewmodel.QSTileConfig +import com.android.systemui.qs.tiles.viewmodel.QSTileState +import com.android.systemui.res.R +import javax.inject.Inject + +/** Maps [QRCodeScannerTileModel] to [QSTileState]. */ +class QRCodeScannerTileMapper +@Inject +constructor( + @Main private val resources: Resources, + private val theme: Resources.Theme, +) : QSTileDataToStateMapper<QRCodeScannerTileModel> { + + override fun map(config: QSTileConfig, data: QRCodeScannerTileModel): QSTileState = + QSTileState.build(resources, theme, config.uiConfig) { + label = resources.getString(R.string.qr_code_scanner_title) + contentDescription = label + icon = { + Icon.Loaded(resources.getDrawable(R.drawable.ic_qr_code_scanner, theme), null) + } + sideViewIcon = QSTileState.SideViewIcon.Chevron + supportedActions = setOf(QSTileState.UserAction.CLICK) + + when (data) { + is QRCodeScannerTileModel.Available -> { + activationState = QSTileState.ActivationState.INACTIVE + secondaryLabel = null + } + is QRCodeScannerTileModel.TemporarilyUnavailable -> { + activationState = QSTileState.ActivationState.UNAVAILABLE + secondaryLabel = + resources.getString(R.string.qr_code_scanner_updating_secondary_label) + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/scroll/CropView.java b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/CropView.java index 5e561cfb14a1..ee1944e0b5cf 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/scroll/CropView.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/CropView.java @@ -45,6 +45,7 @@ import androidx.customview.widget.ExploreByTouchHelper; import androidx.interpolator.view.animation.FastOutSlowInInterpolator; import com.android.internal.graphics.ColorUtils; +import com.android.systemui.Flags; import com.android.systemui.res.R; import java.util.List; @@ -378,8 +379,14 @@ public class CropView extends View { upper = 1; break; } - Log.i(TAG, "getAllowedValues: " + boundary + ", " - + "result=[lower=" + lower + ", upper=" + upper + "]"); + if (lower >= upper) { + Log.wtf(TAG, "getAllowedValues computed an invalid range " + + "[" + lower + ", " + upper + "]"); + if (Flags.screenshotScrollCropViewCrashFix()) { + lower = Math.min(lower, upper); + upper = lower; + } + } return new Range<>(lower, upper); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallBackgroundContainer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/view/ChipBackgroundContainer.kt index ce88a5f5e55f..cae86a652245 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallBackgroundContainer.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/view/ChipBackgroundContainer.kt @@ -14,18 +14,18 @@ * limitations under the License. */ -package com.android.systemui.statusbar.phone.ongoingcall +package com.android.systemui.statusbar.chips.ui.view import android.content.Context import android.util.AttributeSet import com.android.systemui.animation.view.LaunchableLinearLayout /** - * A container view for the ongoing call chip background. Needed so that we can limit the height of - * the background when the font size is very large (200%), in which case the background would go + * A container view for the ongoing activity chip background. Needed so that we can limit the height + * of the background when the font size is very large (200%), in which case the background would go * past the bounds of the status bar. */ -class OngoingCallBackgroundContainer(context: Context, attrs: AttributeSet) : +class ChipBackgroundContainer(context: Context, attrs: AttributeSet) : LaunchableLinearLayout(context, attrs) { /** Sets where this view should fetch its max height from. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/view/ChipChronometer.kt index bb7ba4c4174f..ff3061e850d9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometer.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/view/ChipChronometer.kt @@ -14,36 +14,34 @@ * limitations under the License. */ -package com.android.systemui.statusbar.phone.ongoingcall +package com.android.systemui.statusbar.chips.ui.view import android.content.Context import android.util.AttributeSet - import android.widget.Chronometer import androidx.annotation.UiThread /** - * A [Chronometer] specifically for the ongoing call chip in the status bar. + * A [Chronometer] specifically for chips in the status bar that show ongoing duration of an + * activity. * * This class handles: - * 1) Setting the text width. If we used a basic WRAP_CONTENT for width, the chip width would - * change slightly each second because the width of each number is slightly different. - * - * Instead, we save the largest number width seen so far and ensure that the chip is at least - * that wide. This means the chip may get larger over time (e.g. in the transition from 59:59 - * to 1:00:00), but never smaller. + * 1) Setting the text width. If we used a basic WRAP_CONTENT for width, the chip width would change + * slightly each second because the width of each number is slightly different. * - * 2) Hiding the text if the time gets too long for the space available. Once the text has been - * hidden, it remains hidden for the duration of the call. + * Instead, we save the largest number width seen so far and ensure that the chip is at least + * that wide. This means the chip may get larger over time (e.g. in the transition from 59:59 to + * 1:00:00), but never smaller. + * 2) Hiding the text if the time gets too long for the space available. Once the text has been + * hidden, it remains hidden for the duration of the activity. * * Note that if the text was too big in portrait mode, resulting in the text being hidden, then the * text will also be hidden in landscape (even if there is enough space for it in landscape). */ -class OngoingCallChronometer @JvmOverloads constructor( - context: Context, - attrs: AttributeSet? = null, - defStyle: Int = 0 -) : Chronometer(context, attrs, defStyle) { +class ChipChronometer +@JvmOverloads +constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) : + Chronometer(context, attrs, defStyle) { // Minimum width that the text view can be. Corresponds with the largest number width seen so // far. @@ -53,8 +51,8 @@ class OngoingCallChronometer @JvmOverloads constructor( private var shouldHideText: Boolean = false override fun setBase(base: Long) { - // These variables may have changed during the previous call, so re-set them before the new - // call starts. + // These variables may have changed during the previous activity, so re-set them before the + // new activity starts. minimumTextWidth = 0 shouldHideText = false visibility = VISIBLE @@ -75,9 +73,7 @@ class OngoingCallChronometer @JvmOverloads constructor( } // Evaluate how wide the text *wants* to be if it had unlimited space. - super.onMeasure( - MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), - heightMeasureSpec) + super.onMeasure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), heightMeasureSpec) val desiredTextWidth = measuredWidth // Evaluate how wide the text *can* be based on the enforced constraints diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java index 968b591b9a09..5a616dfd1f63 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java @@ -18,7 +18,7 @@ package com.android.systemui.statusbar.notification.footer.ui.view; import static android.graphics.PorterDuff.Mode.SRC_ATOP; -import static com.android.systemui.Flags.notificationBackgroundTintOptimization; +import static com.android.systemui.Flags.notificationFooterBackgroundTintOptimization; import static com.android.systemui.util.ColorUtilKt.hexColorString; import android.annotation.ColorInt; @@ -407,7 +407,7 @@ public class FooterView extends StackScrollerDecorView { final Drawable clearAllBg = theme.getDrawable(R.drawable.notif_footer_btn_background); final Drawable manageBg = theme.getDrawable(R.drawable.notif_footer_btn_background); final @ColorInt int scHigh; - if (!notificationBackgroundTintOptimization()) { + if (!notificationFooterBackgroundTintOptimization()) { scHigh = Utils.getColorAttrDefaultColor(mContext, com.android.internal.R.attr.materialColorSurfaceContainerHigh); if (scHigh != 0) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt index a901c5f8ae2a..87f11f131f32 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.notification.interruption +import android.Manifest.permission.RECEIVE_EMERGENCY_BROADCAST import android.app.Notification import android.app.Notification.BubbleMetadata import android.app.Notification.CATEGORY_EVENT @@ -23,6 +24,8 @@ import android.app.Notification.CATEGORY_REMINDER import android.app.Notification.VISIBILITY_PRIVATE import android.app.NotificationManager.IMPORTANCE_DEFAULT import android.app.NotificationManager.IMPORTANCE_HIGH +import android.content.pm.PackageManager +import android.content.pm.PackageManager.PERMISSION_GRANTED import android.database.ContentObserver import android.hardware.display.AmbientDisplayConfiguration import android.os.Handler @@ -234,6 +237,7 @@ class AvalancheSuppressor( private val avalancheProvider: AvalancheProvider, private val systemClock: SystemClock, private val systemSettings: SystemSettings, + private val packageManager: PackageManager, ) : VisualInterruptionFilter( types = setOf(PEEK, PULSE), @@ -249,6 +253,7 @@ class AvalancheSuppressor( ALLOW_CATEGORY_EVENT, ALLOW_FSI_WITH_PERMISSION_ON, ALLOW_COLORIZED, + ALLOW_EMERGENCY, SUPPRESS } @@ -299,13 +304,20 @@ class AvalancheSuppressor( if (entry.sbn.notification.isColorized) { return State.ALLOW_COLORIZED } + if (entry.sbn.notification.isColorized) { + return State.ALLOW_COLORIZED + } + if ( + packageManager.checkPermission(RECEIVE_EMERGENCY_BROADCAST, entry.sbn.packageName) == + PERMISSION_GRANTED + ) { + return State.ALLOW_EMERGENCY + } return State.SUPPRESS } private fun isCooldownEnabled(): Boolean { - return systemSettings.getInt( - Settings.System.NOTIFICATION_COOLDOWN_ENABLED, - /* def */ 1 - ) == 1 + return systemSettings.getInt(Settings.System.NOTIFICATION_COOLDOWN_ENABLED, /* def */ 1) == + 1 } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt index e6d97c211dc5..f68e194aace2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt @@ -15,6 +15,7 @@ */ package com.android.systemui.statusbar.notification.interruption +import android.content.pm.PackageManager import android.hardware.display.AmbientDisplayConfiguration import android.os.Handler import android.os.PowerManager @@ -63,7 +64,8 @@ constructor( private val uiEventLogger: UiEventLogger, private val userTracker: UserTracker, private val avalancheProvider: AvalancheProvider, - private val systemSettings: SystemSettings + private val systemSettings: SystemSettings, + private val packageManager: PackageManager ) : VisualInterruptionDecisionProvider { init { @@ -172,7 +174,9 @@ constructor( addFilter(AlertKeyguardVisibilitySuppressor(keyguardNotificationVisibilityProvider)) if (NotificationAvalancheSuppression.isEnabled) { - addFilter(AvalancheSuppressor(avalancheProvider, systemClock, systemSettings)) + addFilter( + AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager) + ) avalancheProvider.register() } started = true @@ -232,14 +236,17 @@ constructor( private fun makeLoggablePeekDecision(entry: NotificationEntry): LoggableDecision = checkConditions(PEEK) - ?: checkFilters(PEEK, entry) ?: checkSuppressInterruptions(entry) - ?: checkSuppressAwakeInterruptions(entry) ?: checkSuppressAwakeHeadsUp(entry) - ?: LoggableDecision.unsuppressed + ?: checkFilters(PEEK, entry) + ?: checkSuppressInterruptions(entry) + ?: checkSuppressAwakeInterruptions(entry) + ?: checkSuppressAwakeHeadsUp(entry) + ?: LoggableDecision.unsuppressed private fun makeLoggablePulseDecision(entry: NotificationEntry): LoggableDecision = checkConditions(PULSE) - ?: checkFilters(PULSE, entry) ?: checkSuppressInterruptions(entry) - ?: LoggableDecision.unsuppressed + ?: checkFilters(PULSE, entry) + ?: checkSuppressInterruptions(entry) + ?: LoggableDecision.unsuppressed override fun makeAndLogBubbleDecision(entry: NotificationEntry): Decision = traceSection("VisualInterruptionDecisionProviderImpl#makeAndLogBubbleDecision") { @@ -252,8 +259,10 @@ constructor( private fun makeLoggableBubbleDecision(entry: NotificationEntry): LoggableDecision = checkConditions(BUBBLE) - ?: checkFilters(BUBBLE, entry) ?: checkSuppressInterruptions(entry) - ?: checkSuppressAwakeInterruptions(entry) ?: LoggableDecision.unsuppressed + ?: checkFilters(BUBBLE, entry) + ?: checkSuppressInterruptions(entry) + ?: checkSuppressAwakeInterruptions(entry) + ?: LoggableDecision.unsuppressed private fun logDecision( type: VisualInterruptionType, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HeadsUpStyleProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HeadsUpStyleProvider.kt index db3cf5abe618..9d0fcd3acceb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HeadsUpStyleProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HeadsUpStyleProvider.kt @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.notification.row import android.app.Flags +import android.os.SystemProperties import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore import javax.inject.Inject @@ -34,8 +35,13 @@ constructor(private val statusBarModeRepositoryStore: StatusBarModeRepositorySto HeadsUpStyleProvider { override fun shouldApplyCompactStyle(): Boolean { - // Use compact HUN for immersive mode. - return Flags.compactHeadsUpNotification() && - statusBarModeRepositoryStore.defaultDisplay.isInFullscreenMode.value + return Flags.compactHeadsUpNotification() && (isInImmersiveMode() || alwaysShow()) } + + private fun isInImmersiveMode() = + statusBarModeRepositoryStore.defaultDisplay.isInFullscreenMode.value + + /** developer setting to always show Minimal HUN, even if the device is not in full screen */ + private fun alwaysShow() = + SystemProperties.getBoolean("persist.compact_heads_up_notification.always_show", false) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationHeadsUpCycling.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationHeadsUpCycling.kt index d4f8ea385667..d6c73a9dda9e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationHeadsUpCycling.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationHeadsUpCycling.kt @@ -23,8 +23,8 @@ import com.android.systemui.flags.RefactorFlagUtils /** Helper for reading or using the heads-up cycling flag state. */ @Suppress("NOTHING_TO_INLINE") object NotificationHeadsUpCycling { - /** The aconfig flag name */ - const val FLAG_NAME = Flags.FLAG_NOTIFICATION_HEADS_UP_CYCLING + /** The aconfig flag name - enable this feature when FLAG_NOTIFICATION_THROTTLE_HUN is on. */ + const val FLAG_NAME = Flags.FLAG_NOTIFICATION_THROTTLE_HUN /** A token used for dependency declaration */ val token: FlagToken @@ -33,7 +33,7 @@ object NotificationHeadsUpCycling { /** Is the heads-up cycling animation enabled */ @JvmStatic inline val isEnabled - get() = Flags.notificationHeadsUpCycling() + get() = Flags.notificationThrottleHun() /** Whether to animate the bottom line when transiting from a tall HUN to a short HUN */ @JvmStatic diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index abbe7d74733d..17b54c8f3970 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -674,7 +674,7 @@ public class NotificationStackScrollLayout void setOverExpansion(float margin) { mAmbientState.setOverExpansion(margin); if (notificationOverExpansionClippingFix() && !SceneContainerFlag.isEnabled()) { - setRoundingClippingYTranslation((int) margin); + setRoundingClippingYTranslation(mShouldUseSplitNotificationShade ? (int) margin : 0); } updateStackPosition(); requestChildrenUpdate(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java index 4fc11df5aaa5..a858fb079d72 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java @@ -119,7 +119,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue private MultiSourceMinAlphaController mEndSideAlphaController; private LinearLayout mEndSideContent; private View mClockView; - private View mOngoingCallChip; + private View mOngoingActivityChip; private View mNotificationIconAreaInner; // Visibilities come in from external system callers via disable flags, but we also sometimes // modify the visibilities internally. We need to store both so that we don't accidentally @@ -345,7 +345,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue mEndSideContent = mStatusBar.findViewById(R.id.status_bar_end_side_content); mEndSideAlphaController = new MultiSourceMinAlphaController(mEndSideContent); mClockView = mStatusBar.findViewById(R.id.clock); - mOngoingCallChip = mStatusBar.findViewById(R.id.ongoing_call_chip); + mOngoingActivityChip = mStatusBar.findViewById(R.id.ongoing_activity_chip); showEndSideContent(false); showClock(false); initOperatorName(); @@ -594,9 +594,9 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue // so if the icons are disabled then the call chip should be, too.) boolean showOngoingCallChip = hasOngoingCall && !disableNotifications; if (showOngoingCallChip) { - showOngoingCallChip(animate); + showOngoingActivityChip(animate); } else { - hideOngoingCallChip(animate); + hideOngoingActivityChip(animate); } mOngoingCallController.notifyChipVisibilityChanged(showOngoingCallChip); } @@ -688,14 +688,19 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue animateShow(mClockView, animate); } - /** Hides the ongoing call chip. */ - public void hideOngoingCallChip(boolean animate) { - animateHiddenState(mOngoingCallChip, View.GONE, animate); + /** Hides the ongoing activity chip. */ + private void hideOngoingActivityChip(boolean animate) { + animateHiddenState(mOngoingActivityChip, View.GONE, animate); } - /** Displays the ongoing call chip. */ - public void showOngoingCallChip(boolean animate) { - animateShow(mOngoingCallChip, animate); + /** + * Displays the ongoing activity chip. + * + * For now, this chip will only ever contain the ongoing call information, but after b/332662551 + * feature is implemented, it will support different kinds of ongoing activities. + */ + private void showOngoingActivityChip(boolean animate) { + animateShow(mOngoingActivityChip, animate); } /** @@ -803,7 +808,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue private void initOngoingCallChip() { mOngoingCallController.addCallback(mOngoingCallListener); - mOngoingCallController.setChipView(mOngoingCallChip); + mOngoingCallController.setChipView(mOngoingActivityChip); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt index ec88b6c56477..a7d4ce30a191 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt @@ -36,6 +36,8 @@ import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dump.DumpManager import com.android.systemui.plugins.ActivityStarter +import com.android.systemui.statusbar.chips.ui.view.ChipChronometer +import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore import com.android.systemui.statusbar.gesture.SwipeStatusBarAwayGestureHandler import com.android.systemui.statusbar.notification.collection.NotificationEntry @@ -145,8 +147,8 @@ class OngoingCallController @Inject constructor( fun setChipView(chipView: View) { tearDownChipView() this.chipView = chipView - val backgroundView: OngoingCallBackgroundContainer? = - chipView.findViewById(R.id.ongoing_call_chip_background) + val backgroundView: ChipBackgroundContainer? = + chipView.findViewById(R.id.ongoing_activity_chip_background) backgroundView?.maxHeightFetcher = { statusBarWindowController.statusBarHeight } if (hasOngoingCall()) { updateChip() @@ -226,7 +228,7 @@ class OngoingCallController @Inject constructor( if (callNotificationInfo == null) { return } val currentChipView = chipView val backgroundView = - currentChipView?.findViewById<View>(R.id.ongoing_call_chip_background) + currentChipView?.findViewById<View>(R.id.ongoing_activity_chip_background) val intent = callNotificationInfo?.intent if (currentChipView != null && backgroundView != null && intent != null) { currentChipView.setOnClickListener { @@ -271,8 +273,8 @@ class OngoingCallController @Inject constructor( @VisibleForTesting fun tearDownChipView() = chipView?.getTimeView()?.stop() - private fun View.getTimeView(): OngoingCallChronometer? { - return this.findViewById(R.id.ongoing_call_chip_time) + private fun View.getTimeView(): ChipChronometer? { + return this.findViewById(R.id.ongoing_activity_chip_time) } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepository.kt index 9c78ab42a14a..886481e64dbe 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepository.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 The Android Open Source Project + * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt index d4b2dbff078b..2e54972c4950 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt @@ -53,6 +53,27 @@ constructor( ) } + fun logTopLevelServiceStateBroadcastEmergencyOnly(subId: Int, serviceState: ServiceState) { + buffer.log( + TAG, + LogLevel.INFO, + { + int1 = subId + bool1 = serviceState.isEmergencyOnly + }, + { "ACTION_SERVICE_STATE for subId=$int1. ServiceState.isEmergencyOnly=$bool1" } + ) + } + + fun logTopLevelServiceStateBroadcastMissingExtras(subId: Int) { + buffer.log( + TAG, + LogLevel.INFO, + { int1 = subId }, + { "ACTION_SERVICE_STATE for subId=$int1. Intent is missing extras. Ignoring" } + ) + } + fun logOnSignalStrengthsChanged(signalStrength: SignalStrength, subId: Int) { buffer.log( TAG, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/ServiceStateModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/ServiceStateModel.kt new file mode 100644 index 000000000000..cce3eb02023b --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/ServiceStateModel.kt @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.pipeline.mobile.data.model + +import android.telephony.ServiceState + +/** + * Simplified representation of a [ServiceState] for use in SystemUI. Add any fields that we need to + * extract from service state here for consumption downstream + */ +data class ServiceStateModel(val isEmergencyOnly: Boolean) { + companion object { + fun fromServiceState(serviceState: ServiceState): ServiceStateModel { + return ServiceStateModel(isEmergencyOnly = serviceState.isEmergencyOnly) + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt index 9471574fc755..5ad8bf1652b6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt @@ -21,6 +21,7 @@ import android.telephony.SubscriptionManager import com.android.settingslib.SignalIcon.MobileIconGroup import com.android.settingslib.mobile.MobileMappings import com.android.settingslib.mobile.MobileMappings.Config +import com.android.systemui.statusbar.pipeline.mobile.data.model.ServiceStateModel import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow @@ -92,6 +93,19 @@ interface MobileConnectionsRepository { val defaultMobileIconGroup: Flow<MobileIconGroup> /** + * [deviceServiceState] is equivalent to the last [Intent.ACTION_SERVICE_STATE] broadcast with a + * subscriptionId of -1 (aka [SubscriptionManager.INVALID_SUBSCRIPTION_ID]). + * + * While each [MobileConnectionsRepository] listens for the service state of each subscription, + * there is potentially a service state associated with the device itself. This value can be + * used to calculate e.g., the emergency calling capability of the device (as opposed to the + * emergency calling capability of an individual mobile connection) + * + * Note: this is a [StateFlow] using an eager sharing strategy. + */ + val deviceServiceState: StateFlow<ServiceStateModel?> + + /** * If any active SIM on the device is in * [android.telephony.TelephonyManager.SIM_STATE_PIN_REQUIRED] or * [android.telephony.TelephonyManager.SIM_STATE_PUK_REQUIRED] or diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt index 8a8e33efbcef..b0681525a137 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt @@ -25,6 +25,7 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.demomode.DemoMode import com.android.systemui.demomode.DemoModeController +import com.android.systemui.statusbar.pipeline.mobile.data.model.ServiceStateModel import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.DemoMobileConnectionsRepository import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileConnectionsRepositoryImpl @@ -151,6 +152,15 @@ constructor( override val defaultMobileIconGroup: Flow<SignalIcon.MobileIconGroup> = activeRepo.flatMapLatest { it.defaultMobileIconGroup } + override val deviceServiceState: StateFlow<ServiceStateModel?> = + activeRepo + .flatMapLatest { it.deviceServiceState } + .stateIn( + scope, + SharingStarted.WhileSubscribed(), + realRepository.deviceServiceState.value + ) + override val isAnySimSecure: Flow<Boolean> = activeRepo.flatMapLatest { it.isAnySimSecure } override fun getIsAnySimSecure(): Boolean = activeRepo.value.getIsAnySimSecure() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt index 2b3c6326032c..a944e9133a31 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt @@ -27,6 +27,7 @@ import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.log.table.TableLogBufferFactory import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.DefaultNetworkType +import com.android.systemui.statusbar.pipeline.mobile.data.model.ServiceStateModel import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository @@ -136,6 +137,9 @@ constructor( override val defaultMobileIconGroup = flowOf(TelephonyIcons.THREE_G) + // TODO(b/339023069): demo command for device-based connectivity state + override val deviceServiceState: StateFlow<ServiceStateModel?> = MutableStateFlow(null) + override val isAnySimSecure: Flow<Boolean> = flowOf(getIsAnySimSecure()) override fun getIsAnySimSecure(): Boolean = false diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt index 0073e9cd3dd8..c32f0e8090ac 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt @@ -18,8 +18,10 @@ package com.android.systemui.statusbar.pipeline.mobile.data.repository.prod import android.annotation.SuppressLint import android.content.Context +import android.content.Intent import android.content.IntentFilter import android.telephony.CarrierConfigManager +import android.telephony.ServiceState import android.telephony.SubscriptionInfo import android.telephony.SubscriptionManager import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID @@ -35,7 +37,6 @@ import com.android.settingslib.SignalIcon.MobileIconGroup import com.android.settingslib.mobile.MobileMappings.Config import com.android.systemui.Dumpable import com.android.systemui.broadcast.BroadcastDispatcher -import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background @@ -47,6 +48,7 @@ import com.android.systemui.statusbar.pipeline.airplane.data.repository.Airplane import com.android.systemui.statusbar.pipeline.dagger.MobileSummaryLog import com.android.systemui.statusbar.pipeline.mobile.data.MobileInputLogger import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel +import com.android.systemui.statusbar.pipeline.mobile.data.model.ServiceStateModel import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy @@ -55,6 +57,7 @@ import com.android.systemui.statusbar.pipeline.shared.data.repository.Connectivi import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel import com.android.systemui.util.kotlin.pairwise +import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow import java.io.PrintWriter import java.lang.ref.WeakReference import javax.inject.Inject @@ -68,6 +71,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.filterNotNull import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapLatest @@ -169,6 +173,35 @@ constructor( } .flowOn(bgDispatcher) + /** Note that this flow is eager, so we don't miss any state */ + override val deviceServiceState: StateFlow<ServiceStateModel?> = + broadcastDispatcher + .broadcastFlow(IntentFilter(Intent.ACTION_SERVICE_STATE)) { intent, _ -> + val subId = + intent.getIntExtra( + SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, + INVALID_SUBSCRIPTION_ID + ) + + val extras = intent.extras + if (extras == null) { + logger.logTopLevelServiceStateBroadcastMissingExtras(subId) + return@broadcastFlow null + } + + val serviceState = ServiceState.newFromBundle(extras) + logger.logTopLevelServiceStateBroadcastEmergencyOnly(subId, serviceState) + if (subId == INVALID_SUBSCRIPTION_ID) { + // Assume that -1 here is the device's service state. We don't care about + // other ones. + ServiceStateModel.fromServiceState(serviceState) + } else { + null + } + } + .filterNotNull() + .stateIn(scope, SharingStarted.Eagerly, null) + /** * State flow that emits the set of mobile data subscriptions, each represented by its own * [SubscriptionModel]. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt index 91d7ca65b30d..cc4d5689c3fb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt @@ -111,6 +111,13 @@ interface MobileIconsInteractor { val isForceHidden: Flow<Boolean> /** + * True if the device-level service state (with -1 subscription id) reports emergency calls + * only. This value is only useful when there are no other subscriptions OR all existing + * subscriptions report that they are not in service. + */ + val isDeviceInEmergencyCallsOnlyMode: Flow<Boolean> + + /** * Vends out a [MobileIconInteractor] tracking the [MobileConnectionRepository] for the given * subId. */ @@ -377,6 +384,9 @@ constructor( .map { it.contains(ConnectivitySlot.MOBILE) } .stateIn(scope, SharingStarted.WhileSubscribed(), false) + override val isDeviceInEmergencyCallsOnlyMode: Flow<Boolean> = + mobileConnectionsRepo.deviceServiceState.map { it?.isEmergencyOnly ?: false } + /** Vends out new [MobileIconInteractor] for a particular subId */ override fun getMobileConnectionInteractorForSubId(subId: Int): MobileIconInteractor = reuseCache[subId]?.get() ?: createMobileConnectionInteractorForSubId(subId) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt index 51c053ee284a..5b954b272044 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt @@ -19,6 +19,9 @@ package com.android.systemui.statusbar.pipeline.satellite.domain.interactor import com.android.internal.telephony.flags.Flags import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.core.LogLevel +import com.android.systemui.statusbar.pipeline.dagger.OemSatelliteInputLog import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor import com.android.systemui.statusbar.pipeline.satellite.data.DeviceBasedSatelliteRepository import com.android.systemui.statusbar.pipeline.satellite.shared.model.SatelliteConnectionState @@ -45,6 +48,7 @@ constructor( deviceProvisioningInteractor: DeviceProvisioningInteractor, wifiInteractor: WifiInteractor, @Application scope: CoroutineScope, + @OemSatelliteInputLog private val logBuffer: LogBuffer, ) { /** Must be observed by any UI showing Satellite iconography */ val isSatelliteAllowed = @@ -79,25 +83,52 @@ constructor( val isWifiActive: Flow<Boolean> = wifiInteractor.wifiNetwork.map { it is WifiNetworkModel.Active } + private val allConnectionsOos = + iconsInteractor.icons.aggregateOver( + selector = { intr -> + combine(intr.isInService, intr.isEmergencyOnly, intr.isNonTerrestrial) { + isInService, + isEmergencyOnly, + isNtn -> + !isInService && !isEmergencyOnly && !isNtn + } + }, + defaultValue = true, // no connections == everything is OOS + ) { isOosAndNotEmergencyAndNotSatellite -> + isOosAndNotEmergencyAndNotSatellite.all { it } + } + /** When all connections are considered OOS, satellite connectivity is potentially valid */ val areAllConnectionsOutOfService = if (Flags.oemEnabledSatelliteFlag()) { - iconsInteractor.icons.aggregateOver( - selector = { intr -> - combine(intr.isInService, intr.isEmergencyOnly, intr.isNonTerrestrial) { - isInService, - isEmergencyOnly, - isNtn -> - !isInService && !(isEmergencyOnly || isNtn) - } - } - ) { isOosAndNotEmergencyOnlyOrSatellite -> - isOosAndNotEmergencyOnlyOrSatellite.all { it } + combine( + allConnectionsOos, + iconsInteractor.isDeviceInEmergencyCallsOnlyMode, + ) { connectionsOos, deviceEmergencyOnly -> + logBuffer.log( + TAG, + LogLevel.INFO, + { + bool1 = connectionsOos + bool2 = deviceEmergencyOnly + }, + { + "Updating OOS status. allConnectionsOOs=$bool1 " + + "deviceEmergencyOnly=$bool2" + }, + ) + // If no connections exist, or all are OOS, then we look to the device-based + // service state to detect if any calls are possible + connectionsOos && !deviceEmergencyOnly } } else { flowOf(false) } .stateIn(scope, SharingStarted.WhileSubscribed(), true) + + companion object { + const val TAG = "DeviceBasedSatelliteInteractor" + } } /** @@ -106,12 +137,22 @@ constructor( * * Provides a way to connect the reactivity of the top-level flow with the reactivity of an * arbitrarily-defined relationship ([selector]) from R to the flow that R exposes. + * + * [defaultValue] allows for a default value to be used if there are no leaf nodes after applying + * [selector]. E.g., if there are no mobile connections, assume that there is no service. */ @OptIn(ExperimentalCoroutinesApi::class) private inline fun <R, reified S, T> Flow<List<R>>.aggregateOver( crossinline selector: (R) -> Flow<S>, - crossinline transform: (Array<S>) -> T + defaultValue: T, + crossinline transform: (Array<S>) -> T, ): Flow<T> { return map { list -> list.map { selector(it) } } - .flatMapLatest { newFlows -> combine(newFlows) { newVals -> transform(newVals) } } + .flatMapLatest { newFlows -> + if (newFlows.isEmpty()) { + flowOf(defaultValue) + } else { + combine(newFlows) { newVals -> transform(newVals) } + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt index f44e7f3da6f0..8b48bd32e089 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt @@ -22,6 +22,7 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dump.DumpManager import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun import com.android.systemui.statusbar.policy.BaseHeadsUpManager.HeadsUpEntry +import com.android.systemui.util.Compile import java.io.PrintWriter import javax.inject.Inject @@ -37,7 +38,7 @@ constructor( ) : Dumpable { private val tag = "AvalancheController" - private val debug = false + private val debug = Compile.IS_DEBUG && Log.isLoggable(tag, Log.DEBUG) // HUN showing right now, in the floating state where full shade is hidden, on launcher or AOD @VisibleForTesting var headsUpEntryShowing: HeadsUpEntry? = null @@ -108,7 +109,10 @@ constructor( if (isOnlyNextEntry) { // HeadsUpEntry.updateEntry recursively calls AvalancheController#update // and goes to the isShowing case above - headsUpEntryShowing!!.updateEntry(false, "avalanche duration update") + headsUpEntryShowing!!.updateEntry( + /* updatePostTime= */ false, + /* updateEarliestRemovalTime= */ false, + /* reason= */ "avalanche duration update") } } logState("after $fn") diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java index b8318a7dfb61..a7fe49b54602 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java @@ -39,6 +39,7 @@ import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.res.R; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag; +import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun; import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor; import com.android.systemui.util.ListenerSet; import com.android.systemui.util.concurrency.DelayableExecutor; @@ -114,7 +115,8 @@ public abstract class BaseHeadsUpManager implements HeadsUpManager { mUiEventLogger = uiEventLogger; mAvalancheController = avalancheController; Resources resources = context.getResources(); - mMinimumDisplayTime = resources.getInteger(R.integer.heads_up_notification_minimum_time); + mMinimumDisplayTime = NotificationThrottleHun.isEnabled() + ? 500 : resources.getInteger(R.integer.heads_up_notification_minimum_time); mStickyForSomeTimeAutoDismissTime = resources.getInteger( R.integer.sticky_heads_up_notification_time); mAutoDismissTime = resources.getInteger(R.integer.heads_up_notification_decay); @@ -765,11 +767,23 @@ public abstract class BaseHeadsUpManager implements HeadsUpManager { * @param updatePostTime whether or not to refresh the post time */ public void updateEntry(boolean updatePostTime, @Nullable String reason) { + updateEntry(updatePostTime, /* updateEarliestRemovalTime= */ true, reason); + } + + /** + * Updates an entry's removal time. + * @param updatePostTime whether or not to refresh the post time + * @param updateEarliestRemovalTime whether this update should further delay removal + */ + public void updateEntry(boolean updatePostTime, boolean updateEarliestRemovalTime, + @Nullable String reason) { Runnable runnable = () -> { mLogger.logUpdateEntry(mEntry, updatePostTime, reason); final long now = mSystemClock.elapsedRealtime(); - mEarliestRemovalTime = now + mMinimumDisplayTime; + if (updateEarliestRemovalTime) { + mEarliestRemovalTime = now + mMinimumDisplayTime; + } if (updatePostTime) { mPostTime = Math.max(mPostTime, now); @@ -785,7 +799,9 @@ public abstract class BaseHeadsUpManager implements HeadsUpManager { FinishTimeUpdater finishTimeCalculator = () -> { final long finishTime = calculateFinishTime(); final long now = mSystemClock.elapsedRealtime(); - final long timeLeft = Math.max(finishTime - now, mMinimumDisplayTime); + final long timeLeft = NotificationThrottleHun.isEnabled() + ? Math.max(finishTime, mEarliestRemovalTime) - now + : Math.max(finishTime - now, mMinimumDisplayTime); return timeLeft; }; scheduleAutoRemovalCallback(finishTimeCalculator, "updateEntry (not sticky)"); @@ -818,13 +834,6 @@ public abstract class BaseHeadsUpManager implements HeadsUpManager { } public int compareNonTimeFields(HeadsUpEntry headsUpEntry) { - boolean isPinned = mEntry.isRowPinned(); - boolean otherPinned = headsUpEntry.mEntry.isRowPinned(); - if (isPinned && !otherPinned) { - return -1; - } else if (!isPinned && otherPinned) { - return 1; - } boolean selfFullscreen = hasFullScreenIntent(mEntry); boolean otherFullscreen = hasFullScreenIntent(headsUpEntry.mEntry); if (selfFullscreen && !otherFullscreen) { @@ -851,6 +860,13 @@ public abstract class BaseHeadsUpManager implements HeadsUpManager { } public int compareTo(@NonNull HeadsUpEntry headsUpEntry) { + boolean isPinned = mEntry.isRowPinned(); + boolean otherPinned = headsUpEntry.mEntry.isRowPinned(); + if (isPinned && !otherPinned) { + return -1; + } else if (!isPinned && otherPinned) { + return 1; + } int nonTimeCompareResult = compareNonTimeFields(headsUpEntry); if (nonTimeCompareResult != 0) { return nonTimeCompareResult; diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java index e0df1e0e5586..2d5e3a6788cc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java @@ -26,7 +26,6 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; import android.graphics.PointF; -import android.platform.test.annotations.EnableFlags; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.View; @@ -40,7 +39,6 @@ import androidx.dynamicanimation.animation.SpringAnimation; import androidx.dynamicanimation.animation.SpringForce; import androidx.test.filters.SmallTest; -import com.android.systemui.Flags; import com.android.systemui.Prefs; import com.android.systemui.SysuiTestCase; import com.android.systemui.accessibility.utils.TestUtils; @@ -230,7 +228,6 @@ public class MenuAnimationControllerTest extends SysuiTestCase { } @Test - @EnableFlags(Flags.FLAG_FLOATING_MENU_ANIMATED_TUCK) public void tuck_animates() { mMenuAnimationController.cancelAnimations(); mMenuAnimationController.moveToEdgeAndHide(); @@ -239,7 +236,6 @@ public class MenuAnimationControllerTest extends SysuiTestCase { } @Test - @EnableFlags(Flags.FLAG_FLOATING_MENU_ANIMATED_TUCK) public void untuck_animates() { mMenuAnimationController.cancelAnimations(); mMenuAnimationController.moveOutEdgeAndShow(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/contrast/ContrastDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/contrast/ContrastDialogDelegateTest.kt deleted file mode 100644 index ab034652e0cc..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/contrast/ContrastDialogDelegateTest.kt +++ /dev/null @@ -1,112 +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.contrast - -import android.app.UiModeManager -import android.app.UiModeManager.ContrastUtils.fromContrastLevel -import android.os.Looper -import android.provider.Settings -import android.testing.AndroidTestingRunner -import android.testing.TestableLooper.RunWithLooper -import android.view.LayoutInflater -import android.view.View -import android.widget.FrameLayout -import androidx.test.filters.SmallTest -import com.android.systemui.SysuiTestCase -import com.android.systemui.animation.DialogTransitionAnimator -import com.android.systemui.flags.FakeFeatureFlags -import com.android.systemui.flags.FeatureFlags -import com.android.systemui.model.SysUiState -import com.android.systemui.settings.UserTracker -import com.android.systemui.statusbar.phone.SystemUIDialog -import com.android.systemui.util.concurrency.FakeExecutor -import com.android.systemui.util.mockito.any -import com.android.systemui.util.mockito.whenever -import com.android.systemui.util.settings.SecureSettings -import com.android.systemui.util.time.FakeSystemClock -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.ArgumentCaptor -import org.mockito.ArgumentMatchers.anyInt -import org.mockito.Mock -import org.mockito.Mockito.eq -import org.mockito.Mockito.verify -import org.mockito.MockitoAnnotations - -/** Test the behaviour of buttons of the [ContrastDialogDelegate]. */ -@SmallTest -@RunWith(AndroidTestingRunner::class) -@RunWithLooper -class ContrastDialogDelegateTest : SysuiTestCase() { - - private val mainExecutor = FakeExecutor(FakeSystemClock()) - private lateinit var mContrastDialogDelegate: ContrastDialogDelegate - @Mock private lateinit var sysuiDialogFactory: SystemUIDialog.Factory - @Mock private lateinit var sysuiDialog: SystemUIDialog - @Mock private lateinit var mockUiModeManager: UiModeManager - @Mock private lateinit var mockUserTracker: UserTracker - @Mock private lateinit var mockSecureSettings: SecureSettings - @Mock private lateinit var sysuiState: SysUiState - - @Before - fun setUp() { - MockitoAnnotations.initMocks(this) - mDependency.injectTestDependency(FeatureFlags::class.java, FakeFeatureFlags()) - mDependency.injectTestDependency(SysUiState::class.java, sysuiState) - mDependency.injectMockDependency(DialogTransitionAnimator::class.java) - whenever(sysuiState.setFlag(any(), any())).thenReturn(sysuiState) - whenever(sysuiDialogFactory.create(any(SystemUIDialog.Delegate::class.java))) - .thenReturn(sysuiDialog) - whenever(sysuiDialog.layoutInflater).thenReturn(LayoutInflater.from(mContext)) - - whenever(mockUserTracker.userId).thenReturn(context.userId) - if (Looper.myLooper() == null) Looper.prepare() - - mContrastDialogDelegate = - ContrastDialogDelegate( - sysuiDialogFactory, - mainExecutor, - mockUiModeManager, - mockUserTracker, - mockSecureSettings - ) - - mContrastDialogDelegate.createDialog() - val viewCaptor = ArgumentCaptor.forClass(View::class.java) - verify(sysuiDialog).setView(viewCaptor.capture()) - whenever(sysuiDialog.requireViewById(anyInt()) as View?).then { - viewCaptor.value.requireViewById(it.getArgument(0)) - } - } - - @Test - fun testClickButtons_putsContrastInSettings() { - mContrastDialogDelegate.onCreate(sysuiDialog, null) - - mContrastDialogDelegate.contrastButtons.forEach { - (contrastLevel: Int, clickedButton: FrameLayout) -> - clickedButton.performClick() - mainExecutor.runAllReady() - verify(mockSecureSettings) - .putFloatForUser( - eq(Settings.Secure.CONTRAST_LEVEL), - eq(fromContrastLevel(contrastLevel)), - eq(context.userId) - ) - } - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt index 53560d740575..48a5df91d47c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt @@ -25,16 +25,19 @@ import androidx.test.filters.FlakyTest import androidx.test.filters.SmallTest import com.android.app.animation.Interpolators import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectValues import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.KeyguardState.AOD import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN +import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED import com.android.systemui.keyguard.shared.model.KeyguardState.OFF import com.android.systemui.keyguard.shared.model.TransitionInfo import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.keyguard.util.KeyguardTransitionRunner +import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat @@ -45,6 +48,8 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.dropWhile import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch +import kotlinx.coroutines.test.advanceTimeBy import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.After @@ -372,6 +377,43 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() { assertThat(wtfHandler.failed).isTrue() } + @Test + fun simulateRaceConditionIsProcessedInOrder() = + testScope.runTest { + val ktr = KeyguardTransitionRepositoryImpl(kosmos.testDispatcher) + val steps by collectValues(ktr.transitions.dropWhile { step -> step.from == OFF }) + + // Add a delay to the first transition in order to attempt to have the second transition + // be processed first + val info1 = TransitionInfo(OWNER_NAME, AOD, LOCKSCREEN, animator = null) + launch { + ktr.forceDelayForRaceConditionTest = true + ktr.startTransition(info1) + } + val info2 = TransitionInfo(OWNER_NAME, LOCKSCREEN, OCCLUDED, animator = null) + launch { + ktr.forceDelayForRaceConditionTest = false + ktr.startTransition(info2) + } + + runCurrent() + assertThat(steps.isEmpty()).isTrue() + + advanceTimeBy(60L) + assertThat(steps[0]) + .isEqualTo( + TransitionStep(info1.from, info1.to, 0f, TransitionState.STARTED, OWNER_NAME) + ) + assertThat(steps[1]) + .isEqualTo( + TransitionStep(info1.from, info1.to, 0f, TransitionState.CANCELED, OWNER_NAME) + ) + assertThat(steps[2]) + .isEqualTo( + TransitionStep(info2.from, info2.to, 0f, TransitionState.STARTED, OWNER_NAME) + ) + } + private fun listWithStep( step: BigDecimal, start: BigDecimal = BigDecimal.ZERO, diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt index 00f94a57a2d1..fa3fe5c80adb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt @@ -1669,7 +1669,9 @@ class KeyguardTransitionScenariosTest(flags: FlagsParameterization?) : SysuiTest // THEN a transition from DOZING => OCCLUDED should occur assertThat(transitionRepository) .startedTransition( - ownerName = "FromDozingTransitionInteractor", + ownerName = + "FromDozingTransitionInteractor" + + "(keyguardInteractor.onCameraLaunchDetected)", from = KeyguardState.DOZING, to = KeyguardState.OCCLUDED, animatorAssertion = { it.isNotNull() }, diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ActivityTaskManagerThumbnailLoaderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ActivityTaskManagerThumbnailLoaderTest.kt index db275ec190ac..db36131b825e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ActivityTaskManagerThumbnailLoaderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ActivityTaskManagerThumbnailLoaderTest.kt @@ -52,7 +52,7 @@ class ActivityTaskManagerThumbnailLoaderTest : SysuiTestCase() { val taskId = 123 val isLowResolution = false val snapshot = createTaskSnapshot() - val thumbnailData = ThumbnailData(snapshot) + val thumbnailData = ThumbnailData.fromSnapshot(snapshot) whenever(activityManager.getTaskThumbnail(taskId, isLowResolution)) .thenReturn(thumbnailData) @@ -74,7 +74,7 @@ class ActivityTaskManagerThumbnailLoaderTest : SysuiTestCase() { fun captureThumbnail_thumbnailAvailable_returnsThumbnailData() = testScope.runTest { val taskId = 321 - val thumbnailData = ThumbnailData(createTaskSnapshot()) + val thumbnailData = ThumbnailData.fromSnapshot(createTaskSnapshot()) whenever(activityManager.takeTaskThumbnail(taskId)).thenReturn(thumbnailData) diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/TileStateToProtoTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/TileStateToProtoTest.kt index 629c663943db..bc947fb910e5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/TileStateToProtoTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/TileStateToProtoTest.kt @@ -3,6 +3,7 @@ package com.android.systemui.qs import android.content.ComponentName import android.service.quicksettings.Tile import android.testing.AndroidTestingRunner +import android.widget.Switch import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.plugins.qs.QSTile @@ -66,15 +67,19 @@ class TileStateToProtoTest : SysuiTestCase() { assertThat(proto?.hasBooleanState()).isFalse() } + /** + * The [QSTile.AdapterState.expandedAccessibilityClassName] setting to [Switch] results in the + * proto having a booleanState. The value of that boolean is true iff the tile is active. + */ @Test - fun booleanState_ACTIVE() { + fun adapterState_ACTIVE() { val state = - QSTile.BooleanState().apply { + QSTile.AdapterState().apply { spec = TEST_SPEC label = TEST_LABEL secondaryLabel = TEST_SUBTITLE state = Tile.STATE_ACTIVE - value = true + expandedAccessibilityClassName = Switch::class.java.name } val proto = state.toProto() @@ -89,6 +94,33 @@ class TileStateToProtoTest : SysuiTestCase() { assertThat(proto?.booleanState).isTrue() } + /** + * Similar to [adapterState_ACTIVE], the use of + * [QSTile.AdapterState.expandedAccessibilityClassName] signals that the tile is toggleable. + */ + @Test + fun adapterState_INACTIVE() { + val state = + QSTile.AdapterState().apply { + spec = TEST_SPEC + label = TEST_LABEL + secondaryLabel = TEST_SUBTITLE + state = Tile.STATE_INACTIVE + expandedAccessibilityClassName = Switch::class.java.name + } + val proto = state.toProto() + + assertThat(proto).isNotNull() + assertThat(proto?.hasSpec()).isTrue() + assertThat(proto?.spec).isEqualTo(TEST_SPEC) + assertThat(proto?.hasComponentName()).isFalse() + assertThat(proto?.label).isEqualTo(TEST_LABEL) + assertThat(proto?.secondaryLabel).isEqualTo(TEST_SUBTITLE) + assertThat(proto?.state).isEqualTo(Tile.STATE_INACTIVE) + assertThat(proto?.hasBooleanState()).isTrue() + assertThat(proto?.booleanState).isFalse() + } + @Test fun noSpec_returnsNull() { val state = diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt index e2a3fac60ee8..ad87315c34f7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt @@ -22,7 +22,7 @@ import android.testing.AndroidTestingRunner import android.testing.TestableLooper import androidx.test.filters.SmallTest import com.android.internal.logging.MetricsLogger -import com.android.systemui.res.R +import com.android.internal.telephony.flags.Flags import com.android.systemui.SysuiTestCase import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.classifier.FalsingManagerFake @@ -33,10 +33,12 @@ import com.android.systemui.qs.QSHost import com.android.systemui.qs.QsEventLogger import com.android.systemui.qs.logging.QSLogger import com.android.systemui.qs.tileimpl.QSTileImpl +import com.android.systemui.res.R import com.android.systemui.settings.UserTracker import com.android.systemui.util.settings.GlobalSettings import com.google.common.truth.Truth.assertThat import dagger.Lazy +import kotlinx.coroutines.Job import org.junit.After import org.junit.Before import org.junit.Test @@ -44,11 +46,15 @@ import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito import org.mockito.MockitoAnnotations +import org.mockito.kotlin.any +import org.mockito.kotlin.times +import org.mockito.kotlin.verify @RunWith(AndroidTestingRunner::class) @TestableLooper.RunWithLooper(setAsMainLooper = true) @SmallTest class AirplaneModeTileTest : SysuiTestCase() { + @Mock private lateinit var mHost: QSHost @Mock @@ -62,7 +68,9 @@ class AirplaneModeTileTest : SysuiTestCase() { @Mock private lateinit var mBroadcastDispatcher: BroadcastDispatcher @Mock - private lateinit var mConnectivityManager: Lazy<ConnectivityManager> + private lateinit var mLazyConnectivityManager: Lazy<ConnectivityManager> + @Mock + private lateinit var mConnectivityManager: ConnectivityManager @Mock private lateinit var mGlobalSettings: GlobalSettings @Mock @@ -72,13 +80,15 @@ class AirplaneModeTileTest : SysuiTestCase() { private lateinit var mTestableLooper: TestableLooper private lateinit var mTile: AirplaneModeTile + @Mock + private lateinit var mClickJob: Job @Before fun setUp() { MockitoAnnotations.initMocks(this) mTestableLooper = TestableLooper.get(this) Mockito.`when`(mHost.context).thenReturn(mContext) Mockito.`when`(mHost.userContext).thenReturn(mContext) - + Mockito.`when`(mLazyConnectivityManager.get()).thenReturn(mConnectivityManager) mTile = AirplaneModeTile( mHost, mUiEventLogger, @@ -90,7 +100,7 @@ class AirplaneModeTileTest : SysuiTestCase() { mActivityStarter, mQsLogger, mBroadcastDispatcher, - mConnectivityManager, + mLazyConnectivityManager, mGlobalSettings, mUserTracker) } @@ -120,4 +130,24 @@ class AirplaneModeTileTest : SysuiTestCase() { assertThat(state.icon) .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_airplane_icon_on)) } + + @Test + fun handleClick_noSatelliteFeature_directSetAirplaneMode() { + mSetFlagsRule.disableFlags(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) + + mTile.handleClick(null) + + verify(mConnectivityManager).setAirplaneMode(any()) + } + + @Test + fun handleClick_hasSatelliteFeatureButClickIsProcessing_doNothing() { + mSetFlagsRule.enableFlags(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) + Mockito.`when`(mClickJob.isCompleted).thenReturn(false) + mTile.mClickJob = mClickJob + + mTile.handleClick(null) + + verify(mConnectivityManager, times(0)).setAirplaneMode(any()) + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt index 830f08a0c445..1ffbb7be49fc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt @@ -9,10 +9,11 @@ import android.testing.TestableLooper import android.testing.TestableLooper.RunWithLooper import androidx.test.filters.SmallTest import com.android.internal.logging.MetricsLogger +import com.android.internal.telephony.flags.Flags import com.android.settingslib.Utils import com.android.settingslib.bluetooth.CachedBluetoothDevice -import com.android.systemui.res.R import com.android.systemui.SysuiTestCase +import com.android.systemui.bluetooth.qsdialog.BluetoothTileDialogViewModel import com.android.systemui.classifier.FalsingManagerFake import com.android.systemui.flags.FeatureFlagsClassic import com.android.systemui.plugins.ActivityStarter @@ -23,13 +24,14 @@ import com.android.systemui.qs.QSHost import com.android.systemui.qs.QsEventLogger import com.android.systemui.qs.logging.QSLogger import com.android.systemui.qs.tileimpl.QSTileImpl -import com.android.systemui.bluetooth.qsdialog.BluetoothTileDialogViewModel +import com.android.systemui.res.R import com.android.systemui.statusbar.policy.BluetoothController import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.eq import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.Job import org.junit.After import org.junit.Before import org.junit.Test @@ -37,6 +39,7 @@ import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito.times import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @RunWith(AndroidTestingRunner::class) @@ -54,7 +57,7 @@ class BluetoothTileTest : SysuiTestCase() { @Mock private lateinit var uiEventLogger: QsEventLogger @Mock private lateinit var featureFlags: FeatureFlagsClassic @Mock private lateinit var bluetoothTileDialogViewModel: BluetoothTileDialogViewModel - + @Mock private lateinit var clickJob: Job private lateinit var testableLooper: TestableLooper private lateinit var tile: FakeBluetoothTile @@ -191,6 +194,41 @@ class BluetoothTileTest : SysuiTestCase() { } @Test + fun handleClick_hasSatelliteFeatureButNoQsTileDialogAndClickIsProcessing_doNothing() { + mSetFlagsRule.enableFlags(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) + `when`(featureFlags.isEnabled(com.android.systemui.flags.Flags.BLUETOOTH_QS_TILE_DIALOG)) + .thenReturn(false) + `when`(clickJob.isCompleted).thenReturn(false) + tile.mClickJob = clickJob + + tile.handleClick(null) + + verify(bluetoothController, times(0)).setBluetoothEnabled(any()) + } + + @Test + fun handleClick_noSatelliteFeatureAndNoQsTileDialog_directSetBtEnable() { + mSetFlagsRule.disableFlags(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) + `when`(featureFlags.isEnabled(com.android.systemui.flags.Flags.BLUETOOTH_QS_TILE_DIALOG)) + .thenReturn(false) + + tile.handleClick(null) + + verify(bluetoothController).setBluetoothEnabled(any()) + } + + @Test + fun handleClick_noSatelliteFeatureButHasQsTileDialog_showDialog() { + mSetFlagsRule.disableFlags(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) + `when`(featureFlags.isEnabled(com.android.systemui.flags.Flags.BLUETOOTH_QS_TILE_DIALOG)) + .thenReturn(true) + + tile.handleClick(null) + + verify(bluetoothTileDialogViewModel).showDialog(null) + } + + @Test fun testMetadataListener_whenDisconnected_isUnregistered() { val state = QSTile.BooleanState() val cachedDevice = mock<CachedBluetoothDevice>() diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/DragDownHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/DragDownHelperTest.kt index 9ec9b69d44c0..05d9495db091 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/DragDownHelperTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/DragDownHelperTest.kt @@ -17,8 +17,8 @@ package com.android.systemui.statusbar -import android.testing.AndroidTestingRunner import android.testing.TestableLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.ExpandHelper import com.android.systemui.SysuiTestCase @@ -39,7 +39,7 @@ import org.mockito.Mockito.`when` as whenever @SmallTest @TestableLooper.RunWithLooper -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class DragDownHelperTest : SysuiTestCase() { private lateinit var dragDownHelper: DragDownHelper diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java index 1504d4c1f033..995b5383c3c2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java @@ -65,9 +65,9 @@ import android.hardware.biometrics.BiometricFingerprintConstants; import android.hardware.biometrics.BiometricSourceType; import android.os.BatteryManager; import android.os.RemoteException; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; @@ -88,7 +88,7 @@ import java.util.List; import java.util.Set; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper public class KeyguardIndicationControllerTest extends KeyguardIndicationControllerBaseTest { @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerWithCoroutinesTest.kt index cdc752098aa7..4a14f8853904 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerWithCoroutinesTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerWithCoroutinesTest.kt @@ -16,9 +16,9 @@ package com.android.systemui.statusbar -import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.view.View +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED import kotlinx.coroutines.Dispatchers @@ -28,7 +28,7 @@ import org.junit.runner.RunWith import org.mockito.Mockito.times import org.mockito.Mockito.verify -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @SmallTest @TestableLooper.RunWithLooper(setAsMainLooper = true) class KeyguardIndicationControllerWithCoroutinesTest : KeyguardIndicationControllerBaseTest() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LSShadeTransitionLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LSShadeTransitionLoggerTest.kt index 8cb530c355bd..948a73208d10 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LSShadeTransitionLoggerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LSShadeTransitionLoggerTest.kt @@ -1,7 +1,7 @@ package com.android.systemui.statusbar -import android.testing.AndroidTestingRunner import android.util.DisplayMetrics +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.log.LogBuffer @@ -16,7 +16,7 @@ import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.junit.MockitoJUnit -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @SmallTest class LSShadeTransitionLoggerTest : SysuiTestCase() { lateinit var logger: LSShadeTransitionLogger @@ -41,4 +41,4 @@ class LSShadeTransitionLoggerTest : SysuiTestCase() { // log a non-null, non row, ensure no crash logger.logDragDownStarted(view) } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LightRevealScrimTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LightRevealScrimTest.kt index d3befb4ad4cd..fe2dd6d78a82 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LightRevealScrimTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LightRevealScrimTest.kt @@ -16,8 +16,8 @@ package com.android.systemui.statusbar -import android.testing.AndroidTestingRunner import android.view.View +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.google.common.truth.Truth.assertThat @@ -28,7 +28,7 @@ import org.junit.Test import org.junit.runner.RunWith import java.util.function.Consumer -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @SmallTest class LightRevealScrimTest : SysuiTestCase() { @@ -85,4 +85,4 @@ class LightRevealScrimTest : SysuiTestCase() { private const val DEFAULT_WIDTH = 42 private const val DEFAULT_HEIGHT = 24 } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionControllerTest.kt index 402d9aab66bd..e48242a3e003 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionControllerTest.kt @@ -16,7 +16,7 @@ package com.android.systemui.statusbar -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.dump.DumpManager @@ -36,7 +36,7 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class LockscreenShadeQsTransitionControllerTest : SysuiTestCase() { private val configurationController = FakeConfigurationController() 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 a92cf8c96339..69e8f4737a5a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt @@ -1,9 +1,9 @@ package com.android.systemui.statusbar import android.app.StatusBarManager.DISABLE2_NOTIFICATION_SHADE -import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.testing.TestableLooper.RunWithLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.ExpandHelper import com.android.systemui.SysUITestModule @@ -74,7 +74,7 @@ private fun <T> anyObject(): T { @SmallTest @RunWithLooper(setAsMainLooper = true) -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @OptIn(ExperimentalCoroutinesApi::class) class LockscreenShadeTransitionControllerTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java index d3febf55117b..ef1c927f22d7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java @@ -32,8 +32,8 @@ import android.os.UserHandle; import android.service.notification.NotificationListenerService.Ranking; import android.service.notification.NotificationListenerService.RankingMap; import android.service.notification.StatusBarNotification; -import android.testing.AndroidTestingRunner; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -52,7 +52,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class NotificationListenerTest extends SysuiTestCase { private static final String TEST_PACKAGE_NAME = "test"; private static final int TEST_UID = 0; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java index d3850be7c192..c9d910c530ea 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java @@ -27,9 +27,9 @@ import android.app.Notification; import android.content.Context; import android.os.SystemClock; import android.os.UserHandle; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -52,7 +52,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper public class NotificationRemoteInputManagerTest extends SysuiTestCase { private static final String TEST_PACKAGE_NAME = "test"; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt index fc0c85e30d5a..9f94cff4ead4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt @@ -17,11 +17,11 @@ package com.android.systemui.statusbar import android.os.IBinder -import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import android.view.Choreographer import android.view.View import android.view.ViewRootImpl +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.animation.ShadeInterpolation @@ -59,7 +59,7 @@ import org.mockito.Mockito.verify import org.mockito.Mockito.`when` import org.mockito.junit.MockitoJUnit -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @RunWithLooper @SmallTest class NotificationShadeDepthControllerTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt index 49e5c456e645..9907740672ed 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt @@ -17,8 +17,8 @@ package com.android.systemui.statusbar -import android.testing.AndroidTestingRunner import android.testing.TestableLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.dump.DumpManager @@ -43,7 +43,7 @@ import org.mockito.Mockito.`when` as whenever @SmallTest @TestableLooper.RunWithLooper -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class PulseExpansionHandlerTest : SysuiTestCase() { private lateinit var pulseExpansionHandler: PulseExpansionHandler diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/RemoteInputNotificationRebuilderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/RemoteInputNotificationRebuilderTest.java index ce11d6a62a8c..58943ea3b4ee 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/RemoteInputNotificationRebuilderTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/RemoteInputNotificationRebuilderTest.java @@ -27,9 +27,9 @@ import android.app.RemoteInputHistoryItem; import android.net.Uri; import android.os.UserHandle; import android.service.notification.StatusBarNotification; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -44,7 +44,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper public class RemoteInputNotificationRebuilderTest extends SysuiTestCase { private static final String TEST_PACKAGE_NAME = "test"; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScrollerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScrollerTest.kt index 2606be5fabad..6b9a19a92fc4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScrollerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScrollerTest.kt @@ -1,7 +1,7 @@ package com.android.systemui.statusbar import org.mockito.Mockito.`when` as whenever -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController @@ -14,7 +14,7 @@ import org.mockito.Mockito.intThat import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @SmallTest class SingleShadeLockScreenOverScrollerTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java index 775dc3c95746..3346e19b4ce9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java @@ -29,9 +29,9 @@ import android.os.Handler; import android.os.RemoteException; import android.os.UserHandle; import android.service.notification.StatusBarNotification; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; @@ -52,7 +52,7 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper @SmallTest public class SmartReplyControllerTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScrollerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScrollerTest.kt index 700fb1ec332c..58473c4e07a3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScrollerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScrollerTest.kt @@ -1,7 +1,7 @@ package com.android.systemui.statusbar -import android.testing.AndroidTestingRunner import android.testing.TestableLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.dump.DumpManager @@ -23,7 +23,7 @@ import org.mockito.Mockito.verifyZeroInteractions import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @SmallTest @TestableLooper.RunWithLooper class SplitShadeLockScreenOverScrollerTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateEventTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateEventTest.kt index 79a2008e7542..26692c908c5a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateEventTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateEventTest.kt @@ -16,7 +16,7 @@ package com.android.systemui.statusbar -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import org.junit.Assert.assertEquals @@ -24,7 +24,7 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class StatusBarStateEventTest : SysuiTestCase() { @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/VibratorHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/VibratorHelperTest.kt index b90582575970..78c1887f7c0d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/VibratorHelperTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/VibratorHelperTest.kt @@ -5,9 +5,9 @@ import android.os.UserHandle import android.os.VibrationAttributes import android.os.VibrationEffect import android.os.Vibrator -import android.testing.AndroidTestingRunner import android.view.HapticFeedbackConstants import android.view.View +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.util.mockito.eq @@ -26,7 +26,7 @@ import org.mockito.Mockito.verify import org.mockito.junit.MockitoJUnit import java.util.concurrent.Executor -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @SmallTest class VibratorHelperTest : SysuiTestCase() { @@ -120,4 +120,4 @@ class VibratorHelperTest : SysuiTestCase() { return verify(vibrator) } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallBackgroundContainerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/view/ChipBackgroundContainerTest.kt index f0a457e2c19f..7d2b463afab7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallBackgroundContainerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/view/ChipBackgroundContainerTest.kt @@ -14,15 +14,15 @@ * limitations under the License. */ -package com.android.systemui.statusbar.phone.ongoingcall +package com.android.systemui.statusbar.chips.ui.view import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.view.LayoutInflater import android.view.View import androidx.test.filters.SmallTest -import com.android.systemui.res.R import com.android.systemui.SysuiTestCase +import com.android.systemui.res.R import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test @@ -31,16 +31,17 @@ import org.junit.runner.RunWith @SmallTest @RunWith(AndroidTestingRunner::class) @TestableLooper.RunWithLooper -class OngoingCallBackgroundContainerTest : SysuiTestCase() { +class ChipBackgroundContainerTest : SysuiTestCase() { - private lateinit var underTest: OngoingCallBackgroundContainer + private lateinit var underTest: ChipBackgroundContainer @Before fun setUp() { allowTestableLooperAsMainThread() TestableLooper.get(this).runWithLooper { - val chipView = LayoutInflater.from(context).inflate(R.layout.ongoing_call_chip, null) - underTest = chipView.requireViewById(R.id.ongoing_call_chip_background) + val chipView = + LayoutInflater.from(context).inflate(R.layout.ongoing_activity_chip, null) + underTest = chipView.requireViewById(R.id.ongoing_activity_chip_background) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/view/ChipChronometerTest.kt index 7e25aa373097..b8d4e47544dd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/view/ChipChronometerTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,15 +14,15 @@ * limitations under the License. */ -package com.android.systemui.statusbar.phone.ongoingcall +package com.android.systemui.statusbar.chips.ui.view import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.view.LayoutInflater import android.view.View import androidx.test.filters.SmallTest -import com.android.systemui.res.R import com.android.systemui.SysuiTestCase +import com.android.systemui.res.R import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test @@ -38,17 +38,18 @@ private const val XL_TEXT = "00:0000" @SmallTest @RunWith(AndroidTestingRunner::class) @TestableLooper.RunWithLooper -class OngoingCallChronometerTest : SysuiTestCase() { +class ChipChronometerTest : SysuiTestCase() { - private lateinit var textView: OngoingCallChronometer + private lateinit var textView: ChipChronometer private lateinit var doesNotFitText: String @Before fun setUp() { allowTestableLooperAsMainThread() TestableLooper.get(this).runWithLooper { - val chipView = LayoutInflater.from(mContext).inflate(R.layout.ongoing_call_chip, null) - textView = chipView.findViewById(R.id.ongoing_call_chip_time)!! + val chipView = + LayoutInflater.from(mContext).inflate(R.layout.ongoing_activity_chip, null) + textView = chipView.findViewById(R.id.ongoing_activity_chip_time)!! measureTextView() calculateDoesNotFixText() } @@ -159,8 +160,8 @@ class OngoingCallChronometerTest : SysuiTestCase() { private fun measureTextView() { textView.measure( - View.MeasureSpec.makeMeasureSpec(TEXT_VIEW_MAX_WIDTH, View.MeasureSpec.AT_MOST), - View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) + View.MeasureSpec.makeMeasureSpec(TEXT_VIEW_MAX_WIDTH, View.MeasureSpec.AT_MOST), + View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) ) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt index 7e88ae080178..643acdbb9277 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt @@ -17,8 +17,8 @@ package com.android.systemui.statusbar.connectivity import android.os.UserManager +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import androidx.lifecycle.Lifecycle import com.android.systemui.SysuiTestCase @@ -42,7 +42,7 @@ import org.mockito.MockitoAnnotations import java.util.concurrent.Executor @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @RunWithLooper(setAsMainLooper = true) class AccessPointControllerImplTest : SysuiTestCase() { @@ -244,4 +244,4 @@ class AccessPointControllerImplTest : SysuiTestCase() { verify(wifiEntryOther).connect(any()) verify(callback, never()).onSettingsActivityTriggered(any()) } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/MobileStateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/MobileStateTest.kt index 7aed4f7e250b..40f81e2bae5d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/MobileStateTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/MobileStateTest.kt @@ -15,7 +15,7 @@ */ package com.android.systemui.statusbar.connectivity -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.settingslib.mobile.TelephonyIcons import com.android.systemui.SysuiTestCase @@ -26,7 +26,7 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class MobileStateTest : SysuiTestCase() { private val state = MobileState() diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java index 461d80412cb5..4241254e1fb9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java @@ -39,10 +39,10 @@ import android.os.Looper; import android.telephony.NetworkRegistrationInfo; import android.telephony.ServiceState; import android.telephony.TelephonyManager; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.settingslib.SignalIcon.MobileIconGroup; @@ -60,7 +60,7 @@ import org.junit.runner.RunWith; import java.util.HashMap; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @RunWithLooper public class NetworkControllerDataTest extends NetworkControllerBaseTest { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerEthernetTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerEthernetTest.java index 3bbf06dbc69c..521cb4f4c42d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerEthernetTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerEthernetTest.java @@ -19,9 +19,9 @@ package com.android.systemui.statusbar.connectivity; import static junit.framework.Assert.assertEquals; import android.net.NetworkCapabilities; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper.RunWithLooper; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import org.junit.Test; @@ -30,7 +30,7 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mockito; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @RunWithLooper public class NetworkControllerEthernetTest extends NetworkControllerBaseTest { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java index 35609a5faf00..22f0e9d20c41 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java @@ -33,10 +33,10 @@ import android.telephony.SignalStrength; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.settingslib.graph.SignalDrawable; @@ -59,7 +59,7 @@ import java.util.Collections; import java.util.List; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @RunWithLooper public class NetworkControllerSignalTest extends NetworkControllerBaseTest { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java index 44a1c50e5a58..6c80a97625a7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java @@ -34,9 +34,9 @@ import android.net.NetworkInfo; import android.net.vcn.VcnTransportInfo; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper.RunWithLooper; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.settingslib.mobile.TelephonyIcons; @@ -50,7 +50,7 @@ import org.mockito.Mockito; import java.util.Collections; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @RunWithLooper public class NetworkControllerWifiTest extends NetworkControllerBaseTest { // These match the constants in WifiManager and need to be kept up to date. diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkTypeResIdCacheTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkTypeResIdCacheTest.kt index 5bf0a94935cf..3eeed73a539c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkTypeResIdCacheTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkTypeResIdCacheTest.kt @@ -16,7 +16,7 @@ package com.android.systemui.statusbar.connectivity -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.settingslib.SignalIcon.MobileIconGroup import com.android.systemui.SysuiTestCase @@ -26,7 +26,7 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class NetworkTypeResIdCacheTest : SysuiTestCase() { private lateinit var cache: NetworkTypeResIdCache private var overrides = MobileIconCarrierIdOverridesFake() diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt index 452302d4db8a..984bda1c0d21 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt @@ -19,11 +19,11 @@ package com.android.systemui.statusbar.events import android.content.Context import android.graphics.Insets import android.graphics.Rect -import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.view.Gravity import android.view.View import android.widget.FrameLayout +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.animation.AnimatorTestRule @@ -46,7 +46,7 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class SystemEventChipAnimationControllerTest : SysuiTestCase() { private lateinit var controller: SystemEventChipAnimationController diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventCoordinatorTest.kt index ae84df55e113..742494b769de 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventCoordinatorTest.kt @@ -15,8 +15,8 @@ */ package com.android.systemui.statusbar.events -import android.testing.AndroidTestingRunner import android.testing.TestableLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor @@ -40,7 +40,7 @@ import org.mockito.Mockito.verify import org.mockito.Mockito.verifyNoMoreInteractions import org.mockito.MockitoAnnotations -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper(setAsMainLooper = true) @SmallTest @OptIn(ExperimentalCoroutinesApi::class) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt index cacfa8dc0be4..376873d19624 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt @@ -19,10 +19,10 @@ package com.android.systemui.statusbar.events import android.graphics.Insets import android.graphics.Rect import android.os.Process -import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import android.view.View import android.widget.FrameLayout +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.animation.AnimatorTestRule @@ -54,7 +54,7 @@ import org.mockito.Mockito.times import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @RunWithLooper(setAsMainLooper = true) @OptIn(ExperimentalCoroutinesApi::class) @SmallTest diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/gesture/GenericGestureDetectorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/gesture/GenericGestureDetectorTest.kt index d3f5adeb05bb..0f58990d4310 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/gesture/GenericGestureDetectorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/gesture/GenericGestureDetectorTest.kt @@ -1,9 +1,9 @@ package com.android.systemui.statusbar.gesture -import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.view.InputEvent import android.view.MotionEvent +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.settings.FakeDisplayTracker @@ -13,7 +13,7 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class GenericGestureDetectorTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AboveShelfObserverTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AboveShelfObserverTest.java index 6b2ee76a75ac..01a0fd020bda 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AboveShelfObserverTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AboveShelfObserverTest.java @@ -19,11 +19,11 @@ package com.android.systemui.statusbar.notification; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; import android.widget.FrameLayout; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -36,7 +36,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @RunWithLooper public class AboveShelfObserverTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AssistantFeedbackControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AssistantFeedbackControllerTest.java index fc4702c209e1..d66b010daefd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AssistantFeedbackControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AssistantFeedbackControllerTest.java @@ -42,9 +42,9 @@ import android.os.Handler; import android.os.UserHandle; import android.provider.DeviceConfig; import android.service.notification.StatusBarNotification; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; @@ -58,7 +58,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper public class AssistantFeedbackControllerTest extends SysuiTestCase { private static final String TEST_PACKAGE_NAME = "test_package"; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicChildBindControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicChildBindControllerTest.java index 0103564088e0..77fd06757595 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicChildBindControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicChildBindControllerTest.java @@ -25,12 +25,12 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.util.ArrayMap; import android.view.LayoutInflater; import android.view.View; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -56,7 +56,7 @@ import java.util.List; import java.util.Map; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper public class DynamicChildBindControllerTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicPrivacyControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicPrivacyControllerTest.java index 5b72ca07edbe..d879fcecffab 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicPrivacyControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicPrivacyControllerTest.java @@ -25,9 +25,9 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -38,9 +38,10 @@ import com.android.systemui.statusbar.policy.KeyguardStateController; import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; @SmallTest -@org.junit.runner.RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper public class DynamicPrivacyControllerTest extends SysuiTestCase { @@ -127,4 +128,4 @@ public class DynamicPrivacyControllerTest extends SysuiTestCase { mDynamicPrivacyController.onUnlockedChanged(); verifyNoMoreInteractions(mListener); } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt index 1cce3b5b9e77..8e8a3513f3be 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt @@ -18,8 +18,8 @@ package com.android.systemui.statusbar.notification import android.provider.DeviceConfig import android.provider.Settings -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.dx.mockito.inline.extended.ExtendedMockito @@ -39,7 +39,7 @@ import org.mockito.Mockito.`when` import org.mockito.MockitoSession import org.mockito.quality.Strictness -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @SmallTest class NotificationSectionsFeatureManagerTest : SysuiTestCase() { var manager: NotificationSectionsFeatureManager? = null diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorControllerTest.kt index 3b3f05d25c16..3abdf6212f22 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorControllerTest.kt @@ -1,9 +1,9 @@ package com.android.systemui.statusbar.notification import android.app.Notification.GROUP_ALERT_SUMMARY -import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.testing.TestableLooper.RunWithLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue @@ -35,7 +35,7 @@ import org.mockito.Mockito.`when` import org.mockito.junit.MockitoJUnit @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @RunWithLooper class NotificationTransitionAnimatorControllerTest : SysuiTestCase() { @Mock lateinit var notificationListContainer: NotificationListContainer diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLoggerTest.kt index 1aac515f538b..a5206f52b6f2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLoggerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLoggerTest.kt @@ -16,7 +16,7 @@ package com.android.systemui.statusbar.notification -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.log.LogBuffer @@ -28,7 +28,7 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @SmallTest class NotificationWakeUpCoordinatorLoggerTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt index 67b540cd762e..0906d8eadf44 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt @@ -16,8 +16,8 @@ package com.android.systemui.statusbar.notification -import android.testing.AndroidTestingRunner import android.testing.TestableLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.compose.animation.scene.ObservableTransitionState import com.android.systemui.SysuiTestCase @@ -60,7 +60,7 @@ import org.mockito.Mockito.verify import org.mockito.Mockito.verifyNoMoreInteractions @OptIn(ExperimentalCoroutinesApi::class) -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @SmallTest @TestableLooper.RunWithLooper(setAsMainLooper = true) class NotificationWakeUpCoordinatorTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/RoundableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/RoundableTest.kt index 7d8cf3657ba1..382b307fb9de 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/RoundableTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/RoundableTest.kt @@ -2,6 +2,7 @@ package com.android.systemui.statusbar.notification import android.platform.test.annotations.EnableFlags import android.view.View +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.notification.shared.NotificationsImprovedHunAnimation @@ -10,13 +11,12 @@ import com.android.systemui.util.mockito.whenever import org.junit.Assert.assertEquals import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.mockito.Mockito.atLeastOnce import org.mockito.Mockito.times import org.mockito.Mockito.verify @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class RoundableTest : SysuiTestCase() { private val targetView: View = mock() private val roundable = FakeRoundable(targetView = targetView) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java index 2d044fec1eb6..8e95ac599ce1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java @@ -30,8 +30,8 @@ import static org.mockito.Mockito.when; import android.app.Notification; import android.app.NotificationChannel; -import android.testing.AndroidTestingRunner; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -51,7 +51,7 @@ import java.util.Arrays; import java.util.List; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class HighPriorityProviderTest extends SysuiTestCase { @Mock private PeopleNotificationIdentifier mPeopleNotificationIdentifier; @Mock private GroupMembershipManager mGroupMembershipManager; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataImplTest.kt index 892575ab6c71..2a587511eaec 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataImplTest.kt @@ -16,9 +16,9 @@ package com.android.systemui.statusbar.notification.collection -import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import androidx.lifecycle.Observer +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.util.concurrency.FakeExecutor @@ -34,7 +34,7 @@ import org.mockito.Mockito.verify import org.mockito.Mockito.verifyNoMoreInteractions @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @RunWithLooper class NotifLiveDataImplTest : SysuiTestCase() { @@ -164,4 +164,4 @@ class NotifLiveDataImplTest : SysuiTestCase() { assertThat(executor.runAllReady()).isEqualTo(2) verifyNoMoreInteractions(syncObserver, asyncObserver) } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataStoreImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataStoreImplTest.kt index 9c8ac5cded9e..d87f827de776 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataStoreImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataStoreImplTest.kt @@ -16,8 +16,8 @@ package com.android.systemui.statusbar.notification.collection -import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.util.concurrency.FakeExecutor @@ -30,7 +30,7 @@ import org.junit.runner.RunWith import java.lang.UnsupportedOperationException @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @RunWithLooper class NotifLiveDataStoreImplTest : SysuiTestCase() { @@ -102,4 +102,4 @@ class NotifLiveDataStoreImplTest : SysuiTestCase() { liveDataStoreImpl.setActiveNotifList(mutableListOf(entry1, entry2)) executor.runAllReady() } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifPipelineChoreographerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifPipelineChoreographerTest.kt index 3b908b4175f9..f1da22f08e75 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifPipelineChoreographerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifPipelineChoreographerTest.kt @@ -16,8 +16,8 @@ package com.android.systemui.statusbar.notification.collection -import android.testing.AndroidTestingRunner import android.view.Choreographer +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.dagger.SysUISingleton @@ -36,7 +36,7 @@ import org.mockito.Mockito.verify import org.mockito.Mockito.`when` as whenever @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class NotifPipelineChoreographerTest : SysuiTestCase() { val viewChoreographer: Choreographer = mock() @@ -118,4 +118,4 @@ interface NotifPipelineChoreographerTestComponent { @BindsInstance @Main executor: DelayableExecutor ): NotifPipelineChoreographerTestComponent } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java index 8a48fe10d7fc..72d1db3affe8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java @@ -46,8 +46,8 @@ import android.os.UserHandle; import android.service.notification.NotificationListenerService.Ranking; import android.service.notification.SnoozeCriterion; import android.service.notification.StatusBarNotification; -import android.testing.AndroidTestingRunner; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -64,7 +64,7 @@ import org.mockito.Mockito; import java.util.ArrayList; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class NotificationEntryTest extends SysuiTestCase { private static final String TEST_PACKAGE_NAME = "test"; private static final int TEST_UID = 0; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/SectionStyleProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/SectionStyleProviderTest.kt index ab55a7d650c6..1fd6b042ad67 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/SectionStyleProviderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/SectionStyleProviderTest.kt @@ -20,7 +20,7 @@ import android.os.UserHandle import android.platform.test.annotations.EnableFlags import android.platform.test.flag.junit.SetFlagsRule import android.service.notification.StatusBarNotification -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection @@ -47,7 +47,7 @@ import org.mockito.MockitoAnnotations import org.mockito.Mockito.`when` as whenever @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class SectionStyleProviderTest : SysuiTestCase() { @Rule @JvmField public val setFlagsRule = SetFlagsRule() @@ -118,4 +118,4 @@ class SectionStyleProviderTest : SysuiTestCase() { override fun getSection(): NotifSection? = NotifSection(inputSectioner, 1) } } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolverTest.kt index 4708350c1c0a..2ad3c9e1c21a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolverTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolverTest.kt @@ -25,7 +25,7 @@ import android.os.Bundle import android.os.UserHandle import android.service.notification.NotificationListenerService.Ranking import android.service.notification.StatusBarNotification -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection @@ -48,7 +48,7 @@ private const val PACKAGE = "pkg" private const val USER_ID = -1 @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class TargetSdkResolverTest : SysuiTestCase() { private val packageManager: PackageManager = mock() private val applicationInfo = ApplicationInfo().apply { targetSdkVersion = SDK_VERSION } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerTest.java index b1180aebe9bd..f029a2ca6099 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerTest.java @@ -30,8 +30,8 @@ import static java.util.Objects.requireNonNull; import android.service.notification.NotificationListenerService.Ranking; import android.service.notification.NotificationListenerService.RankingMap; import android.service.notification.StatusBarNotification; -import android.testing.AndroidTestingRunner; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -57,7 +57,7 @@ import java.util.Arrays; import java.util.Collections; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class GroupCoalescerTest extends SysuiTestCase { private GroupCoalescer mCoalescer; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinatorTest.java index f2207afeda10..1f2925528077 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinatorTest.java @@ -30,9 +30,9 @@ import android.app.Person; import android.content.Intent; import android.graphics.Color; import android.os.UserHandle; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -47,7 +47,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper public class ColorizedFgsCoordinatorTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DataStoreCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DataStoreCoordinatorTest.kt index 59fc591e4d06..e72109d4d8e3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DataStoreCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DataStoreCoordinatorTest.kt @@ -15,8 +15,8 @@ */ package com.android.systemui.statusbar.notification.collection.coordinator -import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder @@ -39,7 +39,7 @@ import org.mockito.Mockito.verifyNoMoreInteractions import org.mockito.MockitoAnnotations.initMocks @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @RunWithLooper class DataStoreCoordinatorTest : SysuiTestCase() { private lateinit var coordinator: DataStoreCoordinator diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DismissibilityCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DismissibilityCoordinatorTest.kt index f91e5a8cf626..543f0c72a8ec 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DismissibilityCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DismissibilityCoordinatorTest.kt @@ -17,7 +17,7 @@ package com.android.systemui.statusbar.notification.collection.coordinator import android.app.Notification -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.dump.DumpManager @@ -38,7 +38,7 @@ import org.mockito.Mockito import org.mockito.Mockito.`when` as whenever @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class DismissibilityCoordinatorTest : SysuiTestCase() { private lateinit var coordinator: DismissibilityCoordinator diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DreamCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DreamCoordinatorTest.kt index a544cad03b77..4d5ea92aa0e6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DreamCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DreamCoordinatorTest.kt @@ -17,7 +17,7 @@ package com.android.systemui.statusbar.notification.collection.coordinator -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository @@ -49,7 +49,7 @@ import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class DreamCoordinatorTest : SysuiTestCase() { @Mock private lateinit var statusBarStateController: SysuiStatusBarStateController @Mock private lateinit var notifPipeline: NotifPipeline diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupCountCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupCountCoordinatorTest.kt index 929c3d4288d4..7b688d4f91fd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupCountCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupCountCoordinatorTest.kt @@ -15,8 +15,8 @@ */ package com.android.systemui.statusbar.notification.collection.coordinator -import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder @@ -36,7 +36,7 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations.initMocks @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @RunWithLooper class GroupCountCoordinatorTest : SysuiTestCase() { private lateinit var coordinator: GroupCountCoordinator diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupWhenCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupWhenCoordinatorTest.kt index eac0e296c51f..3f140265e5f1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupWhenCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupWhenCoordinatorTest.kt @@ -16,8 +16,8 @@ package com.android.systemui.statusbar.notification.collection.coordinator import android.app.Notification -import android.testing.AndroidTestingRunner import android.testing.TestableLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.SbnBuilder @@ -45,7 +45,7 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations.initMocks @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class GroupWhenCoordinatorTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorTest.kt index a652ad64ea8d..7fe97d27f273 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorTest.kt @@ -15,8 +15,8 @@ */ package com.android.systemui.statusbar.notification.collection.coordinator -import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.dump.DumpManager @@ -42,7 +42,7 @@ import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations.initMocks @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @RunWithLooper class GutsCoordinatorTest : SysuiTestCase() { private lateinit var coordinator: GutsCoordinator diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt index cd75e0811fff..8e9323fead92 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt @@ -17,8 +17,8 @@ package com.android.systemui.statusbar.notification.collection.coordinator import android.app.Notification.GROUP_ALERT_ALL import android.app.Notification.GROUP_ALERT_SUMMARY -import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.log.logcatLogBuffer @@ -70,7 +70,7 @@ import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @RunWithLooper class HeadsUpCoordinatorTest : SysuiTestCase() { private lateinit var coordinator: HeadsUpCoordinator diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HideNotifsForOtherUsersCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HideNotifsForOtherUsersCoordinatorTest.java index 27542a462d36..5dcad4bb9e62 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HideNotifsForOtherUsersCoordinatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HideNotifsForOtherUsersCoordinatorTest.java @@ -24,9 +24,9 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.testing.AndroidTestingRunner; import android.util.SparseArray; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -47,7 +47,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class HideNotifsForOtherUsersCoordinatorTest extends SysuiTestCase { @Mock private NotificationLockscreenUserManager mLockscreenUserManager; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt index 5ff73538b359..25533d82608b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt @@ -20,7 +20,7 @@ package com.android.systemui.statusbar.notification.collection.coordinator import android.app.Notification import android.os.UserHandle import android.provider.Settings -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.dump.DumpManager @@ -67,7 +67,7 @@ import kotlin.time.Duration.Companion.seconds import org.mockito.Mockito.`when` as whenever @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class KeyguardCoordinatorTest : SysuiTestCase() { private val headsUpManager: HeadsUpManager = mock() diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinatorTest.java index e90a3ac8bfdc..07c29a024a6c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinatorTest.java @@ -33,8 +33,8 @@ import android.media.session.MediaSession; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; import android.service.notification.NotificationListenerService; -import android.testing.AndroidTestingRunner; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.statusbar.IStatusBarService; @@ -58,7 +58,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public final class MediaCoordinatorTest extends SysuiTestCase { private MediaSession mMediaSession; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/NotificationStatsLoggerCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/NotificationStatsLoggerCoordinatorTest.kt index c29ff416feb9..501bca248c76 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/NotificationStatsLoggerCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/NotificationStatsLoggerCoordinatorTest.kt @@ -18,7 +18,7 @@ package com.android.systemui.statusbar.notification.collection.coordinator import android.platform.test.annotations.EnableFlags import android.service.notification.NotificationListenerService.REASON_CANCEL -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.notification.collection.NotifPipeline @@ -36,7 +36,7 @@ import org.junit.runner.RunWith import org.mockito.Mockito.verify @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @EnableFlags(NotificationsLiveDataStoreRefactor.FLAG_NAME) class NotificationStatsLoggerCoordinatorTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java index cceaaea672c4..80127682be02 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java @@ -39,10 +39,10 @@ import static java.util.Objects.requireNonNull; import android.database.ContentObserver; import android.os.Handler; import android.os.RemoteException; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import androidx.annotation.NonNull; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.statusbar.IStatusBarService; @@ -88,7 +88,7 @@ import java.util.List; import java.util.Map; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper public class PreparationCoordinatorTest extends SysuiTestCase { private NotifCollectionListener mCollectionListener; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java index 3d1253e2b05d..c05b13163d32 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java @@ -35,9 +35,9 @@ import static org.mockito.Mockito.when; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; -import android.testing.AndroidTestingRunner; import androidx.annotation.Nullable; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -67,7 +67,7 @@ import java.util.ArrayList; import java.util.Arrays; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class RankingCoordinatorTest extends SysuiTestCase { @Mock private StatusBarStateController mStatusBarStateController; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RemoteInputCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RemoteInputCoordinatorTest.kt index d3df48e9ef02..deb3fc1224ce 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RemoteInputCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RemoteInputCoordinatorTest.kt @@ -23,8 +23,8 @@ import android.os.Handler import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.service.notification.StatusBarNotification -import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.dump.DumpManager @@ -54,7 +54,7 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations.initMocks @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @RunWithLooper class RemoteInputCoordinatorTest : SysuiTestCase() { private lateinit var coordinator: RemoteInputCoordinator diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAlertTimeCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAlertTimeCoordinatorTest.kt index 7daadb07f89a..1b7ec5381713 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAlertTimeCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAlertTimeCoordinatorTest.kt @@ -15,8 +15,8 @@ */ package com.android.systemui.statusbar.notification.collection.coordinator -import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder @@ -37,7 +37,7 @@ import org.mockito.Mockito.verifyNoMoreInteractions import org.mockito.MockitoAnnotations.initMocks @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @RunWithLooper class RowAlertTimeCoordinatorTest : SysuiTestCase() { private lateinit var coordinator: RowAlertTimeCoordinator diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinatorTest.kt index a66f8ce1a92c..5b231e21e700 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinatorTest.kt @@ -15,8 +15,8 @@ */ package com.android.systemui.statusbar.notification.collection.coordinator -import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.notification.AssistantFeedbackController @@ -41,7 +41,7 @@ import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations.initMocks @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @RunWithLooper class RowAppearanceCoordinatorTest : SysuiTestCase() { private lateinit var coordinator: RowAppearanceCoordinator diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorTest.kt index 56f16f32ec15..ccf7cdd70675 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorTest.kt @@ -17,8 +17,8 @@ package com.android.systemui.statusbar.notification.collection.coordinator import android.service.notification.NotificationListenerService.REASON_APP_CANCEL import android.service.notification.NotificationListenerService.REASON_CANCEL -import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.log.logcatLogBuffer @@ -40,7 +40,7 @@ import java.util.concurrent.Executor import org.mockito.Mockito.`when` as whenever @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @RunWithLooper class ShadeEventCoordinatorTest : SysuiTestCase() { private lateinit var coordinator: ShadeEventCoordinator diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt index ea4f692ef4f1..c7513de7a41a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt @@ -17,8 +17,8 @@ package com.android.systemui.statusbar.notification.collection.coordinator import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags -import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.server.notification.Flags.FLAG_SCREENSHARE_NOTIFICATION_HIDING import com.android.systemui.Flags.FLAG_SCREENSHARE_NOTIFICATION_HIDING_BUG_FIX @@ -51,7 +51,7 @@ import org.mockito.MockitoAnnotations.initMocks import org.mockito.Mockito.`when` as whenever @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @RunWithLooper class StackCoordinatorTest : SysuiTestCase() { private lateinit var coordinator: StackCoordinator diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt index b1d2ea21f7fc..c8fbe61fa799 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt @@ -15,8 +15,8 @@ */ package com.android.systemui.statusbar.notification.collection.coordinator -import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.KeyguardUpdateMonitorCallback @@ -40,7 +40,7 @@ import org.mockito.Mockito.verifyNoMoreInteractions import org.mockito.Mockito.`when` as whenever @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @RunWithLooper class ViewConfigCoordinatorTest : SysuiTestCase() { private lateinit var coordinator: ViewConfigCoordinator diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt index 8e6ceccbc14e..7943872558c3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt @@ -20,8 +20,8 @@ import android.os.Handler import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.provider.Settings.Secure.SHOW_NOTIFICATION_SNOOZE -import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.server.notification.Flags.FLAG_SCREENSHARE_NOTIFICATION_HIDING import com.android.systemui.SysuiTestCase @@ -54,7 +54,7 @@ import org.mockito.Mockito.verifyNoMoreInteractions import org.mockito.Mockito.`when` as whenever @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @RunWithLooper class NotifUiAdjustmentProviderTest : SysuiTestCase() { private val lockscreenUserManager: NotificationLockscreenUserManager = mock() diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/listbuilder/SemiStableSortTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/listbuilder/SemiStableSortTest.kt index 1cdd023dd01c..d2057703d560 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/listbuilder/SemiStableSortTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/listbuilder/SemiStableSortTest.kt @@ -15,9 +15,9 @@ */ package com.android.systemui.statusbar.notification.collection.listbuilder -import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import android.util.Log +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import org.junit.Assert.assertFalse @@ -27,7 +27,7 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @RunWithLooper class SemiStableSortTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderHelperTest.kt index 20369546d68a..49f836fe9f81 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderHelperTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderHelperTest.kt @@ -15,8 +15,8 @@ */ package com.android.systemui.statusbar.notification.collection.listbuilder -import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.notification.collection.listbuilder.ShadeListBuilderHelper.getContiguousSubLists @@ -25,7 +25,7 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @RunWithLooper class ShadeListBuilderHelperTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionInconsistencyTrackerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionInconsistencyTrackerTest.kt index 22f6bdc54b85..341a51e32a46 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionInconsistencyTrackerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionInconsistencyTrackerTest.kt @@ -17,8 +17,8 @@ package com.android.systemui.statusbar.notification.collection.notifcollection import android.service.notification.NotificationListenerService.RankingMap -import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.log.logcatLogBuffer @@ -34,7 +34,7 @@ import org.mockito.Mockito.verify import org.mockito.Mockito.verifyNoMoreInteractions @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @RunWithLooper class NotifCollectionInconsistencyTrackerTest : SysuiTestCase() { private val logger = spy(NotifCollectionLogger(logcatLogBuffer())) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/notifcollection/SelfTrackingLifetimeExtenderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/notifcollection/SelfTrackingLifetimeExtenderTest.kt index a09f3a35c308..99e55a85344a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/notifcollection/SelfTrackingLifetimeExtenderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/notifcollection/SelfTrackingLifetimeExtenderTest.kt @@ -16,8 +16,8 @@ package com.android.systemui.statusbar.notification.collection.notifcollection import android.os.Handler -import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.notification.collection.NotificationEntry @@ -41,7 +41,7 @@ import java.util.function.Consumer import java.util.function.Predicate @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @RunWithLooper class SelfTrackingLifetimeExtenderTest : SysuiTestCase() { private lateinit var extender: TestableSelfTrackingLifetimeExtender diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/provider/VisualStabilityProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/provider/VisualStabilityProviderTest.kt index b56f8e9364c4..586b947d5299 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/provider/VisualStabilityProviderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/provider/VisualStabilityProviderTest.kt @@ -15,7 +15,7 @@ */ package com.android.systemui.statusbar.notification.collection.provider -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.util.mockito.mock @@ -29,7 +29,7 @@ import org.mockito.Mockito.verify import org.mockito.Mockito.verifyNoMoreInteractions @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class VisualStabilityProviderTest : SysuiTestCase() { private val visualStabilityProvider = VisualStabilityProvider() private val listener: OnReorderingAllowedListener = mock() @@ -148,4 +148,4 @@ class VisualStabilityProviderTest : SysuiTestCase() { visualStabilityProvider.isReorderingAllowed = true verify(selfAddingListener, times(2)).onReorderingAllowed() } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferTest.kt index eeabc744987b..9d3e2e8dfcb4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferTest.kt @@ -16,10 +16,10 @@ package com.android.systemui.statusbar.notification.collection.render import android.content.Context -import android.testing.AndroidTestingRunner import android.view.View import android.view.ViewGroup import android.widget.FrameLayout +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.log.logcatLogBuffer @@ -34,7 +34,7 @@ import org.mockito.Mockito.spy import org.mockito.Mockito.verify @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class ShadeViewDifferTest : SysuiTestCase() { private lateinit var differ: ShadeViewDiffer private val rootController = FakeController(mContext, "RootController") diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractorTest.kt index 2a3c1a53559e..39085295fbac 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractorTest.kt @@ -15,7 +15,7 @@ package com.android.systemui.statusbar.notification.domain.interactor -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue @@ -25,7 +25,7 @@ import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @SmallTest class SeenNotificationsInteractorTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/IconManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/IconManagerTest.kt index 4ac9dc2be161..bfa816e65eb2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/IconManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/IconManagerTest.kt @@ -30,8 +30,8 @@ import android.graphics.drawable.Icon import android.os.Bundle import android.os.SystemClock import android.os.UserHandle -import android.testing.AndroidTestingRunner import androidx.test.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.controls.controller.AuxiliaryPersistenceWrapperTest.Companion.any @@ -53,7 +53,7 @@ import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class IconManagerTest : SysuiTestCase() { companion object { private const val TEST_PACKAGE_NAME = "test" diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt index b410b33b97d9..c9f2addfd39b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt @@ -15,7 +15,7 @@ package com.android.systemui.statusbar.notification.icon.domain.interactor -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysUITestComponent import com.android.systemui.SysUITestModule @@ -52,7 +52,7 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class NotificationIconsInteractorTest : SysuiTestCase() { private val bubbles: Bubbles = mock() @@ -151,7 +151,7 @@ class NotificationIconsInteractorTest : SysuiTestCase() { } @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class AlwaysOnDisplayNotificationIconsInteractorTest : SysuiTestCase() { private val bubbles: Bubbles = mock() @@ -256,7 +256,7 @@ class AlwaysOnDisplayNotificationIconsInteractorTest : SysuiTestCase() { } @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class StatusBarNotificationIconsInteractorTest : SysuiTestCase() { private val bubbles: Bubbles = mock() diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderTest.java index 60eea9beb2e0..af2789b8401a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderTest.java @@ -26,9 +26,9 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; -import android.testing.AndroidTestingRunner; import androidx.core.os.CancellationSignal; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.util.NotificationMessagingUtil; @@ -47,7 +47,7 @@ import org.mockito.MockitoAnnotations; import java.util.concurrent.atomic.AtomicReference; -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @SmallTest public class HeadsUpViewBinderTest extends SysuiTestCase { private HeadsUpViewBinder mViewBinder; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java index 86620480fa7b..19214fb831eb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java @@ -42,9 +42,9 @@ import android.content.Context; import android.os.Handler; import android.os.UserHandle; import android.provider.Settings; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.keyguard.KeyguardUpdateMonitor; @@ -86,7 +86,7 @@ import java.util.Map; import java.util.function.Consumer; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase { private static final int NOTIF_USER_ID = 0; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java index 7ade053720e9..3e8461a225b2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java @@ -60,8 +60,8 @@ import android.os.Handler; import android.os.PowerManager; import android.os.RemoteException; import android.platform.test.annotations.DisableFlags; -import android.testing.AndroidTestingRunner; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.logging.testing.UiEventLoggerFake; @@ -96,7 +96,7 @@ import java.util.Set; * Tests for the interruption state provider which understands whether the system & notification * is in a state allowing a particular notification to hun, pulse, or bubble. */ -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @SmallTest public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt index 7ed33126a54f..a6177e8feb1b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt @@ -17,7 +17,7 @@ package com.android.systemui.statusbar.notification.interruption import android.platform.test.annotations.DisableFlags -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider.FullScreenIntentDecision @@ -34,7 +34,7 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @DisableFlags(VisualInterruptionRefactor.FLAG_NAME) class NotificationInterruptStateProviderWrapperTest : VisualInterruptionDecisionProviderTestBase() { override val provider by lazy { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt index edab9d9f7fdf..eeb51a684d42 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt @@ -16,11 +16,13 @@ package com.android.systemui.statusbar.notification.interruption +import android.Manifest.permission import android.app.Notification.CATEGORY_EVENT import android.app.Notification.CATEGORY_REMINDER import android.app.NotificationManager +import android.content.pm.PackageManager.PERMISSION_GRANTED import android.platform.test.annotations.EnableFlags -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.BUBBLE @@ -28,9 +30,11 @@ import com.android.systemui.statusbar.notification.interruption.VisualInterrupti import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PULSE import org.junit.Test import org.junit.runner.RunWith +import org.mockito.Mockito.anyString +import org.mockito.Mockito.`when` @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @EnableFlags(VisualInterruptionRefactor.FLAG_NAME) class VisualInterruptionDecisionProviderImplTest : VisualInterruptionDecisionProviderTestBase() { override val provider by lazy { @@ -51,7 +55,8 @@ class VisualInterruptionDecisionProviderImplTest : VisualInterruptionDecisionPro uiEventLogger, userTracker, avalancheProvider, - systemSettings + systemSettings, + packageManager ) } @@ -83,14 +88,18 @@ class VisualInterruptionDecisionProviderImplTest : VisualInterruptionDecisionPro fun testAvalancheFilter_duringAvalanche_allowConversationFromAfterEvent() { avalancheProvider.startTime = whenAgo(10) - withFilter(AvalancheSuppressor(avalancheProvider, systemClock, systemSettings)) { + withFilter( + AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager) + ) { ensurePeekState() - assertShouldHeadsUp(buildEntry { - importance = NotificationManager.IMPORTANCE_HIGH - isConversation = true - isImportantConversation = false - whenMs = whenAgo(5) - }) + assertShouldHeadsUp( + buildEntry { + importance = NotificationManager.IMPORTANCE_HIGH + isConversation = true + isImportantConversation = false + whenMs = whenAgo(5) + } + ) } } @@ -98,14 +107,18 @@ class VisualInterruptionDecisionProviderImplTest : VisualInterruptionDecisionPro fun testAvalancheFilter_duringAvalanche_suppressConversationFromBeforeEvent() { avalancheProvider.startTime = whenAgo(10) - withFilter(AvalancheSuppressor(avalancheProvider, systemClock, systemSettings)) { + withFilter( + AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager) + ) { ensurePeekState() - assertShouldNotHeadsUp(buildEntry { - importance = NotificationManager.IMPORTANCE_DEFAULT - isConversation = true - isImportantConversation = false - whenMs = whenAgo(15) - }) + assertShouldNotHeadsUp( + buildEntry { + importance = NotificationManager.IMPORTANCE_DEFAULT + isConversation = true + isImportantConversation = false + whenMs = whenAgo(15) + } + ) } } @@ -113,12 +126,16 @@ class VisualInterruptionDecisionProviderImplTest : VisualInterruptionDecisionPro fun testAvalancheFilter_duringAvalanche_allowHighPriorityConversation() { avalancheProvider.startTime = whenAgo(10) - withFilter(AvalancheSuppressor(avalancheProvider, systemClock, systemSettings)) { + withFilter( + AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager) + ) { ensurePeekState() - assertShouldHeadsUp(buildEntry { - importance = NotificationManager.IMPORTANCE_HIGH - isImportantConversation = true - }) + assertShouldHeadsUp( + buildEntry { + importance = NotificationManager.IMPORTANCE_HIGH + isImportantConversation = true + } + ) } } @@ -126,12 +143,16 @@ class VisualInterruptionDecisionProviderImplTest : VisualInterruptionDecisionPro fun testAvalancheFilter_duringAvalanche_allowCall() { avalancheProvider.startTime = whenAgo(10) - withFilter(AvalancheSuppressor(avalancheProvider, systemClock, systemSettings)) { + withFilter( + AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager) + ) { ensurePeekState() - assertShouldHeadsUp(buildEntry { - importance = NotificationManager.IMPORTANCE_HIGH - isCall = true - }) + assertShouldHeadsUp( + buildEntry { + importance = NotificationManager.IMPORTANCE_HIGH + isCall = true + } + ) } } @@ -139,12 +160,16 @@ class VisualInterruptionDecisionProviderImplTest : VisualInterruptionDecisionPro fun testAvalancheFilter_duringAvalanche_allowCategoryReminder() { avalancheProvider.startTime = whenAgo(10) - withFilter(AvalancheSuppressor(avalancheProvider, systemClock, systemSettings)) { + withFilter( + AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager) + ) { ensurePeekState() - assertShouldHeadsUp(buildEntry { - importance = NotificationManager.IMPORTANCE_HIGH - category = CATEGORY_REMINDER - }) + assertShouldHeadsUp( + buildEntry { + importance = NotificationManager.IMPORTANCE_HIGH + category = CATEGORY_REMINDER + } + ) } } @@ -152,12 +177,16 @@ class VisualInterruptionDecisionProviderImplTest : VisualInterruptionDecisionPro fun testAvalancheFilter_duringAvalanche_allowCategoryEvent() { avalancheProvider.startTime = whenAgo(10) - withFilter(AvalancheSuppressor(avalancheProvider, systemClock, systemSettings)) { + withFilter( + AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager) + ) { ensurePeekState() - assertShouldHeadsUp(buildEntry { - importance = NotificationManager.IMPORTANCE_HIGH - category = CATEGORY_EVENT - }) + assertShouldHeadsUp( + buildEntry { + importance = NotificationManager.IMPORTANCE_HIGH + category = CATEGORY_EVENT + } + ) } } @@ -165,7 +194,9 @@ class VisualInterruptionDecisionProviderImplTest : VisualInterruptionDecisionPro fun testAvalancheFilter_duringAvalanche_allowFsi() { avalancheProvider.startTime = whenAgo(10) - withFilter(AvalancheSuppressor(avalancheProvider, systemClock, systemSettings)) { + withFilter( + AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager) + ) { assertFsiNotSuppressed() } } @@ -174,16 +205,44 @@ class VisualInterruptionDecisionProviderImplTest : VisualInterruptionDecisionPro fun testAvalancheFilter_duringAvalanche_allowColorized() { avalancheProvider.startTime = whenAgo(10) - withFilter(AvalancheSuppressor(avalancheProvider, systemClock, systemSettings)) { + withFilter( + AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager) + ) { ensurePeekState() - assertShouldHeadsUp(buildEntry { - importance = NotificationManager.IMPORTANCE_HIGH - isColorized = true - }) + assertShouldHeadsUp( + buildEntry { + importance = NotificationManager.IMPORTANCE_HIGH + isColorized = true + } + ) } } @Test + fun testAvalancheFilter_duringAvalanche_allowEmergency() { + avalancheProvider.startTime = whenAgo(10) + + `when`( + packageManager.checkPermission( + org.mockito.Mockito.eq(permission.RECEIVE_EMERGENCY_BROADCAST), + anyString() + ) + ).thenReturn(PERMISSION_GRANTED) + + withFilter( + AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager) + ) { + ensurePeekState() + assertShouldHeadsUp( + buildEntry { + importance = NotificationManager.IMPORTANCE_HIGH + } + ) + } + } + + + @Test fun testPeekCondition_suppressesOnlyPeek() { withCondition(TestCondition(types = setOf(PEEK)) { true }) { assertPeekSuppressed() diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt index 3b979a7c1386..71e7dc522e20 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt @@ -42,6 +42,7 @@ import android.app.PendingIntent import android.app.PendingIntent.FLAG_MUTABLE import android.content.Context import android.content.Intent +import android.content.pm.PackageManager import android.content.pm.UserInfo import android.graphics.drawable.Icon import android.hardware.display.FakeAmbientDisplayConfiguration @@ -129,6 +130,7 @@ abstract class VisualInterruptionDecisionProviderTestBase : SysuiTestCase() { protected val userTracker = FakeUserTracker() protected val avalancheProvider: AvalancheProvider = mock() lateinit var systemSettings: SystemSettings + protected val packageManager: PackageManager = mock() protected abstract val provider: VisualInterruptionDecisionProvider diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestUtil.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestUtil.kt index 60aaa646fced..61c008b55ad0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestUtil.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestUtil.kt @@ -15,11 +15,11 @@ */ package com.android.systemui.statusbar.notification.interruption +import android.content.pm.PackageManager import android.hardware.display.AmbientDisplayConfiguration import android.os.Handler import android.os.PowerManager import com.android.internal.logging.UiEventLogger -import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.settings.UserTracker @@ -53,7 +53,8 @@ object VisualInterruptionDecisionProviderTestUtil { uiEventLogger: UiEventLogger, userTracker: UserTracker, avalancheProvider: AvalancheProvider, - systemSettings: SystemSettings + systemSettings: SystemSettings, + packageManager: PackageManager, ): VisualInterruptionDecisionProvider { return if (VisualInterruptionRefactor.isEnabled) { VisualInterruptionDecisionProviderImpl( @@ -73,7 +74,8 @@ object VisualInterruptionDecisionProviderTestUtil { uiEventLogger, userTracker, avalancheProvider, - systemSettings + systemSettings, + packageManager ) } else { NotificationInterruptStateProviderWrapper( diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/ExpansionStateLoggerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/ExpansionStateLoggerTest.java index 2662c80dce1d..59741718476f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/ExpansionStateLoggerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/ExpansionStateLoggerTest.java @@ -22,9 +22,9 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import android.os.RemoteException; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.statusbar.IStatusBarService; @@ -44,7 +44,7 @@ import org.mockito.MockitoAnnotations; import java.util.Collections; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper public class ExpansionStateLoggerTest extends SysuiTestCase { private static final String NOTIFICATION_KEY = "notin_key"; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java index 111309112f61..a8929a63a812 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java @@ -35,9 +35,9 @@ import android.os.Handler; import android.os.Looper; import android.os.UserHandle; import android.platform.test.annotations.DisableFlags; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.logging.InstanceId; @@ -87,7 +87,7 @@ import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.Executor; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper @DisableFlags(NotificationsLiveDataStoreRefactor.FLAG_NAME) public class NotificationLoggerTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryLoggerTest.kt index 4b0b4b89fad4..3ea7732bdf15 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryLoggerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryLoggerTest.kt @@ -21,8 +21,8 @@ import android.app.StatsManager import android.graphics.Bitmap import android.graphics.drawable.Icon import android.stats.sysui.NotificationEnums -import android.testing.AndroidTestingRunner import android.util.StatsEvent +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.log.assertLogsWtf @@ -46,7 +46,7 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class NotificationMemoryLoggerTest : SysuiTestCase() { @Rule @JvmField val expect = Expect.create() diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryMeterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryMeterTest.kt index 072a497f1a65..f10a52a73586 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryMeterTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryMeterTest.kt @@ -24,8 +24,8 @@ import android.content.Intent import android.graphics.Bitmap import android.graphics.drawable.Icon import android.stats.sysui.NotificationEnums -import android.testing.AndroidTestingRunner import android.widget.RemoteViews +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.notification.NotificationUtils @@ -36,7 +36,7 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class NotificationMemoryMeterTest : SysuiTestCase() { @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryViewWalkerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryViewWalkerTest.kt index 4bb28ae46211..d7dde96baf32 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryViewWalkerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryViewWalkerTest.kt @@ -3,9 +3,9 @@ package com.android.systemui.statusbar.notification.logging import android.app.Notification import android.graphics.Bitmap import android.graphics.drawable.Icon -import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.widget.RemoteViews +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder @@ -17,7 +17,7 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class NotificationMemoryViewWalkerTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewTest.kt index 9b9cb8213c91..9f98fd4c8508 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewTest.kt @@ -17,9 +17,9 @@ package com.android.systemui.statusbar.notification.row import android.annotation.ColorInt import android.graphics.Color -import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import android.view.View +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.settingslib.Utils import com.android.systemui.res.R @@ -34,7 +34,7 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @RunWithLooper class ActivatableNotificationViewTest : SysuiTestCase() { private val mContentView: View = mock() @@ -98,4 +98,4 @@ class ActivatableNotificationViewTest : SysuiTestCase() { assertThat(mView.topRoundness).isEqualTo(1f) assertThat(mView.roundableState.hashCode()).isEqualTo(roundableState.hashCode()) } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/BigPictureIconManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/BigPictureIconManagerTest.kt index 0eae5fc209b4..fda5cd286074 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/BigPictureIconManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/BigPictureIconManagerTest.kt @@ -20,8 +20,8 @@ import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.Drawable import android.graphics.drawable.Icon import android.net.Uri -import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.widget.NotificationDrawableConsumer import com.android.systemui.SysuiTestCase @@ -50,7 +50,7 @@ private const val MAX_IMAGE_SIZE = 512 // size of the test drawables in pixels @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWithLooper -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class BigPictureIconManagerTest : SysuiTestCase() { private val testDispatcher = StandardTestDispatcher() diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt index 7dcbd8084594..c5b19ab5862c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt @@ -25,8 +25,8 @@ import android.content.pm.ParceledListSlice import android.graphics.Color import android.graphics.drawable.ColorDrawable import android.print.PrintManager.PRINT_SPOOLER_PACKAGE_NAME +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.view.View @@ -47,7 +47,7 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class ChannelEditorDialogControllerTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt index 210b1a7f22f4..e738b616d227 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt @@ -21,8 +21,8 @@ import android.app.Notification import android.net.Uri import android.os.UserHandle import android.os.UserHandle.USER_ALL -import android.testing.AndroidTestingRunner import android.testing.TestableLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.logging.MetricsLogger import com.android.internal.statusbar.IStatusBarService @@ -71,7 +71,7 @@ import org.mockito.Mockito.verify import org.mockito.Mockito.`when` as whenever @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class ExpandableNotificationRowControllerTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragControllerTest.java index 9d2f32d77028..1c5f37cc60c3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragControllerTest.java @@ -31,10 +31,10 @@ import static org.mockito.Mockito.when; import android.app.Notification; import android.app.PendingIntent; import android.content.Context; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -48,7 +48,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @RunWithLooper public class ExpandableNotificationRowDragControllerTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java index aa79c23bdf74..7304bd62293d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java @@ -46,13 +46,13 @@ import android.graphics.drawable.AnimationDrawable; import android.graphics.drawable.Drawable; import android.os.UserHandle; import android.platform.test.annotations.EnableFlags; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; import android.util.DisplayMetrics; import android.view.View; import android.widget.ImageView; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.R; @@ -87,7 +87,7 @@ import java.util.List; import java.util.function.Consumer; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @RunWithLooper public class ExpandableNotificationRowTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java index ffb8646942b5..d04d6fc5f593 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java @@ -45,13 +45,13 @@ import android.content.res.Configuration; import android.graphics.drawable.Drawable; import android.os.UserHandle; import android.service.notification.StatusBarNotification; -import android.testing.AndroidTestingRunner; import android.testing.UiThreadTest; import android.view.LayoutInflater; import android.view.View; import android.widget.ImageView; import android.widget.TextView; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.statusbar.IStatusBarService; @@ -72,7 +72,7 @@ import org.mockito.MockitoAnnotations; import java.util.Locale; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @UiThreadTest public class FeedbackInfoTest extends SysuiTestCase { private static final String TEST_PACKAGE_NAME = "test_package"; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/HeadsUpStyleProviderImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/HeadsUpStyleProviderImplTest.kt index 5e50af39203f..c325791b1f05 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/HeadsUpStyleProviderImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/HeadsUpStyleProviderImplTest.kt @@ -20,7 +20,7 @@ import android.app.Flags.FLAG_COMPACT_HEADS_UP_NOTIFICATION import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.platform.test.flag.junit.SetFlagsRule -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepository @@ -31,7 +31,7 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class HeadsUpStyleProviderImplTest : SysuiTestCase() { @Rule @JvmField val setFlagsRule = SetFlagsRule() diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineTest.java index 25172deb67a7..18fd42da78ea 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineTest.java @@ -22,11 +22,11 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import androidx.annotation.NonNull; import androidx.core.os.CancellationSignal; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -48,7 +48,7 @@ import java.util.ArrayList; import java.util.List; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper public class NotifBindPipelineTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifInflationErrorManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifInflationErrorManagerTest.kt index e38adeb0fcd9..29252b27f6d0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifInflationErrorManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifInflationErrorManagerTest.kt @@ -16,8 +16,8 @@ package com.android.systemui.statusbar.notification.row -import android.testing.AndroidTestingRunner import android.testing.TestableLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.notification.collection.NotificationEntry @@ -33,7 +33,7 @@ import org.mockito.Mockito.never import org.mockito.Mockito.verify @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class NotifInflationErrorManagerTest : SysuiTestCase() { private lateinit var manager: NotifInflationErrorManager diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCacheImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCacheImplTest.java index 20cc01accbc3..8b1c95babe38 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCacheImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCacheImplTest.java @@ -26,9 +26,9 @@ import static junit.framework.Assert.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; -import android.testing.AndroidTestingRunner; import android.widget.RemoteViews; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -45,7 +45,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class NotifRemoteViewCacheImplTest extends SysuiTestCase { private NotifRemoteViewCacheImpl mNotifRemoteViewCache; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java index 03a84036b4b5..a355cd16ee15 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java @@ -40,7 +40,6 @@ import android.os.AsyncTask; import android.os.CancellationSignal; import android.os.Handler; import android.os.Looper; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; import android.util.TypedValue; @@ -49,6 +48,7 @@ import android.view.ViewGroup; import android.widget.RemoteViews; import android.widget.TextView; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import androidx.test.filters.Suppress; @@ -79,7 +79,7 @@ import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @RunWithLooper(setAsMainLooper = true) @Suppress public class NotificationContentInflaterTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt index 7332bc34b030..2bb610ac4449 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt @@ -20,7 +20,6 @@ import android.annotation.DimenRes import android.content.res.Resources import android.os.UserHandle import android.service.notification.StatusBarNotification -import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.testing.ViewUtils import android.view.NotificationHeaderView @@ -29,6 +28,7 @@ import android.view.ViewGroup import android.widget.FrameLayout import android.widget.ImageView import android.widget.LinearLayout +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.R import com.android.internal.widget.NotificationActionListLayout @@ -60,7 +60,7 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations.initMocks @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class NotificationContentViewTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java index 97cb11e2f107..be89ab8c5cf8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java @@ -65,13 +65,13 @@ import android.os.Handler; import android.os.UserHandle; import android.os.UserManager; import android.service.notification.StatusBarNotification; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.LayoutInflater; import android.view.View; import android.widget.ImageView; import android.widget.TextView; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.logging.MetricsLogger; @@ -103,7 +103,7 @@ import java.util.Optional; import java.util.concurrent.CountDownLatch; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper public class NotificationConversationInfoTest extends SysuiTestCase { private static final String TEST_PACKAGE_NAME = "test_package"; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java index 907649b90956..625963f5ec7a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java @@ -57,12 +57,12 @@ import android.os.Handler; import android.os.UserManager; import android.provider.Settings; import android.service.notification.StatusBarNotification; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.util.ArraySet; import android.view.View; import android.view.accessibility.AccessibilityManager; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.logging.MetricsLogger; @@ -115,7 +115,7 @@ import java.util.Optional; * Tests for {@link NotificationGutsManager}. */ @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper public class NotificationGutsManagerTest extends SysuiTestCase { private static final String TEST_CHANNEL_ID = "NotificationManagerServiceTestChannelId"; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt index 1b85dfa5a087..0b5f8d5e948c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt @@ -31,11 +31,11 @@ import android.os.fakeExecutorHandler import android.os.userManager import android.provider.Settings import android.service.notification.NotificationListenerService.Ranking -import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import android.util.ArraySet import android.view.View import android.view.accessibility.accessibilityManager +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.compose.animation.scene.ObservableTransitionState import com.android.internal.logging.MetricsLogger @@ -91,7 +91,7 @@ import org.mockito.invocation.InvocationOnMock /** Tests for [NotificationGutsManager] with the scene container enabled. */ @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @RunWithLooper @EnableSceneContainer class NotificationGutsManagerWithScenesTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsTest.kt index 7f9471e5271f..350f90dcbe91 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsTest.kt @@ -16,11 +16,11 @@ package com.android.systemui.statusbar.notification.row -import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import android.testing.ViewUtils import android.view.LayoutInflater import android.view.View +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.res.R import com.android.systemui.SysuiTestCase @@ -34,7 +34,7 @@ import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @RunWithLooper class NotificationGutsTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java index 13ced928175e..245a6a0b130c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java @@ -55,13 +55,13 @@ import android.os.RemoteException; import android.os.UserHandle; import android.service.notification.StatusBarNotification; import android.telecom.TelecomManager; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.LayoutInflater; import android.view.View; import android.widget.ImageView; import android.widget.TextView; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.logging.MetricsLogger; @@ -85,7 +85,7 @@ import org.mockito.junit.MockitoRule; import java.util.concurrent.CountDownLatch; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper public class NotificationInfoTest extends SysuiTestCase { private static final String TEST_PACKAGE_NAME = "test_package"; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java index e9290289f7f2..027e899e20df 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java @@ -29,13 +29,13 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.provider.Settings; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; import android.testing.ViewUtils; import android.view.View; import android.view.ViewGroup; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; @@ -49,7 +49,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @RunWithLooper @SmallTest public class NotificationMenuRowTest extends LeakCheckedTest { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationSettingsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationSettingsControllerTest.kt index 8261c1c4b42e..352b79f50b90 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationSettingsControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationSettingsControllerTest.kt @@ -22,8 +22,8 @@ import android.database.ContentObserver import android.net.Uri import android.os.Handler import android.provider.Settings.Secure -import android.testing.AndroidTestingRunner import android.testing.TestableLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.dump.DumpManager @@ -54,7 +54,7 @@ import org.mockito.Mockito.verifyZeroInteractions import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class NotificationSettingsControllerTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java index 4a91cd239d87..22f1e4604bbd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java @@ -23,11 +23,11 @@ import static junit.framework.Assert.assertTrue; import static org.mockito.Mockito.mock; import android.provider.Settings; -import android.testing.AndroidTestingRunner; import android.testing.TestableResources; import android.testing.UiThreadTest; import android.util.KeyValueListParser; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -41,7 +41,7 @@ import org.junit.runner.RunWith; import java.util.ArrayList; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @UiThreadTest public class NotificationSnoozeTest extends SysuiTestCase { private static final int RES_DEFAULT = 2; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java index 51665d987888..57b0f3f8d8c5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java @@ -41,7 +41,6 @@ import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.os.UserHandle; import android.service.notification.StatusBarNotification; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.text.SpannableString; import android.view.LayoutInflater; @@ -49,6 +48,7 @@ import android.view.View; import android.widget.ImageView; import android.widget.TextView; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.logging.MetricsLogger; @@ -69,7 +69,7 @@ import org.mockito.junit.MockitoRule; import java.util.concurrent.CountDownLatch; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper public class PartialConversationInfoTest extends SysuiTestCase { private static final String TEST_PACKAGE_NAME = "test_package"; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java index 1534c84fd99a..841cb4a3669b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java @@ -34,10 +34,10 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.util.Log; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -52,7 +52,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper public class RowContentBindStageTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineConversationViewBinderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineConversationViewBinderTest.kt index 1c959af6ec3f..53a11989cca0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineConversationViewBinderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineConversationViewBinderTest.kt @@ -18,8 +18,8 @@ package com.android.systemui.statusbar.notification.row import android.app.Notification import android.app.Person import android.platform.test.annotations.EnableFlags -import android.testing.AndroidTestingRunner import android.testing.TestableLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.R import com.android.systemui.SysuiTestCase @@ -34,7 +34,7 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class SingleLineConversationViewBinderTest : SysuiTestCase() { private lateinit var notificationBuilder: Notification.Builder diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewBinderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewBinderTest.kt index f0fc349777b2..ee819c4df0ef 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewBinderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewBinderTest.kt @@ -17,8 +17,8 @@ package com.android.systemui.statusbar.notification.row import android.app.Notification import android.platform.test.annotations.EnableFlags -import android.testing.AndroidTestingRunner import android.testing.TestableLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.R import com.android.systemui.SysuiTestCase @@ -33,7 +33,7 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class SingleLineViewBinderTest : SysuiTestCase() { private lateinit var notificationBuilder: Notification.Builder diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflaterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflaterTest.kt index b67153a842ac..e025d3d36ab1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflaterTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflaterTest.kt @@ -27,9 +27,9 @@ import android.graphics.PorterDuffXfermode import android.graphics.drawable.Drawable import android.graphics.drawable.Icon import android.platform.test.annotations.EnableFlags -import android.testing.AndroidTestingRunner import android.testing.TestableLooper import androidx.core.graphics.drawable.toBitmap +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.res.R @@ -48,7 +48,7 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper @EnableFlags(AsyncHybridViewInflation.FLAG_NAME) class SingleLineViewInflaterTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/TextPrecomputerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/TextPrecomputerTest.kt index d46763df8a75..f8533a532d32 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/TextPrecomputerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/TextPrecomputerTest.kt @@ -16,11 +16,11 @@ package com.android.systemui.statusbar.notification.row -import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import android.text.PrecomputedText import android.text.TextPaint import android.widget.TextView +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.google.common.truth.Truth.assertThat @@ -29,7 +29,7 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @RunWithLooper class TextPrecomputerTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModelTest.kt index c9602307216d..0dc871a523ee 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModelTest.kt @@ -18,7 +18,7 @@ package com.android.systemui.statusbar.notification.row.ui.viewmodel -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.accessibility.data.repository.FakeAccessibilityRepository @@ -31,7 +31,7 @@ import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @SmallTest class ActivatableNotificationViewModelTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapperTest.java index a15b4cd37184..ec280a1d6d01 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapperTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapperTest.java @@ -24,10 +24,10 @@ import android.app.Notification; import android.graphics.drawable.AnimatedImageDrawable; import android.graphics.drawable.Icon; import android.os.Bundle; -import android.testing.AndroidTestingRunner; import android.view.LayoutInflater; import android.view.View; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.R; @@ -40,7 +40,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @SmallTest public class NotificationBigPictureTemplateViewWrapperTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapperTest.kt index fe2971c46c32..9d990b1d7edf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapperTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapperTest.kt @@ -17,8 +17,8 @@ package com.android.systemui.statusbar.notification.row.wrapper import android.graphics.drawable.AnimatedImageDrawable -import android.testing.AndroidTestingRunner import android.view.View +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.R import com.android.internal.widget.CachingIconView @@ -38,7 +38,7 @@ import org.mockito.Mockito.verify import org.mockito.Mockito.`when` as whenever @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class NotificationConversationTemplateViewWrapperTest : SysuiTestCase() { private lateinit var mRow: ExpandableNotificationRow diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java index 2d72c7e0b714..f9a9704334a0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java @@ -16,10 +16,10 @@ package com.android.systemui.statusbar.notification.row.wrapper; -import android.testing.AndroidTestingRunner; import android.view.View; import android.widget.RemoteViews; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -33,7 +33,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class NotificationCustomViewWrapperTest extends SysuiTestCase { private ExpandableNotificationRow mRow; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMessagingTemplateViewWrapperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMessagingTemplateViewWrapperTest.kt index f26c18b1d197..fc829d53a6b4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMessagingTemplateViewWrapperTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMessagingTemplateViewWrapperTest.kt @@ -17,8 +17,8 @@ package com.android.systemui.statusbar.notification.row.wrapper import android.graphics.drawable.AnimatedImageDrawable -import android.testing.AndroidTestingRunner import android.view.View +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.widget.MessagingGroup import com.android.internal.widget.MessagingImageMessage @@ -36,7 +36,7 @@ import org.mockito.Mockito.verify import org.mockito.Mockito.`when` as whenever @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class NotificationMessagingTemplateViewWrapperTest : SysuiTestCase() { private lateinit var mRow: ExpandableNotificationRow diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapperTest.kt index 54eed26adaf3..1ce3bada4609 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapperTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapperTest.kt @@ -19,7 +19,6 @@ package com.android.systemui.statusbar.notification.row.wrapper import android.app.PendingIntent import android.app.PendingIntent.CancelListener import android.content.Intent -import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.testing.TestableLooper.RunWithLooper import android.testing.ViewUtils @@ -27,6 +26,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.FrameLayout +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.R import com.android.systemui.SysuiTestCase @@ -46,7 +46,7 @@ import org.mockito.Mockito.times import org.mockito.Mockito.verify @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @RunWithLooper class NotificationTemplateViewWrapperTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapperTest.java index fad85f53a091..d17c8dbcf38d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapperTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapperTest.java @@ -20,11 +20,11 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import android.content.Context; -import android.testing.AndroidTestingRunner; import android.view.View; import android.widget.LinearLayout; import android.widget.TextView; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -35,7 +35,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @SmallTest public class NotificationViewWrapperTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt index 59d98c233f99..4c6e25a530a5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt @@ -19,7 +19,7 @@ package com.android.systemui.statusbar.notification.shelf.domain.interactor import android.os.PowerManager -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue @@ -41,7 +41,7 @@ import org.junit.runner.RunWith import org.mockito.Mockito.isNull import org.mockito.Mockito.verify -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @SmallTest class NotificationShelfInteractorTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt index 917569ca787b..e2fb3ba11a02 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt @@ -17,7 +17,7 @@ package com.android.systemui.statusbar.notification.shelf.ui.viewmodel import android.os.PowerManager -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysUITestComponent import com.android.systemui.SysUITestModule @@ -44,7 +44,7 @@ import org.junit.runner.RunWith import org.mockito.Mockito import org.mockito.Mockito.verify -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @SmallTest class NotificationShelfViewModelTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt index fb1594898f24..2349c252369c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt @@ -16,7 +16,7 @@ package com.android.systemui.statusbar.notification.stack -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.dump.DumpManager @@ -33,7 +33,7 @@ import org.junit.runner.RunWith private const val MAX_PULSE_HEIGHT = 100000f -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @SmallTest class AmbientStateTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/MediaContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/MediaContainerViewTest.kt index f4e236e5bbf9..3a77d822eb7e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/MediaContainerViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/MediaContainerViewTest.kt @@ -1,8 +1,8 @@ package com.android.systemui.statusbar.notification.stack -import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.view.LayoutInflater +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.res.R @@ -15,7 +15,7 @@ import org.junit.runner.RunWith * Tests for {@link MediaContainView}. */ @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class MediaContainerViewTest : SysuiTestCase() { @@ -35,4 +35,4 @@ class MediaContainerViewTest : SysuiTestCase() { mediaContainerView.updateClipping() assertTrue(mediaContainerView.clipHeight == 10) } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java index 3b16f1416935..14bbd38ece2c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java @@ -21,13 +21,13 @@ import static org.junit.Assert.assertNull; import android.app.Notification; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; import android.view.NotificationHeaderView; import android.view.View; import android.widget.RemoteViews; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -45,7 +45,7 @@ import org.junit.runner.RunWith; import java.util.List; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @RunWithLooper //@DisableFlags(AsyncGroupHeaderViewInflation.FLAG_NAME) public class NotificationChildrenContainerTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt index 1eed4207541c..48e8f88a15c0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt @@ -2,10 +2,10 @@ package com.android.systemui.statusbar.notification.stack import android.platform.test.annotations.DisableFlags import android.service.notification.StatusBarNotification -import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import android.view.LayoutInflater import android.widget.FrameLayout +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerProgress import com.android.systemui.SysuiTestCase @@ -35,7 +35,7 @@ import org.mockito.Mockito.`when` as whenever /** Tests for {@link NotificationShelf}. */ @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @RunWithLooper open class NotificationShelfTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java index f262df1d875a..ce2491bb098e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java @@ -42,12 +42,12 @@ import static org.mockito.Mockito.when; import android.metrics.LogMaker; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.MotionEvent; import android.view.View; import android.view.ViewTreeObserver; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.logging.MetricsLogger; @@ -126,7 +126,7 @@ import javax.inject.Provider; */ @SmallTest @TestableLooper.RunWithLooper(setAsMainLooper = true) -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { protected KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java index 0c0a2a59d9bc..f461e2f67d20 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java @@ -54,7 +54,6 @@ import android.graphics.Rect; import android.os.SystemClock; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableResources; import android.util.MathUtils; @@ -65,6 +64,7 @@ import android.view.WindowInsets; import android.view.WindowInsetsAnimation; import android.widget.TextView; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.keyguard.BouncerPanelExpansionCalculator; @@ -114,7 +114,7 @@ import java.util.function.Consumer; * Tests for {@link NotificationStackScrollLayout}. */ @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper public class NotificationStackScrollLayoutTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt index 6fec9ad7bd66..dae5542123ed 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt @@ -18,8 +18,8 @@ package com.android.systemui.statusbar.notification.stack import android.annotation.DimenRes import android.service.notification.StatusBarNotification -import android.testing.AndroidTestingRunner import android.view.View.VISIBLE +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.res.R import com.android.systemui.SysuiTestCase @@ -44,7 +44,7 @@ import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class NotificationStackSizeCalculatorTest : SysuiTestCase() { @Mock private lateinit var sysuiStatusBarStateController: SysuiStatusBarStateController diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java index 85a2bdd21073..2d119174efff 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java @@ -37,12 +37,12 @@ import android.animation.Animator; import android.animation.ValueAnimator.AnimatorUpdateListener; import android.os.Handler; import android.service.notification.StatusBarNotification; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -70,7 +70,7 @@ import java.util.stream.Collectors; * Tests for {@link NotificationSwipeHelper}. */ @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper() public class NotificationSwipeHelperTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt index e30947ce84bd..660eb308fdf3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt @@ -1,8 +1,8 @@ package com.android.systemui.statusbar.notification.stack -import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.testing.TestableLooper.RunWithLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.flags.FakeFeatureFlags @@ -15,7 +15,7 @@ import org.junit.runner.RunWith /** Tests for {@link NotificationTargetsHelper}. */ @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @RunWithLooper class NotificationTargetsHelperTest : SysuiTestCase() { private val featureFlags = FakeFeatureFlags() diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt index 926c35f32967..798465e7b165 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt @@ -17,8 +17,8 @@ package com.android.systemui.statusbar.notification.stack import android.platform.test.annotations.EnableFlags -import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.animation.AnimatorTestRule @@ -48,7 +48,7 @@ private const val FULL_SHADE_APPEAR_TRANSLATION = 300 private const val HEADS_UP_ABOVE_SCREEN = 80 @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @RunWithLooper class StackStateAnimatorTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ViewStateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ViewStateTest.kt index cd6bb5f4966a..e493420b64a1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ViewStateTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ViewStateTest.kt @@ -16,7 +16,7 @@ package com.android.systemui.statusbar.notification.stack -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.log.assertDoesNotLogWtf @@ -27,7 +27,7 @@ import org.junit.Assert import org.junit.Test import org.junit.runner.RunWith -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @SmallTest class ViewStateTest : SysuiTestCase() { private val viewState = ViewState() diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractorTest.kt index e2ac2038be32..e46906fb5192 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractorTest.kt @@ -17,10 +17,10 @@ package com.android.systemui.statusbar.notification.stack.domain.interactor import android.content.res.Configuration import android.graphics.Rect -import android.testing.AndroidTestingRunner import android.view.Surface import android.view.Surface.ROTATION_0 import android.view.Surface.ROTATION_90 +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.common.ui.data.repository.ConfigurationRepositoryImpl @@ -52,7 +52,7 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) open class HideNotificationsInteractorTest : SysuiTestCase() { private val testScope = TestScope() diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java index 84cd518cf85a..f0bc655a554d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java @@ -45,10 +45,10 @@ import android.hardware.display.ColorDisplayManager; import android.hardware.display.NightDisplayListener; import android.os.Handler; import android.os.UserHandle; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -89,7 +89,7 @@ import java.util.List; import javax.inject.Named; -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @RunWithLooper @SmallTest public class AutoTileManagerTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java index dc7525c8e256..285949a41fcd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java @@ -37,11 +37,11 @@ import android.hardware.biometrics.BiometricSourceType; import android.os.Handler; import android.os.PowerManager; import android.os.UserHandle; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper.RunWithLooper; import android.testing.TestableResources; import android.view.ViewRootImpl; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.logging.MetricsLogger; @@ -77,7 +77,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @RunWithLooper public class BiometricsUnlockControllerTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java index fe6a88d00374..5675915506a5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java @@ -30,9 +30,9 @@ import android.content.ComponentName; import android.os.PowerManager; import android.os.UserHandle; import android.os.Vibrator; -import android.testing.AndroidTestingRunner; import android.view.HapticFeedbackConstants; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.logging.MetricsLogger; @@ -71,7 +71,7 @@ import org.mockito.stubbing.Answer; import java.util.Optional; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class CentralSurfacesCommandQueueCallbacksTest extends SysuiTestCase { @Mock private CentralSurfaces mCentralSurfaces; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java index 4488799cfd7a..cde241bbe918 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java @@ -57,6 +57,7 @@ import android.app.WallpaperManager; import android.app.trust.TrustManager; import android.content.BroadcastReceiver; import android.content.IntentFilter; +import android.content.pm.PackageManager; import android.content.res.Configuration; import android.graphics.Rect; import android.hardware.devicestate.DeviceState; @@ -75,7 +76,6 @@ import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; import android.service.dreams.IDreamManager; import android.support.test.metricshelper.MetricsAsserts; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; import android.util.DisplayMetrics; @@ -85,6 +85,7 @@ import android.view.ViewGroup.LayoutParams; import android.view.WindowManager; import android.view.WindowMetrics; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.compose.animation.scene.ObservableTransitionState; @@ -223,7 +224,7 @@ import java.util.Optional; import javax.inject.Provider; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @RunWithLooper(setAsMainLooper = true) @EnableFlags(FLAG_LIGHT_REVEAL_MIGRATION) public class CentralSurfacesImplTest extends SysuiTestCase { @@ -339,6 +340,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase { @Mock private WindowRootViewVisibilityInteractor mWindowRootViewVisibilityInteractor; @Mock private KeyboardShortcuts mKeyboardShortcuts; @Mock private KeyboardShortcutListSearch mKeyboardShortcutListSearch; + @Mock private PackageManager mPackageManager; private ShadeController mShadeController; private final FakeSystemClock mFakeSystemClock = new FakeSystemClock(); @@ -392,7 +394,8 @@ public class CentralSurfacesImplTest extends SysuiTestCase { mock(UiEventLogger.class), mUserTracker, mAvalancheProvider, - mSystemSettings); + mSystemSettings, + mPackageManager); mVisualInterruptionDecisionProvider.start(); mContext.addMockSystemService(TrustManager.class, mock(TrustManager.class)); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ConfigurationControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ConfigurationControllerImplTest.kt index 56d23978a5c7..942ea65ec49e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ConfigurationControllerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ConfigurationControllerImplTest.kt @@ -21,7 +21,7 @@ import android.content.res.Configuration.UI_MODE_NIGHT_NO import android.content.res.Configuration.UI_MODE_NIGHT_YES import android.content.res.Configuration.UI_MODE_TYPE_CAR import android.os.LocaleList -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener @@ -36,7 +36,7 @@ import org.mockito.Mockito.never import org.mockito.Mockito.verify import java.util.Locale -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @SmallTest class ConfigurationControllerImplTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeScrimControllerTest.java index 34c43ef52a00..3b3ec263145a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeScrimControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeScrimControllerTest.java @@ -20,9 +20,9 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -36,7 +36,7 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper @SmallTest public class DozeScrimControllerTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FoldStateListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FoldStateListenerTest.kt index 5d42d5167c27..a3e2d1949a29 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FoldStateListenerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FoldStateListenerTest.kt @@ -16,7 +16,7 @@ package com.android.systemui.statusbar.phone import android.hardware.devicestate.DeviceState -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.R import com.android.systemui.SysuiTestCase @@ -30,7 +30,7 @@ import org.mockito.Mockito.times import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations.initMocks -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @SmallTest class FoldStateListenerTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java index 3e9006e5268c..0d06b6431e1c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java @@ -26,12 +26,12 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.platform.test.annotations.DisableFlags; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; import android.view.View; import android.widget.TextView; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -62,7 +62,7 @@ import org.junit.runner.RunWith; import java.util.Optional; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @RunWithLooper public class HeadsUpAppearanceControllerTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java index e8346933f192..cf87afbbff34 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java @@ -28,8 +28,8 @@ import static org.mockito.Mockito.when; import android.content.res.Resources; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; -import android.testing.AndroidTestingRunner; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.Flags; @@ -50,7 +50,7 @@ import org.mockito.MockitoSession; import org.mockito.quality.Strictness; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase { private static final int SCREEN_HEIGHT = 2000; private static final int EMPTY_HEIGHT = 0; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardDismissUtilTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardDismissUtilTest.java index b0aa2d3934cc..d880becaa4bd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardDismissUtilTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardDismissUtilTest.java @@ -20,8 +20,8 @@ import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import android.testing.AndroidTestingRunner; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -35,7 +35,7 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @SmallTest public class KeyguardDismissUtilTest extends SysuiTestCase { @Mock diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextViewTest.java index 5cea931e2070..109cd948b5b3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextViewTest.java @@ -21,10 +21,10 @@ import static android.view.View.ACCESSIBILITY_LIVE_REGION_POLITE; import static com.google.common.truth.Truth.assertThat; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.View; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -35,7 +35,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper public class KeyguardIndicationTextViewTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java index 5b2526e22679..71f09a5d3f04 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java @@ -42,11 +42,11 @@ import android.os.UserManager; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; import android.provider.Settings; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.LayoutInflater; import android.view.View; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.keyguard.CarrierTextController; @@ -99,7 +99,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper public class KeyguardStatusBarViewControllerTest extends SysuiTestCase { @Mock diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewTest.java index c44f979fa971..0932a0c9307c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewTest.java @@ -18,11 +18,11 @@ package com.android.systemui.statusbar.phone; import static com.google.common.truth.Truth.assertThat; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.LayoutInflater; import android.view.View; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -33,7 +33,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper public class KeyguardStatusBarViewTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyLightsOutNotifControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyLightsOutNotifControllerTest.java index f91064b49e95..782ca91bc8fb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyLightsOutNotifControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyLightsOutNotifControllerTest.java @@ -26,7 +26,6 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.platform.test.annotations.DisableFlags; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper.RunWithLooper; import android.view.Display; import android.view.View; @@ -35,6 +34,7 @@ import android.view.WindowInsets; import android.view.WindowManager; import androidx.lifecycle.Observer; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -54,7 +54,7 @@ import org.mockito.MockitoAnnotations; import java.util.Objects; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @RunWithLooper @DisableFlags(NotificationsLiveDataStoreRefactor.FLAG_NAME) public class LegacyLightsOutNotifControllerTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImplTest.java index 9d53b9c66b33..fea0e72fe577 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImplTest.java @@ -21,9 +21,9 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.platform.test.annotations.DisableFlags; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.Flags; @@ -49,7 +49,7 @@ import org.mockito.MockitoAnnotations; import java.util.Optional; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper @DisableFlags(NotificationIconContainerRefactor.FLAG_NAME) public class LegacyNotificationIconAreaControllerImplTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LetterboxAppearanceCalculatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LetterboxAppearanceCalculatorTest.kt index e7b287c5bdc8..518b327036cb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LetterboxAppearanceCalculatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LetterboxAppearanceCalculatorTest.kt @@ -18,9 +18,9 @@ package com.android.systemui.statusbar.phone import android.graphics.Color import android.graphics.Rect -import android.testing.AndroidTestingRunner import android.view.WindowInsetsController import android.view.WindowInsetsController.APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.statusbar.LetterboxDetails import com.android.internal.view.AppearanceRegion @@ -36,7 +36,7 @@ import org.mockito.Mock import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @SmallTest class LetterboxAppearanceCalculatorTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LetterboxBackgroundProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LetterboxBackgroundProviderTest.kt index 1cc0bd3cb36c..788c2cb2a485 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LetterboxBackgroundProviderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LetterboxBackgroundProviderTest.kt @@ -21,8 +21,8 @@ import android.app.WallpaperManager.OnColorsChangedListener import android.graphics.Color import android.os.Handler import android.os.Looper -import android.testing.AndroidTestingRunner import android.view.IWindowManager +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.util.concurrency.FakeExecutor @@ -40,7 +40,7 @@ import org.mockito.Mockito.doAnswer import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @SmallTest class LetterboxBackgroundProviderTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java index 7271a5efc377..a27073c77eb4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java @@ -35,10 +35,10 @@ import static org.mockito.Mockito.when; import android.graphics.Color; import android.graphics.Rect; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import androidx.annotation.ColorInt; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.colorextraction.ColorExtractor.GradientColors; @@ -67,7 +67,7 @@ import java.util.Arrays; import java.util.List; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper public class LightBarControllerTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarTransitionsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarTransitionsControllerTest.java index f71114d92aa3..43c19b833646 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarTransitionsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarTransitionsControllerTest.java @@ -28,9 +28,9 @@ import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.policy.GestureNavigationSettingsObserver; @@ -48,7 +48,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper public class LightBarTransitionsControllerTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt index 9f4e1dd8cc4d..9d97e5a5686e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt @@ -16,8 +16,8 @@ package com.android.systemui.statusbar.phone import android.service.notification.StatusBarNotification -import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.StatusBarIconView @@ -34,7 +34,7 @@ import org.mockito.Mockito.`when` as whenever /** Tests for {@link NotificationIconContainer}. */ @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @RunWithLooper class NotificationIconContainerTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationTapHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationTapHelperTest.java index ccd1a8c7a9b2..9522e1f65a4c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationTapHelperTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationTapHelperTest.java @@ -22,11 +22,11 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.res.Resources; -import android.testing.AndroidTestingRunner; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -42,7 +42,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class NotificationTapHelperTest extends SysuiTestCase { private NotificationTapHelper mNotificationTapHelper; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt index 8d2c1588fb62..f2f336c45d7f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt @@ -22,9 +22,9 @@ import android.app.admin.DevicePolicyResourcesManager import android.content.SharedPreferences import android.os.UserManager import android.telecom.TelecomManager -import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.testing.TestableLooper.RunWithLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.broadcast.BroadcastDispatcher @@ -79,7 +79,7 @@ import org.mockito.Mockito.verify import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @RunWithLooper @OptIn(ExperimentalCoroutinesApi::class) @SmallTest diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java index 1000329cb276..416a869bf2f0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java @@ -49,12 +49,12 @@ import android.content.Context; import android.content.res.ColorStateList; import android.content.res.TypedArray; import android.graphics.Color; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.ViewUtils; import android.util.MathUtils; import android.view.View; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.colorextraction.ColorExtractor.GradientColors; @@ -107,7 +107,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Map; -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) @SmallTest public class ScrimControllerTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarBoundsProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarBoundsProviderTest.kt index 61da701ce971..b9cfe21dcad3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarBoundsProviderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarBoundsProviderTest.kt @@ -17,10 +17,10 @@ package com.android.systemui.statusbar.phone import android.graphics.Rect -import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import android.view.View import android.widget.FrameLayout +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.phone.StatusBarBoundsProvider.BoundsChangeListener @@ -37,7 +37,7 @@ import org.mockito.Mockito.spy import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @RunWithLooper(setAsMainLooper = true) @SmallTest class StatusBarBoundsProviderTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java index 6b3c0053e738..3ca4c594cede 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java @@ -41,7 +41,6 @@ import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.service.trust.TrustAgentService; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.MotionEvent; import android.view.View; @@ -55,6 +54,7 @@ import android.window.OnBackInvokedCallback; import android.window.OnBackInvokedDispatcher; import android.window.WindowOnBackInvokedDispatcher; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.util.LatencyTracker; @@ -119,7 +119,7 @@ import org.mockito.MockitoAnnotations; import java.util.Optional; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java index 269510e0b4b2..9fa392f3a337 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java @@ -51,9 +51,9 @@ import android.os.RemoteException; import android.os.UserHandle; import android.service.dreams.IDreamManager; import android.service.notification.StatusBarNotification; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.jank.InteractionJankMonitor; @@ -119,7 +119,7 @@ import java.util.List; import java.util.Optional; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java index a8c5fc357c7c..95472cad4b90 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java @@ -34,10 +34,10 @@ import android.app.PendingIntent; import android.app.StatusBarManager; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.InitController; @@ -85,7 +85,7 @@ import java.util.List; import java.util.Set; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @RunWithLooper() public class StatusBarNotificationPresenterTest extends SysuiTestCase { private StatusBarNotificationPresenter mStatusBarNotificationPresenter; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java index 929099a8f1f7..35888a5fa734 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java @@ -24,10 +24,10 @@ import static org.mockito.Mockito.when; import static org.mockito.internal.verification.VerificationModeFactory.times; import android.content.Intent; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.View; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -52,7 +52,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper public class StatusBarRemoteInputCallbackTest extends SysuiTestCase { @Mock private DeviceProvisionedController mDeviceProvisionedController; @@ -103,4 +103,4 @@ public class StatusBarRemoteInputCallbackTest extends SysuiTestCase { verify(mStatusBarKeyguardViewManager).showBouncer(true); } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListenerTest.kt index 1455693fc54b..11dd587a04ad 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListenerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListenerTest.kt @@ -21,7 +21,6 @@ import android.graphics.Color import android.graphics.drawable.Drawable import android.graphics.drawable.PaintDrawable import android.os.SystemClock -import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.testing.TestableLooper.RunWithLooper import android.testing.ViewUtils @@ -30,6 +29,7 @@ import android.view.View import android.view.ViewGroupOverlay import android.widget.LinearLayout import androidx.annotation.ColorInt +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.res.R import com.android.systemui.SysuiTestCase @@ -45,7 +45,7 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito.verify -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @RunWithLooper(setAsMainLooper = true) @SmallTest class StatusOverlayHoverListenerTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemUIBottomSheetDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemUIBottomSheetDialogTest.kt index dedd0afb6127..b560c591af1e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemUIBottomSheetDialogTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemUIBottomSheetDialogTest.kt @@ -15,9 +15,9 @@ package com.android.systemui.statusbar.phone import android.app.Dialog import android.content.res.Configuration -import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import android.view.WindowManager +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.kosmos.testScope @@ -39,7 +39,7 @@ import org.mockito.Mockito.verify @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @RunWithLooper(setAsMainLooper = true) class SystemUIBottomSheetDialogTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt index c8ff20b31aae..624c070e95e0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt @@ -18,9 +18,9 @@ package com.android.systemui.statusbar.phone import android.os.Handler import android.os.PowerManager -import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import android.view.Display +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.jank.InteractionJankMonitor import com.android.systemui.SysuiTestCase @@ -51,7 +51,7 @@ import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @RunWithLooper(setAsMainLooper = true) class UnlockedScreenOffAnimationControllerTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java index 66211c922abb..fdf77ae87011 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java @@ -426,7 +426,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { fragment.disable(DEFAULT_DISPLAY, 0, 0, false); assertEquals(View.GONE, - mFragment.getView().findViewById(R.id.ongoing_call_chip).getVisibility()); + mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility()); } @Test @@ -438,7 +438,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { fragment.disable(DEFAULT_DISPLAY, 0, 0, false); assertEquals(View.VISIBLE, - mFragment.getView().findViewById(R.id.ongoing_call_chip).getVisibility()); + mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility()); assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility()); } @@ -452,7 +452,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false); assertEquals(View.GONE, - mFragment.getView().findViewById(R.id.ongoing_call_chip).getVisibility()); + mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility()); } @Test @@ -465,7 +465,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { fragment.disable(DEFAULT_DISPLAY, 0, 0, false); assertEquals(View.GONE, - mFragment.getView().findViewById(R.id.ongoing_call_chip).getVisibility()); + mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility()); } @Test @@ -477,21 +477,21 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { fragment.disable(DEFAULT_DISPLAY, 0, 0, false); assertEquals(View.VISIBLE, - mFragment.getView().findViewById(R.id.ongoing_call_chip).getVisibility()); + mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility()); // Ongoing call ended when(mOngoingCallController.hasOngoingCall()).thenReturn(false); fragment.disable(DEFAULT_DISPLAY, 0, 0, false); assertEquals(View.GONE, - mFragment.getView().findViewById(R.id.ongoing_call_chip).getVisibility()); + mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility()); // Ongoing call started when(mOngoingCallController.hasOngoingCall()).thenReturn(true); fragment.disable(DEFAULT_DISPLAY, 0, 0, false); assertEquals(View.VISIBLE, - mFragment.getView().findViewById(R.id.ongoing_call_chip).getVisibility()); + mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility()); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt index 05464f3b715a..4d6798be9211 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt @@ -106,7 +106,7 @@ class OngoingCallControllerTest : SysuiTestCase() { fun setUp() { allowTestableLooperAsMainThread() TestableLooper.get(this).runWithLooper { - chipView = LayoutInflater.from(mContext).inflate(R.layout.ongoing_call_chip, null) + chipView = LayoutInflater.from(mContext).inflate(R.layout.ongoing_activity_chip, null) } MockitoAnnotations.initMocks(this) @@ -206,7 +206,7 @@ class OngoingCallControllerTest : SysuiTestCase() { View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) ) - assertThat(chipView.findViewById<View>(R.id.ongoing_call_chip_time)?.measuredWidth) + assertThat(chipView.findViewById<View>(R.id.ongoing_activity_chip_time)?.measuredWidth) .isEqualTo(0) } @@ -222,7 +222,7 @@ class OngoingCallControllerTest : SysuiTestCase() { View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) ) - assertThat(chipView.findViewById<View>(R.id.ongoing_call_chip_time)?.measuredWidth) + assertThat(chipView.findViewById<View>(R.id.ongoing_activity_chip_time)?.measuredWidth) .isGreaterThan(0) } @@ -237,7 +237,7 @@ class OngoingCallControllerTest : SysuiTestCase() { View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) ) - assertThat(chipView.findViewById<View>(R.id.ongoing_call_chip_time)?.measuredWidth) + assertThat(chipView.findViewById<View>(R.id.ongoing_activity_chip_time)?.measuredWidth) .isGreaterThan(0) } @@ -472,7 +472,10 @@ class OngoingCallControllerTest : SysuiTestCase() { lateinit var newChipView: View TestableLooper.get(this).runWithLooper { - newChipView = LayoutInflater.from(mContext).inflate(R.layout.ongoing_call_chip, null) + newChipView = LayoutInflater.from(mContext).inflate( + R.layout.ongoing_activity_chip, + null + ) } // Change the chip view associated with the controller. diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt index 598b12ccdc38..eb2538ec032e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt @@ -20,6 +20,7 @@ import android.telephony.SubscriptionInfo import android.telephony.SubscriptionManager import android.telephony.SubscriptionManager.PROFILE_CLASS_UNSET import android.telephony.TelephonyManager +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.demomode.DemoMode @@ -60,7 +61,6 @@ import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @@ -73,7 +73,7 @@ import org.mockito.MockitoAnnotations @Suppress("EXPERIMENTAL_IS_NOT_ENABLED") @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class MobileRepositorySwitcherTest : SysuiTestCase() { private lateinit var underTest: MobileRepositorySwitcher private lateinit var realRepo: MobileConnectionsRepositoryImpl diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryTest.kt index 265440154d77..237aabccfbd9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryTest.kt @@ -17,7 +17,7 @@ package com.android.systemui.statusbar.pipeline.mobile.data.repository.prod import android.telephony.TelephonyManager -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue @@ -44,7 +44,7 @@ import org.mockito.MockitoAnnotations @SmallTest @OptIn(ExperimentalCoroutinesApi::class) -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class CarrierMergedConnectionRepositoryTest : SysuiTestCase() { private lateinit var underTest: CarrierMergedConnectionRepository diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt index 36df61d287a1..96e599f8f4d0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.pipeline.mobile.data.repository.prod +import android.annotation.SuppressLint import android.content.Intent import android.net.ConnectivityManager import android.net.Network @@ -27,8 +28,10 @@ import android.net.NetworkCapabilities.TRANSPORT_WIFI import android.net.vcn.VcnTransportInfo import android.net.wifi.WifiInfo import android.net.wifi.WifiManager +import android.os.Bundle import android.os.ParcelUuid import android.telephony.CarrierConfigManager +import android.telephony.ServiceState import android.telephony.SubscriptionInfo import android.telephony.SubscriptionManager import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID @@ -53,6 +56,7 @@ import com.android.systemui.log.table.TableLogBufferFactory import com.android.systemui.statusbar.connectivity.WifiPickerTrackerFactory import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository import com.android.systemui.statusbar.pipeline.mobile.data.MobileInputLogger +import com.android.systemui.statusbar.pipeline.mobile.data.model.ServiceStateModel import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel import com.android.systemui.statusbar.pipeline.mobile.data.repository.CarrierConfigRepository import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository @@ -595,6 +599,51 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() { assertThat(mobileRepo.getIsCarrierMerged()).isFalse() } + @SuppressLint("UnspecifiedRegisterReceiverFlag") + @Test + fun testDeviceServiceStateFromBroadcast_eagerlyWatchesBroadcast() = + testScope.runTest { + // Value starts out empty (null) + assertThat(underTest.deviceServiceState.value).isNull() + + // WHEN an appropriate intent gets sent out + val intent = serviceStateIntent(subId = -1, emergencyOnly = false) + fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly( + context, + intent, + ) + runCurrent() + + // THEN the repo's state is updated + val expected = ServiceStateModel(isEmergencyOnly = false) + assertThat(underTest.deviceServiceState.value).isEqualTo(expected) + } + + @Test + fun testDeviceServiceStateFromBroadcast_followsSubIdNegativeOne() = + testScope.runTest { + // device based state tracks -1 + val intent = serviceStateIntent(subId = -1, emergencyOnly = false) + fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly( + context, + intent, + ) + runCurrent() + + val deviceBasedState = ServiceStateModel(isEmergencyOnly = false) + assertThat(underTest.deviceServiceState.value).isEqualTo(deviceBasedState) + + // ... and ignores any other subId + val intent2 = serviceStateIntent(subId = 1, emergencyOnly = true) + fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly( + context, + intent2, + ) + runCurrent() + + assertThat(underTest.deviceServiceState.value).isEqualTo(deviceBasedState) + } + @Test @Ignore("b/333912012") fun testConnectionCache_clearsInvalidSubscriptions() = @@ -1491,5 +1540,24 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() { whenever(it.transportInfo).thenReturn(WIFI_INFO_ACTIVE) whenever(it.hasCapability(NET_CAPABILITY_VALIDATED)).thenReturn(true) } + + /** + * To properly mimic telephony manager, create a service state, and then turn it into an + * intent + */ + private fun serviceStateIntent( + subId: Int, + emergencyOnly: Boolean = false, + ): Intent { + val serviceState = ServiceState().apply { isEmergencyOnly = emergencyOnly } + + val bundle = Bundle() + serviceState.fillInNotifierBundle(bundle) + + return Intent(Intent.ACTION_SERVICE_STATE).apply { + putExtras(bundle) + putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId) + } + } } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt index 0f9cbfa66b5b..58d9ee3935fd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt @@ -28,6 +28,7 @@ import com.android.systemui.coroutines.collectLastValue import com.android.systemui.flags.FakeFeatureFlagsClassic import com.android.systemui.flags.Flags import com.android.systemui.log.table.TableLogBuffer +import com.android.systemui.statusbar.pipeline.mobile.data.model.ServiceStateModel import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository @@ -888,6 +889,22 @@ class MobileIconsInteractorTest : SysuiTestCase() { assertThat(interactor1).isSameInstanceAs(interactor2) } + @Test + fun deviceBasedEmergencyMode_emergencyCallsOnly_followsDeviceServiceStateFromRepo() = + testScope.runTest { + val latest by collectLastValue(underTest.isDeviceInEmergencyCallsOnlyMode) + + connectionsRepository.deviceServiceState.value = + ServiceStateModel(isEmergencyOnly = true) + + assertThat(latest).isTrue() + + connectionsRepository.deviceServiceState.value = + ServiceStateModel(isEmergencyOnly = false) + + assertThat(latest).isFalse() + } + /** * Convenience method for creating a pair of subscriptions to test the filteredSubscriptions * flow. diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt index 405e3ed807d5..d303976612c1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt @@ -22,6 +22,7 @@ import androidx.test.filters.SmallTest import com.android.internal.telephony.flags.Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.log.core.FakeLogBuffer import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconsInteractor import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy import com.android.systemui.statusbar.pipeline.satellite.data.prod.FakeDeviceBasedSatelliteRepository @@ -71,6 +72,7 @@ class DeviceBasedSatelliteInteractorTest : SysuiTestCase() { deviceProvisioningInteractor, wifiInteractor, testScope.backgroundScope, + FakeLogBuffer.Factory.create(), ) } @@ -114,6 +116,7 @@ class DeviceBasedSatelliteInteractorTest : SysuiTestCase() { deviceProvisioningInteractor, wifiInteractor, testScope.backgroundScope, + FakeLogBuffer.Factory.create(), ) val latest by collectLastValue(underTest.isSatelliteAllowed) @@ -162,6 +165,7 @@ class DeviceBasedSatelliteInteractorTest : SysuiTestCase() { deviceProvisioningInteractor, wifiInteractor, testScope.backgroundScope, + FakeLogBuffer.Factory.create(), ) val latest by collectLastValue(underTest.connectionState) @@ -218,6 +222,7 @@ class DeviceBasedSatelliteInteractorTest : SysuiTestCase() { deviceProvisioningInteractor, wifiInteractor, testScope.backgroundScope, + FakeLogBuffer.Factory.create(), ) val latest by collectLastValue(underTest.signalStrength) @@ -238,25 +243,97 @@ class DeviceBasedSatelliteInteractorTest : SysuiTestCase() { @Test @EnableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG) - fun areAllConnectionsOutOfService_noConnections_yes() = + fun areAllConnectionsOutOfService_noConnections_noDeviceEmergencyCalls_yes() = testScope.runTest { val latest by collectLastValue(underTest.areAllConnectionsOutOfService) // GIVEN, 0 connections + // GIVEN, device is not in emergency calls only mode + iconsInteractor.isDeviceInEmergencyCallsOnlyMode.value = false + // THEN the value is propagated to this interactor assertThat(latest).isTrue() } @Test @EnableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG) - fun areAllConnectionsOutOfService_twoConnectionsOos_nonNtn_yes() = + fun areAllConnectionsOutOfService_noConnections_deviceEmergencyCalls_yes() = + testScope.runTest { + val latest by collectLastValue(underTest.areAllConnectionsOutOfService) + + // GIVEN, 0 connections + + // GIVEN, device is in emergency calls only mode + iconsInteractor.isDeviceInEmergencyCallsOnlyMode.value = true + + // THEN the value is propagated to this interactor + assertThat(latest).isFalse() + } + + @Test + @EnableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG) + fun areAllConnectionsOutOfService_oneConnectionInService_thenLost_noDeviceEmergencyCalls_yes() = + testScope.runTest { + val latest by collectLastValue(underTest.areAllConnectionsOutOfService) + + // GIVEN, 1 connections + val i1 = iconsInteractor.getMobileConnectionInteractorForSubId(1) + // GIVEN, no device-based emergency calls + iconsInteractor.isDeviceInEmergencyCallsOnlyMode.value = false + + // WHEN connection is in service + i1.isInService.value = true + i1.isEmergencyOnly.value = false + i1.isNonTerrestrial.value = false + + // THEN we are considered NOT to be OOS + assertThat(latest).isFalse() + + // WHEN the connection disappears + iconsInteractor.icons.value = listOf() + + // THEN we are back to OOS + assertThat(latest).isTrue() + } + + @Test + @EnableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG) + fun areAllConnectionsOutOfService_oneConnectionInService_thenLost_deviceEmergencyCalls_no() = + testScope.runTest { + val latest by collectLastValue(underTest.areAllConnectionsOutOfService) + + // GIVEN, 1 connections + val i1 = iconsInteractor.getMobileConnectionInteractorForSubId(1) + // GIVEN, device-based emergency calls + iconsInteractor.isDeviceInEmergencyCallsOnlyMode.value = true + + // WHEN one connection is in service + i1.isInService.value = true + i1.isEmergencyOnly.value = false + i1.isNonTerrestrial.value = false + + // THEN we are considered NOT to be OOS + assertThat(latest).isFalse() + + // WHEN the connection disappears + iconsInteractor.icons.value = listOf() + + // THEN we are still NOT in OOS, due to device-based emergency calls + assertThat(latest).isFalse() + } + + @Test + @EnableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG) + fun areAllConnectionsOutOfService_twoConnectionsOos_nonNtn_noDeviceEmergencyCalls_yes() = testScope.runTest { val latest by collectLastValue(underTest.areAllConnectionsOutOfService) // GIVEN, 2 connections val i1 = iconsInteractor.getMobileConnectionInteractorForSubId(1) val i2 = iconsInteractor.getMobileConnectionInteractorForSubId(2) + // GIVEN, no device-based emergency calls + iconsInteractor.isDeviceInEmergencyCallsOnlyMode.value = false // WHEN all of the connections are OOS and none are NTN i1.isInService.value = false @@ -272,13 +349,39 @@ class DeviceBasedSatelliteInteractorTest : SysuiTestCase() { @Test @EnableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG) - fun areAllConnectionsOutOfService_twoConnectionsOos_oneNtn_no() = + fun areAllConnectionsOutOfService_twoConnectionsOos_nonNtn_deviceEmergencyCalls_no() = + testScope.runTest { + val latest by collectLastValue(underTest.areAllConnectionsOutOfService) + + // GIVEN, 2 connections + val i1 = iconsInteractor.getMobileConnectionInteractorForSubId(1) + val i2 = iconsInteractor.getMobileConnectionInteractorForSubId(2) + // GIVEN, device-based emergency calls + iconsInteractor.isDeviceInEmergencyCallsOnlyMode.value = true + + // WHEN all of the connections are OOS and none are NTN + i1.isInService.value = false + i1.isEmergencyOnly.value = false + i1.isNonTerrestrial.value = false + i2.isInService.value = false + i2.isEmergencyOnly.value = false + i2.isNonTerrestrial.value = false + + // THEN we are not considered OOS due to device based emergency calling + assertThat(latest).isFalse() + } + + @Test + @EnableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG) + fun areAllConnectionsOutOfService_twoConnectionsOos_noDeviceEmergencyCalls_oneNtn_no() = testScope.runTest { val latest by collectLastValue(underTest.areAllConnectionsOutOfService) // GIVEN, 2 connections val i1 = iconsInteractor.getMobileConnectionInteractorForSubId(1) val i2 = iconsInteractor.getMobileConnectionInteractorForSubId(2) + // GIVEN, no device-based emergency calls + iconsInteractor.isDeviceInEmergencyCallsOnlyMode.value = false // WHEN all of the connections are OOS and one is NTN i1.isInService.value = false @@ -296,12 +399,14 @@ class DeviceBasedSatelliteInteractorTest : SysuiTestCase() { @Test @EnableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG) - fun areAllConnectionsOutOfService_oneConnectionOos_nonNtn_yes() = + fun areAllConnectionsOutOfService_oneConnectionOos_noDeviceEmergencyCalls_nonNtn_yes() = testScope.runTest { val latest by collectLastValue(underTest.areAllConnectionsOutOfService) // GIVEN, 1 connection val i1 = iconsInteractor.getMobileConnectionInteractorForSubId(1) + // GIVEN, no device-based emergency calls + iconsInteractor.isDeviceInEmergencyCallsOnlyMode.value = false // WHEN all of the connections are OOS i1.isInService.value = false @@ -314,7 +419,27 @@ class DeviceBasedSatelliteInteractorTest : SysuiTestCase() { @Test @EnableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG) - fun areAllConnectionsOutOfService_oneConnectionOos_ntn_yes() = + fun areAllConnectionsOutOfService_oneConnectionOos_nonNtn_no() = + testScope.runTest { + val latest by collectLastValue(underTest.areAllConnectionsOutOfService) + + // GIVEN, 1 connection + val i1 = iconsInteractor.getMobileConnectionInteractorForSubId(1) + // GIVEN, device-based emergency calls + iconsInteractor.isDeviceInEmergencyCallsOnlyMode.value = true + + // WHEN all of the connections are OOS + i1.isInService.value = false + i1.isEmergencyOnly.value = false + i1.isNonTerrestrial.value = false + + // THEN the value is propagated to this interactor + assertThat(latest).isFalse() + } + + @Test + @EnableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG) + fun areAllConnectionsOutOfService_oneConnectionOos_ntn_no() = testScope.runTest { val latest by collectLastValue(underTest.areAllConnectionsOutOfService) @@ -416,6 +541,7 @@ class DeviceBasedSatelliteInteractorTest : SysuiTestCase() { deviceProvisioningInteractor, wifiInteractor, testScope.backgroundScope, + FakeLogBuffer.Factory.create(), ) val latest by collectLastValue(underTest.areAllConnectionsOutOfService) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt index ceaae9e02e87..43b95688729c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt @@ -75,6 +75,7 @@ class DeviceBasedSatelliteViewModelTest : SysuiTestCase() { deviceProvisioningInteractor, wifiInteractor, testScope.backgroundScope, + FakeLogBuffer.Factory.create(), ) underTest = diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapterTest.kt index d1c38f6cbea7..0a5e63085b9b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapterTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapterTest.kt @@ -22,6 +22,7 @@ import android.graphics.Bitmap import android.os.UserHandle import android.view.View import android.view.ViewGroup +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.qs.user.UserSwitchDialogController @@ -33,14 +34,13 @@ import java.lang.ref.WeakReference import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class BaseUserSwitcherAdapterTest : SysuiTestCase() { @Mock private lateinit var controller: UserSwitcherController diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImplTest.kt index fb4ccb52929a..c22c62825d04 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImplTest.kt @@ -20,8 +20,8 @@ import android.content.ComponentName import android.content.Context import android.content.pm.ServiceInfo import android.provider.Settings -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.res.R @@ -60,7 +60,7 @@ import org.mockito.ArgumentMatchers.anyInt import org.mockito.ArgumentMatchers.anyObject @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class DeviceControlsControllerImplTest : SysuiTestCase() { @Mock diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java index 2955162f80c2..f6e07d3d621e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java @@ -29,10 +29,10 @@ import android.hardware.devicestate.DeviceState; import android.hardware.devicestate.DeviceStateManager; import android.os.UserHandle; import android.provider.Settings; -import android.testing.AndroidTestingRunner; import android.testing.TestableContentResolver; import android.testing.TestableResources; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.R; @@ -51,7 +51,7 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @SmallTest public class DeviceStateRotationLockSettingControllerTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/FlashlightControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/FlashlightControllerImplTest.kt index 1c54263cb0ce..80cc6eca8405 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/FlashlightControllerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/FlashlightControllerImplTest.kt @@ -20,8 +20,8 @@ import android.content.pm.PackageManager import android.hardware.camera2.CameraCharacteristics import android.hardware.camera2.CameraManager import android.hardware.camera2.impl.CameraMetadataNative +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import android.testing.AndroidTestingRunner import com.android.systemui.SysuiTestCase import com.android.systemui.broadcast.BroadcastSender import com.android.systemui.dump.DumpManager @@ -46,7 +46,7 @@ import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class FlashlightControllerImplTest : SysuiTestCase() { @Mock diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherAdapterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherAdapterTest.kt index 0bd6a685708b..9f74915b4d1b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherAdapterTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherAdapterTest.kt @@ -19,10 +19,10 @@ package com.android.systemui.statusbar.policy import android.content.Context import android.content.pm.UserInfo import android.graphics.Bitmap -import android.testing.AndroidTestingRunner import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.util.UserIcons import com.android.systemui.res.R @@ -44,7 +44,7 @@ import org.mockito.Mockito.verify import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @SmallTest class KeyguardUserSwitcherAdapterTest : SysuiTestCase() { @Mock diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SafetyControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SafetyControllerTest.kt index b03edaf8ebd5..4b14e642063a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SafetyControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SafetyControllerTest.kt @@ -23,7 +23,7 @@ import android.content.pm.PackageManager import android.net.Uri import android.os.Handler import android.safetycenter.SafetyCenterManager -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.util.mockito.any @@ -44,7 +44,7 @@ import org.mockito.Mockito.verify import org.mockito.Mockito.`when` @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class SafetyControllerTest : SysuiTestCase() { private val TEST_PC_PKG = "testPermissionControllerPackageName" @@ -188,4 +188,4 @@ class SafetyControllerTest : SysuiTestCase() { assertThat(called).isTrue() } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerFlagDisabledTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerFlagDisabledTest.kt index 3e20f689569e..81f095041fbc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerFlagDisabledTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerFlagDisabledTest.kt @@ -22,7 +22,7 @@ import android.media.projection.MediaProjectionManager import android.os.Handler import android.platform.test.annotations.DisableFlags import android.telephony.TelephonyManager -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.server.notification.Flags import com.android.systemui.SysuiTestCase @@ -38,7 +38,7 @@ import org.mockito.Mockito.verifyZeroInteractions import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @DisableFlags(Flags.FLAG_SCREENSHARE_NOTIFICATION_HIDING) class SensitiveNotificationProtectionControllerFlagDisabledTest : SysuiTestCase() { private val logger = SensitiveNotificationProtectionControllerLogger(logcatLogBuffer()) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/WalletControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/WalletControllerImplTest.kt index dbc2e3471c28..0249ab8fc9eb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/WalletControllerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/WalletControllerImplTest.kt @@ -17,8 +17,8 @@ package com.android.systemui.statusbar.policy import android.service.quickaccesswallet.QuickAccessWalletClient -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase @@ -35,7 +35,7 @@ import org.mockito.MockitoAnnotations import org.mockito.Mockito.`when` @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class WalletControllerImplTest : SysuiTestCase() { @Mock diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java index aac36405e9b6..daea7b94ac4f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -27,6 +27,7 @@ import static android.service.notification.NotificationListenerService.REASON_GR import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.server.notification.Flags.FLAG_SCREENSHARE_NOTIFICATION_HIDING; +import static com.android.wm.shell.Flags.FLAG_ENABLE_BUBBLE_BAR; import static com.google.common.truth.Truth.assertThat; @@ -466,7 +467,8 @@ public class BubblesTest extends SysuiTestCase { mock(UiEventLogger.class), mock(UserTracker.class), mock(AvalancheProvider.class), - mock(SystemSettings.class) + mock(SystemSettings.class), + mock(PackageManager.class) ); interruptionDecisionProvider.start(); @@ -2141,6 +2143,112 @@ public class BubblesTest extends SysuiTestCase { assertThat(mBubbleController.getLayerView().isExpanded()).isFalse(); } + @EnableFlags(FLAG_ENABLE_BUBBLE_BAR) + @Test + public void dragBubbleBarBubble_selectedBubble_expandedViewCollapsesDuringDrag() { + mBubbleProperties.mIsBubbleBarEnabled = true; + mPositioner.setIsLargeScreen(true); + FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener(); + mBubbleController.registerBubbleStateListener(bubbleStateListener); + + // Add 2 bubbles + mEntryListener.onEntryAdded(mRow); + mEntryListener.onEntryAdded(mRow2); + mBubbleController.updateBubble(mBubbleEntry); + mBubbleController.updateBubble(mBubbleEntry2); + + // Select first bubble + mBubbleController.expandStackAndSelectBubbleFromLauncher(mBubbleEntry.getKey(), new Rect()); + assertThat(mBubbleData.getSelectedBubbleKey()).isEqualTo(mBubbleEntry.getKey()); + assertThat(mBubbleController.getLayerView().isExpanded()).isTrue(); + + // Drag first bubble, bubble should collapse + mBubbleController.startBubbleDrag(mBubbleEntry.getKey()); + assertThat(mBubbleController.getLayerView().isExpanded()).isFalse(); + + // Stop dragging, first bubble should be expanded + mBubbleController.stopBubbleDrag(BubbleBarLocation.LEFT); + assertThat(mBubbleData.getSelectedBubbleKey()).isEqualTo(mBubbleEntry.getKey()); + assertThat(mBubbleController.getLayerView().isExpanded()).isTrue(); + } + + @EnableFlags(FLAG_ENABLE_BUBBLE_BAR) + @Test + public void dragBubbleBarBubble_unselectedBubble_expandedViewCollapsesDuringDrag() { + mBubbleProperties.mIsBubbleBarEnabled = true; + mPositioner.setIsLargeScreen(true); + FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener(); + mBubbleController.registerBubbleStateListener(bubbleStateListener); + + // Add 2 bubbles + mEntryListener.onEntryAdded(mRow); + mEntryListener.onEntryAdded(mRow2); + mBubbleController.updateBubble(mBubbleEntry); + mBubbleController.updateBubble(mBubbleEntry2); + + // Select first bubble + mBubbleController.expandStackAndSelectBubbleFromLauncher(mBubbleEntry.getKey(), new Rect()); + assertThat(mBubbleData.getSelectedBubbleKey()).isEqualTo(mBubbleEntry.getKey()); + assertThat(mBubbleController.getLayerView().isExpanded()).isTrue(); + + // Drag second bubble, bubble should collapse + mBubbleController.startBubbleDrag(mBubbleEntry2.getKey()); + assertThat(mBubbleController.getLayerView().isExpanded()).isFalse(); + + // Stop dragging, first bubble should be expanded + mBubbleController.stopBubbleDrag(BubbleBarLocation.LEFT); + assertThat(mBubbleData.getSelectedBubbleKey()).isEqualTo(mBubbleEntry.getKey()); + assertThat(mBubbleController.getLayerView().isExpanded()).isTrue(); + } + + @EnableFlags(FLAG_ENABLE_BUBBLE_BAR) + @Test + public void dismissBubbleBarBubble_selected_selectsAndExpandsNext() { + mBubbleProperties.mIsBubbleBarEnabled = true; + mPositioner.setIsLargeScreen(true); + FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener(); + mBubbleController.registerBubbleStateListener(bubbleStateListener); + + // Add 2 bubbles + mEntryListener.onEntryAdded(mRow); + mEntryListener.onEntryAdded(mRow2); + mBubbleController.updateBubble(mBubbleEntry); + mBubbleController.updateBubble(mBubbleEntry2); + + // Select first bubble + mBubbleController.expandStackAndSelectBubbleFromLauncher(mBubbleEntry.getKey(), new Rect()); + // Drag first bubble to dismiss + mBubbleController.startBubbleDrag(mBubbleEntry.getKey()); + mBubbleController.dragBubbleToDismiss(mBubbleEntry.getKey()); + // Second bubble is selected and expanded + assertThat(mBubbleData.getSelectedBubbleKey()).isEqualTo(mBubbleEntry2.getKey()); + assertThat(mBubbleController.getLayerView().isExpanded()).isTrue(); + } + + @EnableFlags(FLAG_ENABLE_BUBBLE_BAR) + @Test + public void dismissBubbleBarBubble_unselected_selectionDoesNotChange() { + mBubbleProperties.mIsBubbleBarEnabled = true; + mPositioner.setIsLargeScreen(true); + FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener(); + mBubbleController.registerBubbleStateListener(bubbleStateListener); + + // Add 2 bubbles + mEntryListener.onEntryAdded(mRow); + mEntryListener.onEntryAdded(mRow2); + mBubbleController.updateBubble(mBubbleEntry); + mBubbleController.updateBubble(mBubbleEntry2); + + // Select first bubble + mBubbleController.expandStackAndSelectBubbleFromLauncher(mBubbleEntry.getKey(), new Rect()); + // Drag second bubble to dismiss + mBubbleController.startBubbleDrag(mBubbleEntry2.getKey()); + mBubbleController.dragBubbleToDismiss(mBubbleEntry2.getKey()); + // First bubble remains selected and expanded + assertThat(mBubbleData.getSelectedBubbleKey()).isEqualTo(mBubbleEntry.getKey()); + assertThat(mBubbleController.getLayerView().isExpanded()).isTrue(); + } + @DisableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING) @Test public void doesNotRegisterSensitiveStateListener() { diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java index 9dcd94687668..8eef930cbb0c 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java @@ -15,8 +15,6 @@ */ package com.android.systemui; -import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT; - import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; @@ -99,8 +97,22 @@ public abstract class SysuiTestCase { .setProvideMainThread(true) .build(); - @Rule - public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT); + @ClassRule + public static final SetFlagsRule.ClassRule mSetFlagsClassRule = + new SetFlagsRule.ClassRule( + android.app.Flags.class, + android.hardware.biometrics.Flags.class, + android.multiuser.Flags.class, + android.net.platform.flags.Flags.class, + android.os.Flags.class, + android.service.controls.flags.Flags.class, + com.android.internal.telephony.flags.Flags.class, + com.android.server.notification.Flags.class, + com.android.systemui.Flags.class); + + // TODO(b/339471826): Fix Robolectric to execute the @ClassRule correctly + @Rule public final SetFlagsRule mSetFlagsRule = + isRobolectricTest() ? new SetFlagsRule() : mSetFlagsClassRule.createSetFlagsRule(); @Rule(order = 10) public final SceneContainerRule mSceneContainerRule = new SceneContainerRule(); diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/data/repository/FakeBrightnessPolicyRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/data/repository/FakeBrightnessPolicyRepository.kt index d3ceb15ca5a2..f5d02f328336 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/data/repository/FakeBrightnessPolicyRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/data/repository/FakeBrightnessPolicyRepository.kt @@ -38,4 +38,8 @@ class FakeBrightnessPolicyRepository : BrightnessPolicyRepository { ) ) } + + fun setBaseUserRestriction() { + _restrictionPolicy.value = PolicyRestriction.Restricted(RestrictedLockUtils.EnforcedAdmin()) + } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qrcodescanner/QRCodeScannerControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qrcodescanner/QRCodeScannerControllerKosmos.kt new file mode 100644 index 000000000000..8ad6087cfe82 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qrcodescanner/QRCodeScannerControllerKosmos.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qrcodescanner + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.qrcodescanner.controller.QRCodeScannerController +import com.android.systemui.util.mockito.mock + +val Kosmos.qrCodeScannerController by Kosmos.Fixture { mock<QRCodeScannerController>() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/FakeQSTileIntentUserInputHandler.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/FakeQSTileIntentUserInputHandler.kt index c4bf8ff12817..f50443ec4e86 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/FakeQSTileIntentUserInputHandler.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/FakeQSTileIntentUserInputHandler.kt @@ -31,7 +31,11 @@ class FakeQSTileIntentUserInputHandler : QSTileIntentUserInputHandler { private val mutableInputs = mutableListOf<Input>() - override fun handle(expandable: Expandable?, intent: Intent) { + override fun handle( + expandable: Expandable?, + intent: Intent, + handleDismissShadeShowOverLockScreenWhenLocked: Boolean + ) { mutableInputs.add(Input.Intent(expandable, intent)) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerKosmos.kt new file mode 100644 index 000000000000..ccfb6092a2e3 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerKosmos.kt @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.base.actions + +import com.android.systemui.kosmos.Kosmos + +val Kosmos.qsTileIntentUserInputHandler by Kosmos.Fixture { FakeQSTileIntentUserInputHandler() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsKosmos.kt new file mode 100644 index 000000000000..146c1ad6ab70 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.base.analytics + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.util.mockito.mock + +val Kosmos.qsTileAnalytics by Kosmos.Fixture { mock<QSTileAnalytics>() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorKosmos.kt new file mode 100644 index 000000000000..9ad49f052c9e --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorKosmos.kt @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.base.interactor + +import com.android.systemui.kosmos.Kosmos + +val Kosmos.fakeDisabledByPolicyInteractor by Kosmos.Fixture { FakeDisabledByPolicyInteractor() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/qr/QRCodeScannerTileKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/qr/QRCodeScannerTileKosmos.kt new file mode 100644 index 000000000000..dcfcce77942e --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/qr/QRCodeScannerTileKosmos.kt @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.impl.qr + +import android.content.res.mainResources +import com.android.systemui.classifier.fakeFalsingManager +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.kosmos.backgroundCoroutineContext +import com.android.systemui.kosmos.testDispatcher +import com.android.systemui.kosmos.testScope +import com.android.systemui.qrcodescanner.dagger.QRCodeScannerModule +import com.android.systemui.qrcodescanner.qrCodeScannerController +import com.android.systemui.qs.qsEventLogger +import com.android.systemui.qs.tiles.base.actions.qsTileIntentUserInputHandler +import com.android.systemui.qs.tiles.base.analytics.qsTileAnalytics +import com.android.systemui.qs.tiles.base.interactor.fakeDisabledByPolicyInteractor +import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelImpl +import com.android.systemui.qs.tiles.impl.custom.qsTileLogger +import com.android.systemui.qs.tiles.impl.qr.domain.interactor.QRCodeScannerTileDataInteractor +import com.android.systemui.qs.tiles.impl.qr.domain.interactor.QRCodeScannerTileUserActionInteractor +import com.android.systemui.qs.tiles.impl.qr.ui.QRCodeScannerTileMapper +import com.android.systemui.user.data.repository.fakeUserRepository +import com.android.systemui.util.time.systemClock + +val Kosmos.qsQRCodeScannerTileConfig by + Kosmos.Fixture { QRCodeScannerModule.provideQRCodeScannerTileConfig(qsEventLogger) } + +val Kosmos.qrCodeScannerTileDataInteractor by + Kosmos.Fixture { + QRCodeScannerTileDataInteractor( + backgroundCoroutineContext, + applicationCoroutineScope, + qrCodeScannerController + ) + } + +val Kosmos.qrCodeScannerTileUserActionInteractor by + Kosmos.Fixture { QRCodeScannerTileUserActionInteractor(qsTileIntentUserInputHandler) } + +val Kosmos.qrCodeScannerTileMapper by + Kosmos.Fixture { QRCodeScannerTileMapper(mainResources, mainResources.newTheme()) } + +val Kosmos.qsQRCodeScannerViewModel by + Kosmos.Fixture { + QSTileViewModelImpl( + qsQRCodeScannerTileConfig, + { qrCodeScannerTileUserActionInteractor }, + { qrCodeScannerTileDataInteractor }, + { qrCodeScannerTileMapper }, + fakeDisabledByPolicyInteractor, + fakeUserRepository, + fakeFalsingManager, + qsTileAnalytics, + qsTileLogger, + systemClock, + testDispatcher, + testScope.backgroundScope, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt index cce038f4ffc1..8229575a128f 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt @@ -23,6 +23,7 @@ import com.android.settingslib.SignalIcon import com.android.settingslib.mobile.MobileMappings import com.android.settingslib.mobile.TelephonyIcons import com.android.systemui.log.table.TableLogBuffer +import com.android.systemui.statusbar.pipeline.mobile.data.model.ServiceStateModel import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy @@ -93,6 +94,8 @@ class FakeMobileConnectionsRepository( private val _defaultMobileIconGroup = MutableStateFlow(DEFAULT_ICON) override val defaultMobileIconGroup = _defaultMobileIconGroup + override val deviceServiceState = MutableStateFlow<ServiceStateModel?>(null) + override val isAnySimSecure = MutableStateFlow(false) override fun getIsAnySimSecure(): Boolean = isAnySimSecure.value diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt index de6c87c2b515..3a4bf8e48d33 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt @@ -81,6 +81,8 @@ class FakeMobileIconsInteractor( override val isForceHidden = MutableStateFlow(false) + override val isDeviceInEmergencyCallsOnlyMode = MutableStateFlow(false) + /** Always returns a new fake interactor */ override fun getMobileConnectionInteractorForSubId(subId: Int): FakeMobileIconInteractor { return FakeMobileIconInteractor(tableLogBuffer).also { diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig index 2b18b7d1cff2..a50fb9a4c318 100644 --- a/services/accessibility/accessibility.aconfig +++ b/services/accessibility/accessibility.aconfig @@ -138,6 +138,16 @@ flag { } flag { + name: "manager_package_monitor_logic_fix" + namespace: "accessibility" + description: "Corrects the return values of the HandleForceStop function" + bug: "337392123" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "pinch_zoom_zero_min_span" namespace: "accessibility" description: "Whether to set min span of ScaleGestureDetector to zero." @@ -152,6 +162,16 @@ flag { } flag { + name: "remove_on_window_infos_changed_handler" + namespace: "accessibility" + description: "Updates onWindowInfosChanged() to run without posting to a handler." + bug: "333834990" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "reset_hover_event_timer_on_action_up" namespace: "accessibility" description: "Reset the timer for sending hover events on receiving ACTION_UP to guarantee the correct amount of time is available between taps." diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index c70b6419e3b8..a15d2ca57788 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -697,7 +697,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub /** * Returns the lock object for any synchronized test blocks. - * Should not be used outside of testing. + * External classes should only use for testing. * @return lock object. */ @VisibleForTesting @@ -801,7 +801,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub * * @param packages list of packages that have stopped. * @param userState user state to be read & modified. - * @return {@code true} if a service was enabled or a button target was removed, + * @return {@code true} if the lists of enabled services or buttons were changed, * {@code false} otherwise. */ @VisibleForTesting @@ -824,6 +824,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub userState.getBindingServicesLocked().remove(comp); userState.getCrashedServicesLocked().remove(comp); enabledServicesChanged = true; + break; } } } @@ -851,132 +852,14 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return mPackageMonitor; } - private void registerBroadcastReceivers() { - mPackageMonitor = new PackageMonitor(true) { - @Override - public void onSomePackagesChanged() { - if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_PACKAGE_BROADCAST_RECEIVER)) { - mTraceManager.logTrace(LOG_TAG + ".PM.onSomePackagesChanged", - FLAGS_PACKAGE_BROADCAST_RECEIVER); - } - - final int userId = getChangingUserId(); - List<AccessibilityServiceInfo> parsedAccessibilityServiceInfos = null; - List<AccessibilityShortcutInfo> parsedAccessibilityShortcutInfos = null; - parsedAccessibilityServiceInfos = parseAccessibilityServiceInfos(userId); - parsedAccessibilityShortcutInfos = parseAccessibilityShortcutInfos(userId); - synchronized (mLock) { - // Only the profile parent can install accessibility services. - // Therefore we ignore packages from linked profiles. - if (userId != mCurrentUserId) { - return; - } - onSomePackagesChangedLocked(parsedAccessibilityServiceInfos, - parsedAccessibilityShortcutInfos); - } - } - - @Override - public void onPackageUpdateFinished(String packageName, int uid) { - // The package should already be removed from mBoundServices, and added into - // mBindingServices in binderDied() during updating. Remove services from this - // package from mBindingServices, and then update the user state to re-bind new - // versions of them. - if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_PACKAGE_BROADCAST_RECEIVER)) { - mTraceManager.logTrace(LOG_TAG + ".PM.onPackageUpdateFinished", - FLAGS_PACKAGE_BROADCAST_RECEIVER, - "packageName=" + packageName + ";uid=" + uid); - } - final int userId = getChangingUserId(); - List<AccessibilityServiceInfo> parsedAccessibilityServiceInfos = null; - List<AccessibilityShortcutInfo> parsedAccessibilityShortcutInfos = null; - parsedAccessibilityServiceInfos = parseAccessibilityServiceInfos(userId); - parsedAccessibilityShortcutInfos = parseAccessibilityShortcutInfos(userId); - synchronized (mLock) { - if (userId != mCurrentUserId) { - return; - } - final AccessibilityUserState userState = getUserStateLocked(userId); - final boolean reboundAService = userState.getBindingServicesLocked().removeIf( - component -> component != null - && component.getPackageName().equals(packageName)) - || userState.mCrashedServices.removeIf(component -> component != null - && component.getPackageName().equals(packageName)); - // Reloads the installed services info to make sure the rebound service could - // get a new one. - userState.mInstalledServices.clear(); - final boolean configurationChanged; - configurationChanged = readConfigurationForUserStateLocked(userState, - parsedAccessibilityServiceInfos, parsedAccessibilityShortcutInfos); - if (reboundAService || configurationChanged) { - onUserStateChangedLocked(userState); - } - // Passing 0 for restoreFromSdkInt to have this migration check execute each - // time. It can make sure a11y button settings are correctly if there's an a11y - // service updated and modifies the a11y button configuration. - migrateAccessibilityButtonSettingsIfNecessaryLocked(userState, packageName, - /* restoreFromSdkInt = */0); - } - } - - @Override - public void onPackageRemoved(String packageName, int uid) { - if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_PACKAGE_BROADCAST_RECEIVER)) { - mTraceManager.logTrace(LOG_TAG + ".PM.onPackageRemoved", - FLAGS_PACKAGE_BROADCAST_RECEIVER, - "packageName=" + packageName + ";uid=" + uid); - } - - synchronized (mLock) { - final int userId = getChangingUserId(); - // Only the profile parent can install accessibility services. - // Therefore we ignore packages from linked profiles. - if (userId != mCurrentUserId) { - return; - } - onPackageRemovedLocked(packageName); - } - } - - /** - * Handles instances in which a package or packages have forcibly stopped. - * - * @param intent intent containing package event information. - * @param uid linux process user id (different from Android user id). - * @param packages array of package names that have stopped. - * @param doit whether to try and handle the stop or just log the trace. - * - * @return {@code true} if package should be restarted, {@code false} otherwise. - */ - @Override - public boolean onHandleForceStop(Intent intent, String[] packages, - int uid, boolean doit) { - if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_PACKAGE_BROADCAST_RECEIVER)) { - mTraceManager.logTrace(LOG_TAG + ".PM.onHandleForceStop", - FLAGS_PACKAGE_BROADCAST_RECEIVER, - "intent=" + intent + ";packages=" + Arrays.toString(packages) - + ";uid=" + uid + ";doit=" + doit); - } - synchronized (mLock) { - final int userId = getChangingUserId(); - // Only the profile parent can install accessibility services. - // Therefore we ignore packages from linked profiles. - if (userId != mCurrentUserId) { - return false; - } - final AccessibilityUserState userState = getUserStateLocked(userId); - - if (doit && onPackagesForceStoppedLocked(packages, userState)) { - onUserStateChangedLocked(userState); - return false; - } else { - return true; - } - } - } - }; + @VisibleForTesting + void setPackageMonitor(PackageMonitor monitor) { + mPackageMonitor = monitor; + } + private void registerBroadcastReceivers() { // package changes + mPackageMonitor = new ManagerPackageMonitor(this); mPackageMonitor.register(mContext, null, UserHandle.ALL, true); // user change and unlock @@ -992,7 +875,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub @Override public void onReceive(Context context, Intent intent) { if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_USER_BROADCAST_RECEIVER)) { - mTraceManager.logTrace(LOG_TAG + ".BR.onReceive", FLAGS_USER_BROADCAST_RECEIVER, + mTraceManager.logTrace( + LOG_TAG + ".BR.onReceive", + FLAGS_USER_BROADCAST_RECEIVER, "context=" + context + ";intent=" + intent); } @@ -1045,7 +930,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub setNonA11yToolNotificationToMatchSafetyCenter(); } }; - mContext.registerReceiverAsUser(receiver, UserHandle.ALL, filter, null, mMainHandler, + mContext.registerReceiverAsUser( + receiver, UserHandle.ALL, filter, null, mMainHandler, Context.RECEIVER_EXPORTED); if (!android.companion.virtual.flags.Flags.vdmPublicApis()) { @@ -4371,7 +4257,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub ); if (!targetWithNoTile.isEmpty()) { - throw new IllegalArgumentException( + Slog.e(LOG_TAG, "Unable to add/remove Tiles for a11y features: " + targetWithNoTile + "as the Tiles aren't provided"); } @@ -6223,6 +6109,162 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } + @VisibleForTesting + public static class ManagerPackageMonitor extends PackageMonitor { + private final AccessibilityManagerService mManagerService; + public ManagerPackageMonitor(AccessibilityManagerService managerService) { + super(/* supportsPackageRestartQuery = */ true); + mManagerService = managerService; + } + + @Override + public void onSomePackagesChanged() { + if (mManagerService.mTraceManager.isA11yTracingEnabledForTypes( + FLAGS_PACKAGE_BROADCAST_RECEIVER)) { + mManagerService.mTraceManager.logTrace(LOG_TAG + ".PM.onSomePackagesChanged", + FLAGS_PACKAGE_BROADCAST_RECEIVER); + } + + final int userId = getChangingUserId(); + List<AccessibilityServiceInfo> parsedAccessibilityServiceInfos = mManagerService + .parseAccessibilityServiceInfos(userId); + List<AccessibilityShortcutInfo> parsedAccessibilityShortcutInfos = mManagerService + .parseAccessibilityShortcutInfos(userId); + synchronized (mManagerService.getLock()) { + // Only the profile parent can install accessibility services. + // Therefore we ignore packages from linked profiles. + if (userId != mManagerService.getCurrentUserIdLocked()) { + return; + } + mManagerService.onSomePackagesChangedLocked(parsedAccessibilityServiceInfos, + parsedAccessibilityShortcutInfos); + } + } + + @Override + public void onPackageUpdateFinished(String packageName, int uid) { + // The package should already be removed from mBoundServices, and added into + // mBindingServices in binderDied() during updating. Remove services from this + // package from mBindingServices, and then update the user state to re-bind new + // versions of them. + if (mManagerService.mTraceManager.isA11yTracingEnabledForTypes( + FLAGS_PACKAGE_BROADCAST_RECEIVER)) { + mManagerService.mTraceManager.logTrace( + LOG_TAG + ".PM.onPackageUpdateFinished", + FLAGS_PACKAGE_BROADCAST_RECEIVER, + "packageName=" + packageName + ";uid=" + uid); + } + final int userId = getChangingUserId(); + List<AccessibilityServiceInfo> parsedAccessibilityServiceInfos = mManagerService + .parseAccessibilityServiceInfos(userId); + List<AccessibilityShortcutInfo> parsedAccessibilityShortcutInfos = + mManagerService.parseAccessibilityShortcutInfos(userId); + synchronized (mManagerService.getLock()) { + if (userId != mManagerService.getCurrentUserIdLocked()) { + return; + } + final AccessibilityUserState userState = mManagerService.getUserStateLocked(userId); + final boolean reboundAService = userState.getBindingServicesLocked().removeIf( + component -> component != null + && component.getPackageName().equals(packageName)) + || userState.mCrashedServices.removeIf(component -> component != null + && component.getPackageName().equals(packageName)); + // Reloads the installed services info to make sure the rebound service could + // get a new one. + userState.mInstalledServices.clear(); + final boolean configurationChanged; + configurationChanged = mManagerService.readConfigurationForUserStateLocked( + userState, parsedAccessibilityServiceInfos, + parsedAccessibilityShortcutInfos); + if (reboundAService || configurationChanged) { + mManagerService.onUserStateChangedLocked(userState); + } + // Passing 0 for restoreFromSdkInt to have this migration check execute each + // time. It can make sure a11y button settings are correctly if there's an a11y + // service updated and modifies the a11y button configuration. + mManagerService.migrateAccessibilityButtonSettingsIfNecessaryLocked( + userState, packageName, /* restoreFromSdkInt = */0); + } + } + + @Override + public void onPackageRemoved(String packageName, int uid) { + if (mManagerService.mTraceManager.isA11yTracingEnabledForTypes( + FLAGS_PACKAGE_BROADCAST_RECEIVER)) { + mManagerService.mTraceManager.logTrace(LOG_TAG + ".PM.onPackageRemoved", + FLAGS_PACKAGE_BROADCAST_RECEIVER, + "packageName=" + packageName + ";uid=" + uid); + } + + synchronized (mManagerService.getLock()) { + final int userId = getChangingUserId(); + // Only the profile parent can install accessibility services. + // Therefore we ignore packages from linked profiles. + if (userId != mManagerService.getCurrentUserIdLocked()) { + return; + } + mManagerService.onPackageRemovedLocked(packageName); + } + } + + /** + * Handles instances in which a package or packages have forcibly stopped. + * + * @param intent intent containing package event information. + * @param uid linux process user id (different from Android user id). + * @param packages array of package names that have stopped. + * @param doit whether to try and handle the stop or just log the trace. + * + * @return {@code true} if doit == {@code false} + * and at least one of the provided packages is enabled. + * In any other case, returns {@code false}. + * This is to indicate whether further action is necessary. + */ + @Override + public boolean onHandleForceStop(Intent intent, String[] packages, + int uid, boolean doit) { + if (mManagerService.mTraceManager.isA11yTracingEnabledForTypes( + FLAGS_PACKAGE_BROADCAST_RECEIVER)) { + mManagerService.mTraceManager.logTrace(LOG_TAG + ".PM.onHandleForceStop", + FLAGS_PACKAGE_BROADCAST_RECEIVER, + "intent=" + intent + ";packages=" + Arrays.toString(packages) + + ";uid=" + uid + ";doit=" + doit); + } + synchronized (mManagerService.getLock()) { + final int userId = getChangingUserId(); + // Only the profile parent can install accessibility services. + // Therefore we ignore packages from linked profiles. + if (userId != mManagerService.getCurrentUserIdLocked()) { + return false; + } + final AccessibilityUserState userState = mManagerService.getUserStateLocked(userId); + + if (Flags.managerPackageMonitorLogicFix()) { + if (!doit) { + // if we're not handling the stop here, then we only need to know + // if any of the force-stopped packages are currently enabled. + return userState.mEnabledServices.stream().anyMatch( + (comp) -> Arrays.stream(packages).anyMatch( + (pkg) -> pkg.equals(comp.getPackageName())) + ); + } else if (mManagerService.onPackagesForceStoppedLocked(packages, userState)) { + mManagerService.onUserStateChangedLocked(userState); + } + return false; + } else { + // this old logic did not properly indicate when base packageMonitor's routine + // should handle stopping the package. + if (doit && mManagerService.onPackagesForceStoppedLocked(packages, userState)) { + mManagerService.onUserStateChangedLocked(userState); + return false; + } else { + return true; + } + } + } + } + } + void sendPendingWindowStateChangedEventsForAvailableWindowLocked(int windowId) { final int eventSize = mSendWindowStateChangedEventRunnables.size(); for (int i = eventSize - 1; i >= 0; i--) { diff --git a/services/companion/java/com/android/server/companion/virtual/TEST_MAPPING b/services/companion/java/com/android/server/companion/virtual/TEST_MAPPING index 340bc327fc7f..caa877c2b964 100644 --- a/services/companion/java/com/android/server/companion/virtual/TEST_MAPPING +++ b/services/companion/java/com/android/server/companion/virtual/TEST_MAPPING @@ -81,9 +81,6 @@ "name": "CtsPermissionTestCases", "options": [ { - "include-filter": "android.permissionmultidevice.cts.DeviceAwarePermissionGrantTest" - }, - { "include-filter": "android.permission.cts.DevicePermissionsTest" }, { @@ -93,6 +90,14 @@ "exclude-annotation": "androidx.test.filters.FlakyTest" } ] + }, + { + "name": "CtsPermissionMultiDeviceTestCases", + "options": [ + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + } + ] } ] } diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java index a61925732256..c1d59db1f0c7 100644 --- a/services/core/java/com/android/server/PackageWatchdog.java +++ b/services/core/java/com/android/server/PackageWatchdog.java @@ -138,6 +138,11 @@ public class PackageWatchdog { static final long DEFAULT_BOOT_LOOP_TRIGGER_WINDOW_MS = TimeUnit.MINUTES.toMillis(10); + // Time needed to apply mitigation + private static final String MITIGATION_WINDOW_MS = + "persist.device_config.configuration.mitigation_window_ms"; + private static final long DEFAULT_MITIGATION_WINDOW_MS = TimeUnit.SECONDS.toMillis(5); + // Threshold level at which or above user might experience significant disruption. private static final String MAJOR_USER_IMPACT_LEVEL_THRESHOLD = "persist.device_config.configuration.major_user_impact_level_threshold"; @@ -210,6 +215,9 @@ public class PackageWatchdog { @GuardedBy("mLock") private boolean mSyncRequired = false; + @GuardedBy("mLock") + private long mLastMitigation = -1000000; + @FunctionalInterface @VisibleForTesting interface SystemClock { @@ -400,6 +408,14 @@ public class PackageWatchdog { Slog.w(TAG, "Could not resolve a list of failing packages"); return; } + synchronized (mLock) { + final long now = mSystemClock.uptimeMillis(); + if (now >= mLastMitigation + && (now - mLastMitigation) < getMitigationWindowMs()) { + Slog.i(TAG, "Skipping onPackageFailure mitigation"); + return; + } + } mLongTaskHandler.post(() -> { synchronized (mLock) { if (mAllObservers.isEmpty()) { @@ -500,10 +516,17 @@ public class PackageWatchdog { int currentObserverImpact, int mitigationCount) { if (currentObserverImpact < getUserImpactLevelLimit()) { + synchronized (mLock) { + mLastMitigation = mSystemClock.uptimeMillis(); + } currentObserverToNotify.execute(versionedPackage, failureReason, mitigationCount); } } + private long getMitigationWindowMs() { + return SystemProperties.getLong(MITIGATION_WINDOW_MS, DEFAULT_MITIGATION_WINDOW_MS); + } + /** * Called when the system server boots. If the system server is detected to be in a boot loop, diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java index a508ebfdb950..8c1bb3b0cb71 100644 --- a/services/core/java/com/android/server/SystemConfig.java +++ b/services/core/java/com/android/server/SystemConfig.java @@ -1783,7 +1783,13 @@ public class SystemConfig { String gidStr = parser.getAttributeValue(null, "gid"); if (gidStr != null) { int gid = Process.getGidForName(gidStr); - perm.gids = appendInt(perm.gids, gid); + if (gid != -1) { + perm.gids = appendInt(perm.gids, gid); + } else { + Slog.w(TAG, "<group> with unknown gid \"" + + gidStr + " for permission " + name + " in " + + parser.getPositionDescription()); + } } else { Slog.w(TAG, "<group> without gid at " + parser.getPositionDescription()); diff --git a/services/core/java/com/android/server/TEST_MAPPING b/services/core/java/com/android/server/TEST_MAPPING index 04e85c7f117a..a3b6d806c6ce 100644 --- a/services/core/java/com/android/server/TEST_MAPPING +++ b/services/core/java/com/android/server/TEST_MAPPING @@ -134,6 +134,13 @@ }, { "name": "CtsSuspendAppsTestCases" + }, + { + "name": "CtsWindowManagerBackgroundActivityTestCases", + "file_patterns": [ + "Background.*\\.java", + "Activity.*\\.java" + ] } ], "presubmit-large": [ @@ -189,18 +196,16 @@ "name": "SelinuxFrameworksTests" }, { - "name": "CtsWindowManagerBackgroundActivityTestCases", - "file_patterns": [ - "Background.*\\.java", - "Activity.*\\.java" - ] - }, - { "name": "WmTests", "file_patterns": [ "Background.*\\.java", "Activity.*\\.java" + ], + "options": [ + { + "include-filter": "com.android.server.wm.BackgroundActivityStart*" + } ] } - ] + ] } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 00d8efa764ce..316937c8280a 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -19975,6 +19975,26 @@ public class ActivityManagerService extends IActivityManager.Stub addStartInfoTimestampInternal(key, timestampNs, userId, uid); } + + @Override + public void killApplicationSync(String pkgName, int appId, int userId, + String reason, int exitInfoReason) { + if (pkgName == null) { + return; + } + // Make sure the uid is valid. + if (appId < 0) { + Slog.w(TAG, "Invalid appid specified for pkg : " + pkgName); + return; + } + synchronized (ActivityManagerService.this) { + ActivityManagerService.this.forceStopPackageLocked(pkgName, appId, + /* callerWillRestart= */ false, /*purgeCache= */ false, + /* doit= */ true, /* evenPersistent= */ false, + /* uninstalling= */ false, /* packageStateStopped= */ false, + userId, reason, exitInfoReason); + } + } } long inputDispatchingTimedOut(int pid, final boolean aboveSystem, TimeoutRecord timeoutRecord) { diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 4f841497b201..58732fd200d2 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -159,6 +159,8 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * All information we are collecting about things that can happen that impact @@ -409,26 +411,14 @@ public final class BatteryStatsService extends IBatteryStats.Stub com.android.internal.R.bool.config_batteryStatsResetOnUnplugHighBatteryLevel); final boolean resetOnUnplugAfterSignificantCharge = context.getResources().getBoolean( com.android.internal.R.bool.config_batteryStatsResetOnUnplugAfterSignificantCharge); - final long powerStatsThrottlePeriodCpu = context.getResources().getInteger( - com.android.internal.R.integer.config_defaultPowerStatsThrottlePeriodCpu); - final long powerStatsThrottlePeriodMobileRadio = context.getResources().getInteger( - com.android.internal.R.integer.config_defaultPowerStatsThrottlePeriodMobileRadio); - final long powerStatsThrottlePeriodWifi = context.getResources().getInteger( - com.android.internal.R.integer.config_defaultPowerStatsThrottlePeriodWifi); - mBatteryStatsConfig = + BatteryStatsImpl.BatteryStatsConfig.Builder batteryStatsConfigBuilder = new BatteryStatsImpl.BatteryStatsConfig.Builder() .setResetOnUnplugHighBatteryLevel(resetOnUnplugHighBatteryLevel) - .setResetOnUnplugAfterSignificantCharge(resetOnUnplugAfterSignificantCharge) - .setPowerStatsThrottlePeriodMillis( - BatteryConsumer.POWER_COMPONENT_CPU, - powerStatsThrottlePeriodCpu) - .setPowerStatsThrottlePeriodMillis( - BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO, - powerStatsThrottlePeriodMobileRadio) - .setPowerStatsThrottlePeriodMillis( - BatteryConsumer.POWER_COMPONENT_WIFI, - powerStatsThrottlePeriodWifi) - .build(); + .setResetOnUnplugAfterSignificantCharge( + resetOnUnplugAfterSignificantCharge); + setPowerStatsThrottlePeriods(batteryStatsConfigBuilder, context.getResources().getString( + com.android.internal.R.string.config_powerStatsThrottlePeriods)); + mBatteryStatsConfig = batteryStatsConfigBuilder.build(); mPowerStatsUidResolver = new PowerStatsUidResolver(); mStats = new BatteryStatsImpl(mBatteryStatsConfig, Clock.SYSTEM_CLOCK, mMonotonicClock, systemDir, mHandler, this, this, mUserManagerUserInfoProvider, mPowerProfile, @@ -515,6 +505,26 @@ public final class BatteryStatsService extends IBatteryStats.Stub return config; } + private void setPowerStatsThrottlePeriods(BatteryStatsImpl.BatteryStatsConfig.Builder builder, + String configString) { + Matcher matcher = Pattern.compile("([^:]+):(\\d+)\\s*").matcher(configString); + while (matcher.find()) { + String powerComponentName = matcher.group(1); + long throttlePeriod; + try { + throttlePeriod = Long.parseLong(matcher.group(2)); + } catch (NumberFormatException nfe) { + throw new IllegalArgumentException( + "Invalid config_powerStatsThrottlePeriods format: " + configString); + } + if (powerComponentName.equals("*")) { + builder.setDefaultPowerStatsThrottlePeriodMillis(throttlePeriod); + } else { + builder.setPowerStatsThrottlePeriodMillis(powerComponentName, throttlePeriod); + } + } + } + /** * Creates an instance of BatteryStatsService and restores data from stored state. */ diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 6779f7a37f20..a5449a0f0431 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -37,6 +37,7 @@ import static android.os.Process.startWebView; import static android.system.OsConstants.EAGAIN; import static com.android.sdksandbox.flags.Flags.selinuxSdkSandboxAudit; +import static com.android.sdksandbox.flags.Flags.selinuxSdkSandboxInputSelector; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LRU; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_NETWORK; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESSES; @@ -2065,11 +2066,15 @@ public final class ProcessList { } } - return app.info.seInfo - + (TextUtils.isEmpty(app.info.seInfoUser) ? "" : app.info.seInfoUser) + extraInfo; + if (selinuxSdkSandboxInputSelector()) { + return app.info.seInfo + extraInfo + TextUtils.emptyIfNull(app.info.seInfoUser); + } else { + return app.info.seInfo + + (TextUtils.isEmpty(app.info.seInfoUser) ? "" : app.info.seInfoUser) + + extraInfo; + } } - @GuardedBy("mService") boolean startProcessLocked(HostingRecord hostingRecord, String entryPoint, ProcessRecord app, int uid, int[] gids, int runtimeFlags, int zygotePolicyFlags, int mountExternal, diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java index 7dcd95232374..9bf5c2174d2c 100644 --- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java +++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java @@ -157,6 +157,7 @@ public class SettingsToPropertiesMapper { "car_telemetry", "codec_fwk", "companion", + "com_android_adbd", "content_protection", "context_hub", "core_experiments_team_internal", @@ -264,11 +265,11 @@ public class SettingsToPropertiesMapper { Uri settingUri = Settings.Global.getUriFor(globalSetting); String propName = makePropertyName(GLOBAL_SETTINGS_CATEGORY, globalSetting); if (settingUri == null) { - log("setting uri is null for globalSetting " + globalSetting); + logErr("setting uri is null for globalSetting " + globalSetting); continue; } if (propName == null) { - log("invalid prop name for globalSetting " + globalSetting); + logErr("invalid prop name for globalSetting " + globalSetting); continue; } @@ -296,7 +297,7 @@ public class SettingsToPropertiesMapper { for (String key : properties.getKeyset()) { String propertyName = makePropertyName(scope, key); if (propertyName == null) { - log("unable to construct system property for " + scope + "/" + logErr("unable to construct system property for " + scope + "/" + key); return; } @@ -308,7 +309,7 @@ public class SettingsToPropertiesMapper { // sys prop slot can be removed. String aconfigPropertyName = makeAconfigFlagPropertyName(scope, key); if (aconfigPropertyName == null) { - log("unable to construct system property for " + scope + "/" + logErr("unable to construct system property for " + scope + "/" + key); return; } @@ -326,7 +327,7 @@ public class SettingsToPropertiesMapper { for (String key : properties.getKeyset()) { String aconfigPropertyName = makeAconfigFlagPropertyName(scope, key); if (aconfigPropertyName == null) { - log("unable to construct system property for " + scope + "/" + logErr("unable to construct system property for " + scope + "/" + key); return; } @@ -356,7 +357,7 @@ public class SettingsToPropertiesMapper { if (!propertyName.matches(SYSTEM_PROPERTY_VALID_CHARACTERS_REGEX) || propertyName.contains(SYSTEM_PROPERTY_INVALID_SUBSTRING)) { - log("unable to construct system property for " + actualNamespace + logErr("unable to construct system property for " + actualNamespace + "/" + flagName); continue; } @@ -394,9 +395,9 @@ public class SettingsToPropertiesMapper { try{ client.connect(new LocalSocketAddress( "aconfigd", LocalSocketAddress.Namespace.RESERVED)); - log("connected to aconfigd socket"); + Slog.d(TAG, "connected to aconfigd socket"); } catch (IOException ioe) { - log("failed to connect to aconfigd socket", ioe); + logErr("failed to connect to aconfigd socket", ioe); return null; } @@ -406,7 +407,7 @@ public class SettingsToPropertiesMapper { inputStream = new DataInputStream(client.getInputStream()); outputStream = new DataOutputStream(client.getOutputStream()); } catch (IOException ioe) { - log("failed to get local socket iostreams", ioe); + logErr("failed to get local socket iostreams", ioe); return null; } @@ -415,9 +416,9 @@ public class SettingsToPropertiesMapper { byte[] requests_bytes = requests.getBytes(); outputStream.writeInt(requests_bytes.length); outputStream.write(requests_bytes, 0, requests_bytes.length); - log("flag override requests sent to aconfigd"); + Slog.d(TAG, "flag override requests sent to aconfigd"); } catch (IOException ioe) { - log("failed to send requests to aconfigd", ioe); + logErr("failed to send requests to aconfigd", ioe); return null; } @@ -425,10 +426,10 @@ public class SettingsToPropertiesMapper { try { int num_bytes = inputStream.readInt(); ProtoInputStream returns = new ProtoInputStream(inputStream); - log("received " + num_bytes + " bytes back from aconfigd"); + Slog.d(TAG, "received " + num_bytes + " bytes back from aconfigd"); return returns; } catch (IOException ioe) { - log("failed to read requests return from aconfigd", ioe); + logErr("failed to read requests return from aconfigd", ioe); return null; } } @@ -461,18 +462,18 @@ public class SettingsToPropertiesMapper { long msgsToken = proto.start(StorageReturnMessages.MSGS); switch (proto.nextField()) { case (int) StorageReturnMessage.FLAG_OVERRIDE_MESSAGE: - log("successfully handled override requests"); + Slog.d(TAG, "successfully handled override requests"); long msgToken = proto.start(StorageReturnMessage.FLAG_OVERRIDE_MESSAGE); proto.end(msgToken); break; case (int) StorageReturnMessage.ERROR_MESSAGE: String errmsg = proto.readString(StorageReturnMessage.ERROR_MESSAGE); - log("override request failed: " + errmsg); + Slog.d(TAG, "override request failed: " + errmsg); break; case ProtoInputStream.NO_MORE_FIELDS: break; default: - log("invalid message type, expecting only flag override return or error message"); + logErr("invalid message type, expecting only flag override return or error message"); break; } proto.end(msgsToken); @@ -480,7 +481,7 @@ public class SettingsToPropertiesMapper { case ProtoInputStream.NO_MORE_FIELDS: return; default: - log("invalid message type, expect storage return message"); + logErr("invalid message type, expect storage return message"); break; } } @@ -501,14 +502,14 @@ public class SettingsToPropertiesMapper { int idx = flagName.indexOf(":"); if (idx == -1 || idx == flagName.length() - 1 || idx == 0) { - log("invalid local flag override: " + flagName); + logErr("invalid local flag override: " + flagName); continue; } String actualNamespace = flagName.substring(0, idx); String fullFlagName = flagName.substring(idx+1); idx = fullFlagName.lastIndexOf("."); if (idx == -1) { - log("invalid flag name: " + fullFlagName); + logErr("invalid flag name: " + fullFlagName); continue; } String packageName = fullFlagName.substring(0, idx); @@ -528,7 +529,7 @@ public class SettingsToPropertiesMapper { try { parseAndLogAconfigdReturn(returns); } catch (IOException ioe) { - log("failed to parse aconfigd return", ioe); + logErr("failed to parse aconfigd return", ioe); } } @@ -572,7 +573,7 @@ public class SettingsToPropertiesMapper { for (String property_name : property_names) { String[] segments = property_name.split("\\."); if (segments.length < 3) { - log("failed to extract category name from property " + property_name); + logErr("failed to extract category name from property " + property_name); continue; } categories.add(segments[2]); @@ -617,7 +618,7 @@ public class SettingsToPropertiesMapper { String stagedValue = flagValuesToStage.get(fullFlagName); int idx = fullFlagName.lastIndexOf("."); if (idx == -1) { - log("invalid flag name: " + fullFlagName); + logErr("invalid flag name: " + fullFlagName); continue; } String packageName = fullFlagName.substring(0, idx); @@ -638,7 +639,7 @@ public class SettingsToPropertiesMapper { try { parseAndLogAconfigdReturn(returns); } catch (IOException ioe) { - log("failed to parse aconfigd return", ioe); + logErr("failed to parse aconfigd return", ioe); } } @@ -680,7 +681,7 @@ public class SettingsToPropertiesMapper { for (String flagName : properties.getKeyset()) { int idx = flagName.indexOf(NAMESPACE_REBOOT_STAGING_DELIMITER); if (idx == -1 || idx == flagName.length() - 1 || idx == 0) { - log("invalid staged flag: " + flagName); + logErr("invalid staged flag: " + flagName); continue; } String actualNamespace = flagName.substring(0, idx); @@ -731,7 +732,7 @@ public class SettingsToPropertiesMapper { } value = ""; } else if (value.length() > SYSTEM_PROPERTY_MAX_LENGTH) { - log("key=" + key + " value=" + value + " exceeds system property max length."); + logErr("key=" + key + " value=" + value + " exceeds system property max length."); return; } @@ -741,11 +742,11 @@ public class SettingsToPropertiesMapper { // Failure to set a property can be caused by SELinux denial. This usually indicates // that the property wasn't allowlisted in sepolicy. // No need to report it on all user devices, only on debug builds. - log("Unable to set property " + key + " value '" + value + "'", e); + logErr("Unable to set property " + key + " value '" + value + "'", e); } } - private static void log(String msg, Exception e) { + private static void logErr(String msg, Exception e) { if (Build.IS_DEBUGGABLE) { Slog.wtf(TAG, msg, e); } else { @@ -753,7 +754,7 @@ public class SettingsToPropertiesMapper { } } - private static void log(String msg) { + private static void logErr(String msg) { if (Build.IS_DEBUGGABLE) { Slog.wtf(TAG, msg); } else { @@ -771,7 +772,7 @@ public class SettingsToPropertiesMapper { br.close(); } catch (IOException ioe) { - log("failed to read file " + RESET_RECORD_FILE_PATH, ioe); + logErr("failed to read file " + RESET_RECORD_FILE_PATH, ioe); } return content; } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java index 46061a56631c..275c9309ffc9 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java @@ -206,6 +206,10 @@ public final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { launchDeviceDiscovery(); startQueuedActions(); if (!mDelayedMessageBuffer.isBuffered(Constants.MESSAGE_ACTIVE_SOURCE)) { + if (hasAction(RequestActiveSourceAction.class)) { + Slog.i(TAG, "RequestActiveSourceAction is in progress. Restarting."); + removeAction(RequestActiveSourceAction.class); + } addAndStartAction(new RequestActiveSourceAction(this, new IHdmiControlCallback.Stub() { @Override public void onComplete(int result) { @@ -1308,6 +1312,8 @@ public final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { mService.sendCecCommand( HdmiCecMessageBuilder.buildActiveSource( getDeviceInfo().getLogicalAddress(), activePath)); + updateActiveSource(getDeviceInfo().getLogicalAddress(), activePath, + "HdmiCecLocalDeviceTv#launchRoutingControl()"); } } } diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index 936e8b6a28b6..cca73b5eabf0 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -1646,6 +1646,13 @@ public class HdmiControlService extends SystemService { case Constants.MESSAGE_ROUTING_CHANGE: case Constants.MESSAGE_SET_STREAM_PATH: case Constants.MESSAGE_TEXT_VIEW_ON: + // RequestActiveSourceAction is started after the TV finished logical address + // allocation. This action is used by the TV to get the active source from the CEC + // network. If the TV sent a source changing CEC message, this action does not have + // to continue anymore. + if (isTvDeviceEnabled()) { + tv().removeAction(RequestActiveSourceAction.class); + } sendCecCommandWithRetries(command, callback); break; default: diff --git a/services/core/java/com/android/server/hdmi/RequestActiveSourceAction.java b/services/core/java/com/android/server/hdmi/RequestActiveSourceAction.java index d2504164a6df..539a00db45b8 100644 --- a/services/core/java/com/android/server/hdmi/RequestActiveSourceAction.java +++ b/services/core/java/com/android/server/hdmi/RequestActiveSourceAction.java @@ -21,13 +21,20 @@ import android.hardware.hdmi.IHdmiControlCallback; import android.util.Slog; /** - * Feature action that sends <Request Active Source> message and waits for <Active Source>. + * Feature action that sends <Request Active Source> message and waits for <Active Source> on TV + * panels. + * This action has a delay before sending <Request Active Source>. This is because it should wait + * for a possible request from LauncherX and can be cancelled if an <Active Source> message was + * received or the TV switched to another input. */ public class RequestActiveSourceAction extends HdmiCecFeatureAction { private static final String TAG = "RequestActiveSourceAction"; + // State to wait for the LauncherX to call the CEC API. + private static final int STATE_WAIT_FOR_LAUNCHERX_API_CALL = 1; + // State to wait for the <Active Source> message. - private static final int STATE_WAIT_FOR_ACTIVE_SOURCE = 1; + private static final int STATE_WAIT_FOR_ACTIVE_SOURCE = 2; // Number of retries <Request Active Source> is sent if no device answers this message. private static final int MAX_SEND_RETRY_COUNT = 1; @@ -43,10 +50,12 @@ public class RequestActiveSourceAction extends HdmiCecFeatureAction { boolean start() { Slog.v(TAG, "RequestActiveSourceAction started."); - sendCommand(HdmiCecMessageBuilder.buildRequestActiveSource(getSourceAddress())); + mState = STATE_WAIT_FOR_LAUNCHERX_API_CALL; - mState = STATE_WAIT_FOR_ACTIVE_SOURCE; - addTimer(mState, HdmiConfig.TIMEOUT_MS); + // We wait for default timeout to allow the message triggered by the LauncherX API call to + // be sent by the TV and another default timeout in case the message has to be answered + // (e.g. TV sent a <Set Stream Path> or <Routing Change>). + addTimer(mState, HdmiConfig.TIMEOUT_MS * 2); return true; } @@ -65,13 +74,23 @@ public class RequestActiveSourceAction extends HdmiCecFeatureAction { if (mState != state) { return; } - if (mState == STATE_WAIT_FOR_ACTIVE_SOURCE) { - if (mSendRetryCount++ < MAX_SEND_RETRY_COUNT) { + + switch (mState) { + case STATE_WAIT_FOR_LAUNCHERX_API_CALL: + mState = STATE_WAIT_FOR_ACTIVE_SOURCE; sendCommand(HdmiCecMessageBuilder.buildRequestActiveSource(getSourceAddress())); addTimer(mState, HdmiConfig.TIMEOUT_MS); - } else { - finishWithCallback(HdmiControlManager.RESULT_TIMEOUT); - } + return; + case STATE_WAIT_FOR_ACTIVE_SOURCE: + if (mSendRetryCount++ < MAX_SEND_RETRY_COUNT) { + sendCommand(HdmiCecMessageBuilder.buildRequestActiveSource(getSourceAddress())); + addTimer(mState, HdmiConfig.TIMEOUT_MS); + } else { + finishWithCallback(HdmiControlManager.RESULT_TIMEOUT); + } + return; + default: + return; } } } diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 8685d2c45762..8e85b81d9bd2 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -2671,24 +2671,6 @@ public class InputManagerService extends IInputManager.Stub return null; } - private static class PointerDisplayIdChangedArgs { - final int mPointerDisplayId; - final float mXPosition; - final float mYPosition; - PointerDisplayIdChangedArgs(int pointerDisplayId, float xPosition, float yPosition) { - mPointerDisplayId = pointerDisplayId; - mXPosition = xPosition; - mYPosition = yPosition; - } - } - - // Native callback. - @SuppressWarnings("unused") - @VisibleForTesting - void onPointerDisplayIdChanged(int pointerDisplayId, float xPosition, float yPosition) { - // TODO(b/311416205): Remove. - } - @Override @EnforcePermission(Manifest.permission.MONITOR_STICKY_MODIFIER_STATE) public void registerStickyModifierStateListener( diff --git a/services/core/java/com/android/server/inputmethod/ImeTrackerService.java b/services/core/java/com/android/server/inputmethod/ImeTrackerService.java index 1c14fc1b08dd..fff0e6e1b995 100644 --- a/services/core/java/com/android/server/inputmethod/ImeTrackerService.java +++ b/services/core/java/com/android/server/inputmethod/ImeTrackerService.java @@ -133,6 +133,13 @@ public final class ImeTrackerService extends IImeTracker.Stub { } } + @Override + public void onDispatched(@NonNull ImeTracker.Token statsToken) { + synchronized (mLock) { + mHistory.setFinished(statsToken, ImeTracker.STATUS_SUCCESS, ImeTracker.PHASE_NOT_SET); + } + } + /** * Updates the IME request tracking token with new information available in IMMS. * diff --git a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java index b7091744c3b6..e862c7e96200 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java @@ -443,7 +443,16 @@ final class InputMethodBindingController { mCurId = info.getId(); mLastBindTime = SystemClock.uptimeMillis(); - addFreshWindowToken(); + final int displayIdToShowIme = mService.getDisplayIdToShowImeLocked(); + mCurToken = new Binder(); + mService.setCurTokenDisplayIdLocked(displayIdToShowIme); + if (DEBUG) { + Slog.v(TAG, "Adding window token: " + mCurToken + " for display: " + + displayIdToShowIme); + } + mWindowManagerInternal.addWindowToken(mCurToken, + WindowManager.LayoutParams.TYPE_INPUT_METHOD, + displayIdToShowIme, null /* options */); return new InputBindResult( InputBindResult.ResultCode.SUCCESS_WAITING_IME_BINDING, null, null, null, mCurId, mCurSeq, false); @@ -471,22 +480,6 @@ final class InputMethodBindingController { } @GuardedBy("ImfLock.class") - private void addFreshWindowToken() { - int displayIdToShowIme = mService.getDisplayIdToShowImeLocked(); - mCurToken = new Binder(); - - mService.setCurTokenDisplayIdLocked(displayIdToShowIme); - - if (DEBUG) { - Slog.v(TAG, "Adding window token: " + mCurToken + " for display: " - + displayIdToShowIme); - } - mWindowManagerInternal.addWindowToken(mCurToken, - WindowManager.LayoutParams.TYPE_INPUT_METHOD, - displayIdToShowIme, null /* options */); - } - - @GuardedBy("ImfLock.class") private void unbindMainConnection() { mContext.unbindService(mMainConnection); mHasMainConnection = false; diff --git a/services/core/java/com/android/server/inputmethod/InputMethodInfoUtils.java b/services/core/java/com/android/server/inputmethod/InputMethodInfoUtils.java index 6339686629f5..458c3598d7ae 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodInfoUtils.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodInfoUtils.java @@ -22,6 +22,7 @@ import static com.android.server.inputmethod.SubtypeUtils.SUBTYPE_MODE_KEYBOARD; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; +import android.os.Parcel; import android.text.TextUtils; import android.util.Slog; import android.view.inputmethod.InputMethodInfo; @@ -323,4 +324,24 @@ final class InputMethodInfoUtils { return SubtypeUtils.containsSubtypeOf(imi, requiredLocale, checkCountry, requiredSubtypeMode); } + + /** + * Marshals the given {@link InputMethodInfo} into a byte array. + * + * @param imi {@link InputMethodInfo} to be marshalled + * @return a byte array where the given {@link InputMethodInfo} is marshalled + */ + @NonNull + static byte[] marshal(@NonNull InputMethodInfo imi) { + Parcel parcel = null; + try { + parcel = Parcel.obtain(); + parcel.writeTypedObject(imi, 0); + return parcel.marshall(); + } finally { + if (parcel != null) { + parcel.recycle(); + } + } + } } diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 954f9bcd9c43..e5df42eaeea5 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -262,6 +262,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. private static final int NOT_A_SUBTYPE_ID = InputMethodUtils.NOT_A_SUBTYPE_ID; private static final String TAG_TRY_SUPPRESSING_IME_SWITCHER = "TrySuppressingImeSwitcher"; private static final String HANDLER_THREAD_NAME = "android.imms"; + private static final String PACKAGE_MONITOR_THREAD_NAME = "android.imms2"; /** * When set, {@link #startInputUncheckedLocked} will return @@ -281,10 +282,35 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @NonNull private final String[] mNonPreemptibleInputMethods; + /** + * See {@link #shouldEnableExperimentalConcurrentMultiUserMode(Context)} about when set to be + * {@code true}. + */ + private final boolean mExperimentalConcurrentMultiUserModeEnabled; + + /** + * Returns {@code true} if experimental concurrent multi-user mode is enabled. + * + * <p>Currently not compatible with profiles (e.g. work profile).</p> + * + * @param context {@link Context} to be used to query + * {@link PackageManager#FEATURE_AUTOMOTIVE} + * @return {@code true} if experimental concurrent multi-user mode is enabled. + */ + static boolean shouldEnableExperimentalConcurrentMultiUserMode(@NonNull Context context) { + return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE) + && UserManager.isVisibleBackgroundUsersEnabled() + && context.getResources().getBoolean(android.R.bool.config_perDisplayFocusEnabled) + && Flags.concurrentInputMethods(); + } + final Context mContext; final Resources mRes; private final Handler mHandler; + @NonNull + private final Handler mPackageMonitorHandler; + @MultiUserUnawareField @UserIdInt @GuardedBy("ImfLock.class") @@ -1187,8 +1213,10 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. public static final class Lifecycle extends SystemService { private final InputMethodManagerService mService; + public Lifecycle(Context context) { - this(context, new InputMethodManagerService(context)); + this(context, new InputMethodManagerService(context, + shouldEnableExperimentalConcurrentMultiUserMode(context))); } public Lifecycle( @@ -1240,9 +1268,15 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @Override public void onUserStarting(TargetUser user) { // Called on ActivityManager thread. - SecureSettingsWrapper.onUserStarting(user.getUserIdentifier()); + final int userId = user.getUserIdentifier(); + SecureSettingsWrapper.onUserStarting(userId); synchronized (ImfLock.class) { - mService.mUserDataRepository.getOrCreate(user.getUserIdentifier()); + mService.mUserDataRepository.getOrCreate(userId); + if (mService.mExperimentalConcurrentMultiUserModeEnabled) { + if (mService.mCurrentUserId != userId) { + mService.experimentalInitializeVisibleBackgroundUserLocked(userId); + } + } } } @@ -1263,6 +1297,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. // We need to rebuild IMEs. postInputMethodSettingUpdatedLocked(false /* resetDefaultEnabledIme */); updateInputMethodsFromSettingsLocked(true /* enabledChanged */); + } else if (mExperimentalConcurrentMultiUserModeEnabled) { + experimentalInitializeVisibleBackgroundUserLocked(userId); } } } @@ -1287,16 +1323,21 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. mHandler.post(task); } - public InputMethodManagerService(Context context) { - this(context, null, null); + public InputMethodManagerService(Context context, + boolean experimentalConcurrentMultiUserModeEnabled) { + this(context, experimentalConcurrentMultiUserModeEnabled, null, null, null); } @VisibleForTesting InputMethodManagerService( Context context, + boolean experimentalConcurrentMultiUserModeEnabled, @Nullable ServiceThread serviceThreadForTesting, + @Nullable ServiceThread packageMonitorThreadForTesting, @Nullable IntFunction<InputMethodBindingController> bindingControllerForTesting) { synchronized (ImfLock.class) { + mExperimentalConcurrentMultiUserModeEnabled = + experimentalConcurrentMultiUserModeEnabled; mContext = context; mRes = context.getResources(); SecureSettingsWrapper.onStart(mContext); @@ -1312,6 +1353,17 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. true /* allowIo */); thread.start(); mHandler = Handler.createAsync(thread.getLooper(), this); + { + final ServiceThread packageMonitorThread = + packageMonitorThreadForTesting != null + ? packageMonitorThreadForTesting + : new ServiceThread( + PACKAGE_MONITOR_THREAD_NAME, + Process.THREAD_PRIORITY_FOREGROUND, + true /* allowIo */); + packageMonitorThread.start(); + mPackageMonitorHandler = Handler.createAsync(packageMonitorThread.getLooper()); + } SystemLocaleWrapper.onStart(context, this::onActionLocaleChanged, mHandler); mImeTrackerService = new ImeTrackerService(serviceThreadForTesting != null ? serviceThreadForTesting.getLooper() : Looper.getMainLooper()); @@ -1487,7 +1539,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. // Note that in b/197848765 we want to see if we can keep the binding alive for better // profile switching. final var userData = mUserDataRepository.getOrCreate(mCurrentUserId); - userData.mBindingController.unbindCurrentMethod(); + final var bindingController = userData.mBindingController; + bindingController.unbindCurrentMethod(); unbindCurrentClientLocked(UnbindReason.SWITCH_USER); @@ -1585,7 +1638,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } }, "Lazily initialize IMMS#mImeDrawsImeNavBarRes"); - mMyPackageMonitor.register(mContext, UserHandle.ALL, mHandler); + mMyPackageMonitor.register(mContext, UserHandle.ALL, mPackageMonitorHandler); mSettingsObserver.registerContentObserverLocked(currentUserId); final IntentFilter broadcastFilterForAllUsers = new IntentFilter(); @@ -1613,8 +1666,9 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. /** * Returns true iff the caller is identified to be the current input method with the token. - * @param token The window token given to the input method when it was started. - * @return true if and only if non-null valid token is specified. + * + * @param token the window token given to the input method when it was started + * @return true if and only if non-null valid token is specified */ @GuardedBy("ImfLock.class") private boolean calledWithValidTokenLocked(@NonNull IBinder token) { @@ -1706,9 +1760,10 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. // Check if selected IME of current user supports handwriting. if (userId == mCurrentUserId) { final var userData = mUserDataRepository.getOrCreate(userId); - return userData.mBindingController.supportsStylusHandwriting() + final var bindingController = userData.mBindingController; + return bindingController.supportsStylusHandwriting() && (!connectionless - || userData.mBindingController.supportsConnectionlessStylusHandwriting()); + || bindingController.supportsConnectionlessStylusHandwriting()); } final InputMethodSettings settings = InputMethodSettingsRepository.get(userId); final InputMethodInfo imi = settings.getMethodMap().get( @@ -1769,10 +1824,11 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. /** * Gets enabled subtypes of the specified {@link InputMethodInfo}. * - * @param imiId if null, returns enabled subtypes for the current {@link InputMethodInfo}. + * @param imiId if null, returns enabled subtypes for the current + * {@link InputMethodInfo} * @param allowsImplicitlyEnabledSubtypes {@code true} to return the implicitly enabled - * subtypes. - * @param userId the user ID to be queried about. + * subtypes + * @param userId the user ID to be queried about */ @Override public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(String imiId, @@ -1816,10 +1872,11 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. * <p>As a general principle, IPCs from the application process that take * {@link IInputMethodClient} will be rejected without this step.</p> * - * @param client {@link android.os.Binder} proxy that is associated with the singleton instance - * of {@link android.view.inputmethod.InputMethodManager} that runs on the client - * process - * @param inputConnection communication channel for the fallback {@link InputConnection} + * @param client {@link android.os.Binder} proxy that is associated with the + * singleton instance of + * {@link android.view.inputmethod.InputMethodManager} that runs + * on the client process + * @param inputConnection communication channel for the fallback {@link InputConnection} * @param selfReportedDisplayId self-reported display ID to which the client is associated. * Whether the client is still allowed to access to this display * or not needs to be evaluated every time the client interacts @@ -1844,10 +1901,10 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } } - // TODO(b/325515685): Move this method to InputMethodBindingController /** * Hide the IME if the removed user is the current user. */ + // TODO(b/325515685): Move this method to InputMethodBindingController @GuardedBy("ImfLock.class") private void onClientRemoved(ClientState client) { clearClientSessionLocked(client); @@ -1904,7 +1961,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. // following dependencies also need to be user independent: mCurClient, mBoundToMethod, // getCurMethodLocked(), and mMenuController. final var userData = mUserDataRepository.getOrCreate(mCurrentUserId); - mCurClient.mClient.onUnbindMethod(userData.mBindingController.getSequenceNumber(), + final var bindingController = userData.mBindingController; + mCurClient.mClient.onUnbindMethod(bindingController.getSequenceNumber(), unbindClientReason); mCurClient.mSessionRequested = false; mCurClient.mSessionRequestedForAccessibility = false; @@ -1984,13 +2042,14 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. final boolean restarting = !initial; final Binder startInputToken = new Binder(); final var userData = mUserDataRepository.getOrCreate(mCurrentUserId); + final var bindingController = userData.mBindingController; final StartInputInfo info = new StartInputInfo(mCurrentUserId, getCurTokenLocked(), - mCurTokenDisplayId, userData.mBindingController.getCurId(), startInputReason, + getCurTokenDisplayIdLocked(), bindingController.getCurId(), startInputReason, restarting, UserHandle.getUserId(mCurClient.mUid), mCurClient.mSelfReportedDisplayId, mImeBindingState.mFocusedWindow, mCurEditorInfo, mImeBindingState.mFocusedWindowSoftInputMode, - userData.mBindingController.getSequenceNumber()); + bindingController.getSequenceNumber()); mImeTargetWindowMap.put(startInputToken, mImeBindingState.mFocusedWindow); mStartInputHistory.addEntry(info); @@ -2002,7 +2061,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. if (mCurrentUserId == UserHandle.getUserId( mCurClient.mUid)) { mPackageManagerInternal.grantImplicitAccess(mCurrentUserId, null /* intent */, - UserHandle.getAppId(userData.mBindingController.getCurMethodUid()), + UserHandle.getAppId(bindingController.getCurMethodUid()), mCurClient.mUid, true /* direct */); } @@ -2023,20 +2082,20 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. null /* resultReceiver */, SoftInputShowHideReason.ATTACH_NEW_INPUT); } - final var curId = userData.mBindingController.getCurId(); + final var curId = bindingController.getCurId(); final InputMethodInfo curInputMethodInfo = InputMethodSettingsRepository.get(mCurrentUserId) .getMethodMap().get(curId); final boolean suppressesSpellChecker = curInputMethodInfo != null && curInputMethodInfo.suppressesSpellChecker(); final SparseArray<IAccessibilityInputMethodSession> accessibilityInputMethodSessions = createAccessibilityInputMethodSessions(mCurClient.mAccessibilitySessions); - if (userData.mBindingController.supportsStylusHandwriting() && hasSupportedStylusLocked()) { + if (bindingController.supportsStylusHandwriting() && hasSupportedStylusLocked()) { mHwController.setInkWindowInitializer(new InkWindowInitializer()); } return new InputBindResult(InputBindResult.ResultCode.SUCCESS_WITH_IME_SESSION, session.mSession, accessibilityInputMethodSessions, (session.mChannel != null ? session.mChannel.dup() : null), - curId, userData.mBindingController.getSequenceNumber(), suppressesSpellChecker); + curId, bindingController.getSequenceNumber(), suppressesSpellChecker); } @GuardedBy("ImfLock.class") @@ -2130,7 +2189,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. final boolean connectionWasActive = mCurInputConnection != null; // Bump up the sequence for this client and attach it. - userData.mBindingController.advanceSequenceNumber(); + final var bindingController = userData.mBindingController; + bindingController.advanceSequenceNumber(); mCurClient = cs; mCurInputConnection = inputConnection; @@ -2153,7 +2213,6 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. if (connectionIsActive != connectionWasActive) { mInputManagerInternal.notifyInputMethodConnectionActive(connectionIsActive); } - final var bindingController = userData.mBindingController; // If configured, we want to avoid starting up the IME if it is not supposed to be showing if (shouldPreventImeStartupLocked(selectedMethodId, startInputFlags, @@ -2171,7 +2230,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. // display ID. final String curId = bindingController.getCurId(); if (curId != null && curId.equals(bindingController.getSelectedMethodId()) - && mDisplayIdToShowIme == mCurTokenDisplayId) { + && mDisplayIdToShowIme == getCurTokenDisplayIdLocked()) { if (cs.mCurSession != null) { // Fast case: if we are already connected to the input method, // then just return it. @@ -2202,11 +2261,13 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. /** * Update the current deviceId and return the relevant imeId for this device. - * 1. If the device changes to virtual and its custom IME is not available, then disable IME. - * 2. If the device changes to virtual with valid custom IME, then return the custom IME. If - * the old device was default, then store the current imeId so it can be restored. - * 3. If the device changes to default, restore the default device IME. - * 4. Otherwise keep the current imeId. + * + * <p>1. If the device changes to virtual and its custom IME is not available, then disable + * IME.</p> + * <p>2. If the device changes to virtual with valid custom IME, then return the custom IME. If + * the old device was default, then store the current imeId so it can be restored.</p> + * <p>3. If the device changes to default, restore the default device IME.</p> + * <p>4. Otherwise keep the current imeId.</p> */ @GuardedBy("ImfLock.class") private String computeCurrentDeviceMethodIdLocked(String currentMethodId) { @@ -2304,7 +2365,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @Nullable private InputBindResult tryReuseConnectionLocked(@NonNull UserDataRepository.UserData userData, @NonNull ClientState cs) { - if (userData.mBindingController.hasMainConnection()) { + final var bindingController = userData.mBindingController; + if (bindingController.hasMainConnection()) { if (getCurMethodLocked() != null) { // Return to client, and we will get back with it when // we have had a session made for it. @@ -2313,10 +2375,10 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. return new InputBindResult( InputBindResult.ResultCode.SUCCESS_WAITING_IME_SESSION, null, null, null, - userData.mBindingController.getCurId(), - userData.mBindingController.getSequenceNumber(), false); + bindingController.getCurId(), + bindingController.getSequenceNumber(), false); } else { - final long lastBindTime = userData.mBindingController.getLastBindTime(); + final long lastBindTime = bindingController.getLastBindTime(); long bindingDuration = SystemClock.uptimeMillis() - lastBindTime; if (bindingDuration < TIME_TO_RECONNECT) { // In this case we have connected to the service, but @@ -2329,8 +2391,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. return new InputBindResult( InputBindResult.ResultCode.SUCCESS_WAITING_IME_BINDING, null, null, null, - userData.mBindingController.getCurId(), - userData.mBindingController.getSequenceNumber(), false); + bindingController.getCurId(), + bindingController.getSequenceNumber(), false); } else { EventLog.writeEvent(EventLogTags.IMF_FORCE_RECONNECT_IME, getSelectedMethodIdLocked(), bindingDuration, 0); @@ -2349,12 +2411,12 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. /** * Find the display where the IME should be shown. * - * @param displayId the ID of the display where the IME client target is. - * @param checker instance of {@link ImeDisplayValidator} which is used for - * checking display config to adjust the final target display. - * @return The ID of the display where the IME should be shown or - * {@link android.view.Display#INVALID_DISPLAY} if the display has an ImePolicy of - * {@link WindowManager#DISPLAY_IME_POLICY_HIDE}. + * @param displayId the ID of the display where the IME client target is + * @param checker instance of {@link ImeDisplayValidator} which is used for + * checking display config to adjust the final target display + * @return the ID of the display where the IME should be shown or + * {@link android.view.Display#INVALID_DISPLAY} if the display has an ImePolicy of + * {@link WindowManager#DISPLAY_IME_POLICY_HIDE} */ static int computeImeDisplayIdForTarget(int displayId, @NonNull ImeDisplayValidator checker) { if (displayId == DEFAULT_DISPLAY || displayId == INVALID_DISPLAY) { @@ -2377,7 +2439,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. void initializeImeLocked(@NonNull IInputMethodInvoker inputMethod, @NonNull IBinder token) { if (DEBUG) { Slog.v(TAG, "Sending attach of token: " + token + " for display: " - + mCurTokenDisplayId); + + getCurTokenDisplayIdLocked()); } inputMethod.initializeInternal(token, new InputMethodPrivilegedOperationsImpl(this, token), getInputMethodNavButtonFlagsLocked()); @@ -2457,13 +2519,14 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @GuardedBy("ImfLock.class") void resetCurrentMethodAndClientLocked(@UnbindReason int unbindClientReason) { - final var userData = mUserDataRepository.getOrCreate(mCurrentUserId); - userData.mBindingController.setSelectedMethodId(null); + final var bindingController = + mUserDataRepository.getOrCreate(mCurrentUserId).mBindingController; + bindingController.setSelectedMethodId(null); // Callback before clean-up binding states. // TODO(b/338461930): Check if this is still necessary or not. onUnbindCurrentMethodByReset(); - userData.mBindingController.unbindCurrentMethod(); + bindingController.unbindCurrentMethod(); unbindCurrentClientLocked(unbindClientReason); } @@ -2666,9 +2729,10 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } // Whether the current display has a navigation bar. When this is false (e.g. emulator), // the IME should not draw the IME navigation bar. + final int tokenDisplayId = getCurTokenDisplayIdLocked(); final boolean hasNavigationBar = mWindowManagerInternal - .hasNavigationBar(mCurTokenDisplayId != INVALID_DISPLAY - ? mCurTokenDisplayId : DEFAULT_DISPLAY); + .hasNavigationBar(tokenDisplayId != INVALID_DISPLAY + ? tokenDisplayId : DEFAULT_DISPLAY); final boolean canImeDrawsImeNavBar = mImeDrawsImeNavBarRes != null && mImeDrawsImeNavBarRes.get() && hasNavigationBar; final boolean shouldShowImeSwitcherWhenImeIsShown = shouldShowImeSwitcherLocked( @@ -2764,8 +2828,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. // Note that we still need to update IME status when focusing external display // that does not support system decoration and fallback to show IME on default // display since it is intentional behavior. - if (mCurTokenDisplayId != topFocusedDisplayId - && mCurTokenDisplayId != FALLBACK_DISPLAY_ID) { + final int tokenDisplayId = getCurTokenDisplayIdLocked(); + if (tokenDisplayId != topFocusedDisplayId && tokenDisplayId != FALLBACK_DISPLAY_ID) { return; } mImeWindowVis = vis; @@ -2828,7 +2892,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. Slog.d(TAG, "IME window vis: " + vis + " active: " + (vis & InputMethodService.IME_ACTIVE) + " inv: " + (vis & InputMethodService.IME_INVISIBLE) - + " displayId: " + mCurTokenDisplayId); + + " displayId: " + getCurTokenDisplayIdLocked()); } final IBinder focusedWindowToken = mImeBindingState != null ? mImeBindingState.mFocusedWindow : null; @@ -2857,7 +2921,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } final boolean needsToShowImeSwitcher = shouldShowImeSwitcherLocked(vis); if (mStatusBarManagerInternal != null) { - mStatusBarManagerInternal.setImeWindowStatus(mCurTokenDisplayId, + mStatusBarManagerInternal.setImeWindowStatus(getCurTokenDisplayIdLocked(), getCurTokenLocked(), vis, backDisposition, needsToShowImeSwitcher); } } finally { @@ -2871,6 +2935,49 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. mMenuController.updateKeyboardFromSettingsLocked(); } + /** + * This is an experimental implementation used when and only when + * {@link #mExperimentalConcurrentMultiUserModeEnabled}. + * + * <p>Never assume what this method is doing is officially supported. For the canonical and + * desired behaviors always refer to single-user code paths such as + * {@link #updateInputMethodsFromSettingsLocked(boolean)}.</p> + * + * <p>Here are examples of missing features.</p> + * <ul> + * <li>Subtypes are not supported at all!</li> + * <li>Profiles are not supported.</li> + * <li> + * {@link PackageManager#COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED} is not updated. + * </li> + * <li>{@link #mDeviceIdToShowIme} is ignored.</li> + * <li>{@link #mSwitchingController} is ignored.</li> + * <li>{@link #mHardwareKeyboardShortcutController} is ignored.</li> + * <li>{@link #mPreventImeStartupUnlessTextEditor} is ignored.</li> + * <li>and so on.</li> + * </ul> + */ + @GuardedBy("ImfLock.class") + void experimentalInitializeVisibleBackgroundUserLocked(@UserIdInt int userId) { + if (!mUserManagerInternal.isUserVisible(userId)) { + return; + } + final var settings = InputMethodSettingsRepository.get(userId); + String id = settings.getSelectedInputMethod(); + if (TextUtils.isEmpty(id)) { + final InputMethodInfo imi = InputMethodInfoUtils.getMostApplicableDefaultIME( + settings.getEnabledInputMethodList()); + if (imi == null) { + return; + } + id = imi.getId(); + settings.putSelectedInputMethod(id); + } + final var userData = mUserDataRepository.getOrCreate(userId); + final var bindingController = userData.mBindingController; + bindingController.setSelectedMethodId(id); + } + @GuardedBy("ImfLock.class") void updateInputMethodsFromSettingsLocked(boolean enabledMayChange) { final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId); @@ -3546,13 +3653,14 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. final InputBindResult result; synchronized (ImfLock.class) { final var userData = mUserDataRepository.getOrCreate(userId); + final var bindingController = userData.mBindingController; // If the system is not yet ready, we shouldn't be running third party code. if (!mSystemReady) { return new InputBindResult( InputBindResult.ResultCode.ERROR_SYSTEM_NOT_READY, null /* method */, null /* accessibilitySessions */, null /* channel */, getSelectedMethodIdLocked(), - userData.mBindingController.getSequenceNumber(), + bindingController.getSequenceNumber(), false /* isInputMethodSuppressingSpellChecker */); } final ClientState cs = mClientController.getClient(client.asBinder()); @@ -3761,7 +3869,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. // window token removed. // Note that we can trust client's display ID as long as it matches // to the display ID obtained from the window. - if (cs.mSelfReportedDisplayId != mCurTokenDisplayId) { + if (cs.mSelfReportedDisplayId != getCurTokenDisplayIdLocked()) { userData.mBindingController.unbindCurrentMethod(); } } @@ -4125,7 +4233,6 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. * This is kept due to {@code @UnsupportedAppUsage} in * {@link InputMethodManager#getInputMethodWindowVisibleHeight()} and a dependency in * {@link InputMethodService#onCreate()}. - * * @return {@link WindowManagerInternal#getInputMethodWindowVisibleHeight(int)} * * @deprecated TODO(b/113914148): Check if we can remove this @@ -4143,7 +4250,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } // This should probably use the caller's display id, but because this is unsupported // and maintained only for compatibility, there's no point in fixing it. - curTokenDisplayId = mCurTokenDisplayId; + curTokenDisplayId = getCurTokenDisplayIdLocked(); } return mWindowManagerInternal.getInputMethodWindowVisibleHeight(curTokenDisplayId); }); @@ -4224,8 +4331,9 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. // a new Stylus is detected. If IME supports handwriting, and we don't have // handwriting initialized, lets do it now. final var userData = mUserDataRepository.getOrCreate(mCurrentUserId); + final var bindingController = userData.mBindingController; if (!mHwController.getCurrentRequestId().isPresent() - && userData.mBindingController.supportsStylusHandwriting()) { + && bindingController.supportsStylusHandwriting()) { scheduleResetStylusHandwriting(); } } @@ -4283,7 +4391,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. /** * Helper method to set a stylus idle-timeout after which handwriting {@code InkWindow} * will be removed. - * @param timeout to set in milliseconds. To reset to default, use a value <= zero. + * + * @param timeout to set in milliseconds. To reset to default, use a value <= zero */ @BinderThread @IInputMethodManagerImpl.PermissionVerified(Manifest.permission.TEST_INPUT_METHOD) @@ -4407,9 +4516,10 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. private void dumpDebug(ProtoOutputStream proto, long fieldId) { synchronized (ImfLock.class) { final var userData = mUserDataRepository.getOrCreate(mCurrentUserId); + final var bindingController = userData.mBindingController; final long token = proto.start(fieldId); proto.write(CUR_METHOD_ID, getSelectedMethodIdLocked()); - proto.write(CUR_SEQ, userData.mBindingController.getSequenceNumber()); + proto.write(CUR_SEQ, bindingController.getSequenceNumber()); proto.write(CUR_CLIENT, Objects.toString(mCurClient)); mImeBindingState.dumpDebug(proto, mWindowManagerInternal); proto.write(LAST_IME_TARGET_WINDOW_NAME, @@ -4419,13 +4529,13 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. if (mCurEditorInfo != null) { mCurEditorInfo.dumpDebug(proto, CUR_ATTRIBUTE); } - proto.write(CUR_ID, userData.mBindingController.getCurId()); + proto.write(CUR_ID, bindingController.getCurId()); mVisibilityStateComputer.dumpDebug(proto, fieldId); proto.write(IN_FULLSCREEN_MODE, mInFullscreenMode); proto.write(CUR_TOKEN, Objects.toString(getCurTokenLocked())); - proto.write(CUR_TOKEN_DISPLAY_ID, mCurTokenDisplayId); + proto.write(CUR_TOKEN_DISPLAY_ID, getCurTokenDisplayIdLocked()); proto.write(SYSTEM_READY, mSystemReady); - proto.write(HAVE_CONNECTION, userData.mBindingController.hasMainConnection()); + proto.write(HAVE_CONNECTION, bindingController.hasMainConnection()); proto.write(BOUND_TO_METHOD, mBoundToMethod); proto.write(IS_INTERACTIVE, mIsInteractive); proto.write(BACK_DISPOSITION, mBackDisposition); @@ -4537,7 +4647,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. final IBinder requestToken = mVisibilityStateComputer.getWindowTokenFrom(requestImeToken); final WindowManagerInternal.ImeTargetInfo info = mWindowManagerInternal.onToggleImeRequested( - show, mImeBindingState.mFocusedWindow, requestToken, mCurTokenDisplayId); + show, mImeBindingState.mFocusedWindow, requestToken, + getCurTokenDisplayIdLocked()); mSoftInputShowHideHistory.addEntry(new SoftInputShowHideHistory.Entry( mImeBindingState.mFocusedWindowClient, mImeBindingState.mFocusedWindowEditorInfo, info.focusedWindowName, mImeBindingState.mFocusedWindowSoftInputMode, reason, @@ -4796,10 +4907,11 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. case MSG_RESET_HANDWRITING: { synchronized (ImfLock.class) { final var userData = mUserDataRepository.getOrCreate(mCurrentUserId); - if (userData.mBindingController.supportsStylusHandwriting() + final var bindingController = userData.mBindingController; + if (bindingController.supportsStylusHandwriting() && getCurMethodLocked() != null && hasSupportedStylusLocked()) { Slog.d(TAG, "Initializing Handwriting Spy"); - mHwController.initializeHandwritingSpy(mCurTokenDisplayId); + mHwController.initializeHandwritingSpy(getCurTokenDisplayIdLocked()); } else { mHwController.reset(); } @@ -4822,11 +4934,12 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. return true; } final var userData = mUserDataRepository.getOrCreate(mCurrentUserId); + final var bindingController = userData.mBindingController; final HandwritingModeController.HandwritingSession session = mHwController.startHandwritingSession( msg.arg1 /*requestId*/, msg.arg2 /*pid*/, - userData.mBindingController.getCurMethodUid(), + bindingController.getCurMethodUid(), mImeBindingState.mFocusedWindow); if (session == null) { Slog.e(TAG, @@ -4878,8 +4991,9 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } // TODO(b/325515685): user data must be retrieved by a userId parameter final var userData = mUserDataRepository.getOrCreate(mCurrentUserId); + final var bindingController = userData.mBindingController; if (mImePlatformCompatUtils.shouldUseSetInteractiveProtocol( - userData.mBindingController.getCurMethodUid())) { + bindingController.getCurMethodUid())) { // Handle IME visibility when interactive changed before finishing the input to // ensure we preserve the last state as possible. final ImeVisibilityResult imeVisRes = mVisibilityStateComputer.onInteractiveChanged( @@ -5124,7 +5238,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @GuardedBy("ImfLock.class") void sendOnNavButtonFlagsChangedLocked() { final var userData = mUserDataRepository.getOrCreate(mCurrentUserId); - final IInputMethodInvoker curMethod = userData.mBindingController.getCurMethod(); + final var bindingController = userData.mBindingController; + final IInputMethodInvoker curMethod = bindingController.getCurMethod(); if (curMethod == null) { // No need to send the data if the IME is not yet bound. return; @@ -5169,10 +5284,10 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. /** * Enable or disable the given IME by updating {@link Settings.Secure#ENABLED_INPUT_METHODS}. * - * @param id ID of the IME is to be manipulated. It is OK to pass IME ID that is currently not - * recognized by the system. - * @param enabled {@code true} if {@code id} needs to be enabled. - * @return {@code true} if the IME was previously enabled. {@code false} otherwise. + * @param id ID of the IME is to be manipulated. It is OK to pass IME ID that is currently + * not recognized by the system + * @param enabled {@code true} if {@code id} needs to be enabled + * @return {@code true} if the IME was previously enabled */ @GuardedBy("ImfLock.class") private boolean setInputMethodEnabledLocked(String id, boolean enabled) { @@ -5279,8 +5394,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. /** * Gets the current subtype of this input method. * - * @param userId User ID to be queried about. - * @return The current {@link InputMethodSubtype} for the specified user. + * @param userId User ID to be queried about + * @return the current {@link InputMethodSubtype} for the specified user */ @Nullable @Override @@ -5354,7 +5469,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. /** * Returns the default {@link InputMethodInfo} for the specific userId. - * @param userId user ID to query. + * + * @param userId user ID to query */ @GuardedBy("ImfLock.class") private InputMethodInfo queryDefaultInputMethodForUserIdLocked(@UserIdInt int userId) { @@ -5388,11 +5504,11 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. * Filter the access to the input method by rules of the package visibility. Return {@code true} * if the given input method is the currently selected one or visible to the caller. * - * @param targetPkgName The package name of input method to check. - * @param callingUid The caller that is going to access the input method. - * @param userId The user ID where the input method resides. - * @param settings The input method settings under the given user ID. - * @return {@code true} if caller is able to access the input method. + * @param targetPkgName the package name of input method to check + * @param callingUid the caller that is going to access the input method + * @param userId the user ID where the input method resides + * @param settings the input method settings under the given user ID + * @return {@code true} if caller is able to access the input method */ private boolean canCallerAccessInputMethod(@NonNull String targetPkgName, int callingUid, @UserIdInt int userId, @NonNull InputMethodSettings settings) { @@ -5558,7 +5674,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. //TODO(b/150843766): Check if Input Token is valid. final IBinder curHostInputToken; synchronized (ImfLock.class) { - if (displayId != mCurTokenDisplayId) { + if (displayId != getCurTokenDisplayIdLocked()) { return false; } curHostInputToken = mAutofillController.getCurHostInputToken(); @@ -5610,6 +5726,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. IAccessibilityInputMethodSession session, @UserIdInt int userId) { synchronized (ImfLock.class) { final var userData = mUserDataRepository.getOrCreate(mCurrentUserId); + final var bindingController = userData.mBindingController; // TODO(b/305829876): Implement user ID verification if (mCurClient != null) { clearClientSessionForAccessibilityLocked(mCurClient, accessibilityConnectionId); @@ -5632,8 +5749,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. final InputBindResult res = new InputBindResult( InputBindResult.ResultCode.SUCCESS_WITH_ACCESSIBILITY_SESSION, imeSession, accessibilityInputMethodSessions, /* channel= */ null, - userData.mBindingController.getCurId(), - userData.mBindingController.getSequenceNumber(), + bindingController.getCurId(), + bindingController.getSequenceNumber(), /* isInputMethodSuppressingSpellChecker= */ false); mCurClient.mClient.onBindAccessibilityService(res, accessibilityConnectionId); } @@ -5645,6 +5762,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @UserIdInt int userId) { synchronized (ImfLock.class) { final var userData = mUserDataRepository.getOrCreate(mCurrentUserId); + final var bindingController = userData.mBindingController; // TODO(b/305829876): Implement user ID verification if (mCurClient != null) { if (DEBUG) { @@ -5654,7 +5772,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. // A11yManagerService unbinds the disabled accessibility service. We don't need // to do it here. mCurClient.mClient.onUnbindAccessibilityService( - userData.mBindingController.getSequenceNumber(), + bindingController.getSequenceNumber(), accessibilityConnectionId); } // We only have sessions when we bound to an input method. Remove this session @@ -5877,18 +5995,19 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. }; mClientController.forAllClients(clientControllerDump); final var userData = mUserDataRepository.getOrCreate(mCurrentUserId); + final var bindingController = userData.mBindingController; p.println(" mCurrentUserId=" + mCurrentUserId); p.println(" mCurMethodId=" + getSelectedMethodIdLocked()); client = mCurClient; p.println(" mCurClient=" + client + " mCurSeq=" - + userData.mBindingController.getSequenceNumber()); + + bindingController.getSequenceNumber()); p.println(" mFocusedWindowPerceptible=" + mFocusedWindowPerceptible); mImeBindingState.dump(/* prefix= */ " ", p); - p.println(" mCurId=" + userData.mBindingController.getCurId() - + " mHaveConnection=" + userData.mBindingController.hasMainConnection() + p.println(" mCurId=" + bindingController.getCurId() + + " mHaveConnection=" + bindingController.hasMainConnection() + " mBoundToMethod=" + mBoundToMethod + " mVisibleBound=" - + userData.mBindingController.isVisibleBound()); + + bindingController.isVisibleBound()); p.println(" mUserDataRepository="); // TODO(b/324907325): Remove the suppress warnings once b/324907325 is fixed. @@ -5902,15 +6021,17 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. mUserDataRepository.forAllUserData(userDataDump); p.println(" mCurToken=" + getCurTokenLocked()); - p.println(" mCurTokenDisplayId=" + mCurTokenDisplayId); + p.println(" mCurTokenDisplayId=" + getCurTokenDisplayIdLocked()); p.println(" mCurHostInputToken=" + mAutofillController.getCurHostInputToken()); - p.println(" mCurIntent=" + userData.mBindingController.getCurIntent()); + p.println(" mCurIntent=" + bindingController.getCurIntent()); method = getCurMethodLocked(); p.println(" mCurMethod=" + getCurMethodLocked()); p.println(" mEnabledSession=" + mEnabledSession); mVisibilityStateComputer.dump(pw, " "); p.println(" mInFullscreenMode=" + mInFullscreenMode); p.println(" mSystemReady=" + mSystemReady + " mInteractive=" + mIsInteractive); + p.println(" mExperimentalConcurrentMultiUserModeEnabled=" + + mExperimentalConcurrentMultiUserModeEnabled); p.println(" ENABLE_HIDE_IME_CAPTION_BAR=" + InputMethodService.ENABLE_HIDE_IME_CAPTION_BAR); p.println(" mSettingsObserver=" + mSettingsObserver); @@ -6143,8 +6264,9 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. /** * Handles {@code adb shell ime list}. - * @param shellCommand {@link ShellCommand} object that is handling this command. - * @return Exit code of the command. + * + * @param shellCommand {@link ShellCommand} object that is handling this command + * @return exit code of the command */ @BinderThread @ShellCommandResult @@ -6202,9 +6324,9 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. /** * Handles {@code adb shell ime enable} and {@code adb shell ime disable}. * - * @param shellCommand {@link ShellCommand} object that is handling this command. - * @param enabled {@code true} if the command was {@code adb shell ime enable}. - * @return Exit code of the command. + * @param shellCommand {@link ShellCommand} object that is handling this command + * @param enabled {@code true} if the command was {@code adb shell ime enable} + * @return exit code of the command */ @BinderThread @ShellCommandResult @@ -6239,8 +6361,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. * {@link ShellCommand#getNextArg()} and {@link ShellCommand#getNextArgRequired()} for the * main arguments.</p> * - * @param shellCommand {@link ShellCommand} from which options should be obtained. - * @return User ID to be resolved. {@link UserHandle#CURRENT} if not specified. + * @param shellCommand {@link ShellCommand} from which options should be obtained + * @return user ID to be resolved. {@link UserHandle#CURRENT} if not specified */ @BinderThread @UserIdInt @@ -6262,12 +6384,12 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. /** * Handles core logic of {@code adb shell ime enable} and {@code adb shell ime disable}. * - * @param userId user ID specified to the command. Pseudo user IDs are not supported. - * @param imeId IME ID specified to the command. - * @param enabled {@code true} for {@code adb shell ime enable}. {@code false} otherwise. - * @param out {@link PrintWriter} to output standard messages. - * @param error {@link PrintWriter} to output error messages. - * @return {@code false} if it fails to enable the IME. {@code false} otherwise. + * @param userId user ID specified to the command (pseudo user IDs are not supported) + * @param imeId IME ID specified to the command + * @param enabled {@code true} for {@code adb shell ime enable} + * @param out {@link PrintWriter} to output standard messages + * @param error {@link PrintWriter} to output error messages + * @return {@code false} if it fails to enable the IME */ @BinderThread @GuardedBy("ImfLock.class") @@ -6325,7 +6447,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. /** * Handles {@code adb shell ime set}. * - * @param shellCommand {@link ShellCommand} object that is handling this command. + * @param shellCommand {@link ShellCommand} object that is handling this command * @return Exit code of the command. */ @BinderThread @@ -6368,7 +6490,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. /** * Handles {@code adb shell ime reset-ime}. - * @param shellCommand {@link ShellCommand} object that is handling this command. + * + * @param shellCommand {@link ShellCommand} object that is handling this command * @return Exit code of the command. */ @BinderThread @@ -6384,8 +6507,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. continue; } // Skip on headless user - if (USER_TYPE_SYSTEM_HEADLESS.equals( - mUserManagerInternal.getUserInfo(userId).userType)) { + final var userInfo = mUserManagerInternal.getUserInfo(userId); + if (userInfo != null && USER_TYPE_SYSTEM_HEADLESS.equals(userInfo.userType)) { continue; } final String nextIme; @@ -6395,7 +6518,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. hideCurrentInputLocked(mImeBindingState.mFocusedWindow, 0 /* flags */, SoftInputShowHideReason.HIDE_RESET_SHELL_COMMAND); final var userData = mUserDataRepository.getOrCreate(userId); - userData.mBindingController.unbindCurrentMethod(); + final var bindingController = userData.mBindingController; + bindingController.unbindCurrentMethod(); // Enable default IMEs, disable others var toDisable = settings.getEnabledInputMethodList(); @@ -6448,7 +6572,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. /** * Handles {@code adb shell cmd input_method tracing start/stop/save-for-bugreport}. - * @param shellCommand {@link ShellCommand} object that is handling this command. + * + * @param shellCommand {@link ShellCommand} object that is handling this command * @return Exit code of the command. */ @BinderThread @@ -6493,9 +6618,9 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. /** * @param userId the actual user handle obtained by {@link UserHandle#getIdentifier()} - * and *not* pseudo ids like {@link UserHandle#USER_ALL etc}. - * @return {@code true} if userId has debugging privileges. - * i.e. {@link UserManager#DISALLOW_DEBUGGING_FEATURES} is {@code false}. + * and *not* pseudo ids like {@link UserHandle#USER_ALL etc} + * @return {@code true} if userId has debugging privileges + * i.e. {@link UserManager#DISALLOW_DEBUGGING_FEATURES} is {@code false} */ private boolean userHasDebugPriv(@UserIdInt int userId, ShellCommand shellCommand) { if (mUserManagerInternal.hasUserRestriction( @@ -6516,8 +6641,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. /** * Creates an IME request tracking token for the current focused client. * - * @param show whether this is a show or a hide request. - * @param reason the reason why the IME request was created. + * @param show whether this is a show or a hide request + * @param reason the reason why the IME request was created */ @NonNull private ImeTracker.Token createStatsTokenForFocusedClient(boolean show, diff --git a/services/core/java/com/android/server/inputmethod/InputMethodMap.java b/services/core/java/com/android/server/inputmethod/InputMethodMap.java index 221309e4bf3e..bab21e8b7b06 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodMap.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodMap.java @@ -23,6 +23,7 @@ import android.annotation.Nullable; import android.util.ArrayMap; import android.view.inputmethod.InputMethodInfo; +import java.util.Arrays; import java.util.List; /** @@ -99,4 +100,37 @@ final class InputMethodMap { } return updated ? InputMethodMap.of(newMethodMap) : this; } + + /** + * Compares the given two {@link InputMethodMap} instances to see if they contain the same data + * or not. + * + * @param map1 {@link InputMethodMap} to be compared with + * @param map2 {@link InputMethodMap} to be compared with + * @return {@code true} if both {@link InputMethodMap} instances contain exactly the same data + */ + @AnyThread + static boolean areSame(@NonNull InputMethodMap map1, @NonNull InputMethodMap map2) { + if (map1 == map2) { + return true; + } + final int size = map1.size(); + if (size != map2.size()) { + return false; + } + for (int i = 0; i < size; ++i) { + final var imi1 = map1.valueAt(i); + final var imeId = imi1.getId(); + final var imi2 = map2.get(imeId); + if (imi2 == null) { + return false; + } + final var marshaled1 = InputMethodInfoUtils.marshal(imi1); + final var marshaled2 = InputMethodInfoUtils.marshal(imi2); + if (!Arrays.equals(marshaled1, marshaled2)) { + return false; + } + } + return true; + } } diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java index 73647dbbe978..e1f893953d66 100644 --- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java +++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java @@ -2800,6 +2800,10 @@ class MediaRouter2ServiceImpl { final String providerId = route.getProviderId(); final MediaRoute2Provider provider = findProvider(providerId); if (provider == null) { + Slog.w( + TAG, + "Ignoring transferToRoute due to lack of matching provider for target: " + + route); return; } provider.transferToRoute( diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java index a3c5d2d336f2..69f07d5c5f7b 100644 --- a/services/core/java/com/android/server/media/MediaSessionRecord.java +++ b/services/core/java/com/android/server/media/MediaSessionRecord.java @@ -1057,6 +1057,7 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde return -1; } + @NonNull private PlaybackInfo getVolumeAttributes() { int volumeType; AudioAttributes attributes; @@ -1850,6 +1851,7 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde return mFlags; } + @NonNull @Override public PlaybackInfo getVolumeAttributes() { return MediaSessionRecord.this.getVolumeAttributes(); diff --git a/services/core/java/com/android/server/notification/NotificationAttentionHelper.java b/services/core/java/com/android/server/notification/NotificationAttentionHelper.java index a78b3a2e0c61..13429db47ebd 100644 --- a/services/core/java/com/android/server/notification/NotificationAttentionHelper.java +++ b/services/core/java/com/android/server/notification/NotificationAttentionHelper.java @@ -22,12 +22,14 @@ import static android.app.Notification.FLAG_ONLY_ALERT_ONCE; import static android.app.NotificationManager.IMPORTANCE_MIN; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE; import static android.media.audio.Flags.focusExclusiveWithRecording; import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS; import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS; import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS; +import android.Manifest.permission; import android.annotation.IntDef; import android.app.ActivityManager; import android.app.KeyguardManager; @@ -223,7 +225,10 @@ public final class NotificationAttentionHelper { mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_T2), mFlagResolver.getIntValue(NotificationFlags.NOTIF_VOLUME1), mFlagResolver.getIntValue(NotificationFlags.NOTIF_VOLUME2), - mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_COUNTER_RESET)); + mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_COUNTER_RESET), + record -> mPackageManager.checkPermission( + permission.RECEIVE_EMERGENCY_BROADCAST, + record.getSbn().getPackageName()) == PERMISSION_GRANTED); return new StrategyAvalanche( mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_T1), @@ -231,14 +236,17 @@ public final class NotificationAttentionHelper { mFlagResolver.getIntValue(NotificationFlags.NOTIF_VOLUME1), mFlagResolver.getIntValue(NotificationFlags.NOTIF_VOLUME2), mFlagResolver.getIntValue(NotificationFlags.NOTIF_AVALANCHE_TIMEOUT), - appStrategy); + appStrategy, appStrategy.mExemptionProvider); } else { return new StrategyPerApp( mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_T1), mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_T2), mFlagResolver.getIntValue(NotificationFlags.NOTIF_VOLUME1), mFlagResolver.getIntValue(NotificationFlags.NOTIF_VOLUME2), - mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_COUNTER_RESET)); + mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_COUNTER_RESET), + record -> mPackageManager.checkPermission( + permission.RECEIVE_EMERGENCY_BROADCAST, + record.getSbn().getPackageName()) == PERMISSION_GRANTED); } } @@ -1098,6 +1106,11 @@ public final class NotificationAttentionHelper { } } + // Returns true if a notification should be exempted from attenuation + private interface ExemptionProvider { + boolean isExempted(NotificationRecord record); + } + @VisibleForTesting abstract static class PolitenessStrategy { static final int POLITE_STATE_DEFAULT = 0; @@ -1128,8 +1141,10 @@ public final class NotificationAttentionHelper { protected boolean mIsActive = true; + protected final ExemptionProvider mExemptionProvider; + public PolitenessStrategy(int timeoutPolite, int timeoutMuted, int volumePolite, - int volumeMuted) { + int volumeMuted, ExemptionProvider exemptionProvider) { mVolumeStates = new HashMap<>(); mLastUpdatedTimestampByPackage = new HashMap<>(); @@ -1137,6 +1152,7 @@ public final class NotificationAttentionHelper { this.mTimeoutMuted = timeoutMuted; this.mVolumePolite = volumePolite / 100.0f; this.mVolumeMuted = volumeMuted / 100.0f; + this.mExemptionProvider = exemptionProvider; } abstract void onNotificationPosted(NotificationRecord record); @@ -1294,8 +1310,8 @@ public final class NotificationAttentionHelper { private final int mMaxPostedForReset; public StrategyPerApp(int timeoutPolite, int timeoutMuted, int volumePolite, - int volumeMuted, int maxPosted) { - super(timeoutPolite, timeoutMuted, volumePolite, volumeMuted); + int volumeMuted, int maxPosted, ExemptionProvider exemptionProvider) { + super(timeoutPolite, timeoutMuted, volumePolite, volumeMuted, exemptionProvider); mNumPosted = new HashMap<>(); mMaxPostedForReset = maxPosted; @@ -1316,7 +1332,12 @@ public final class NotificationAttentionHelper { final String key = getChannelKey(record); @PolitenessState final int currState = getPolitenessState(record); - @PolitenessState int nextState = getNextState(currState, timeSinceLastNotif); + @PolitenessState int nextState; + if (Flags.politeNotificationsAttnUpdate()) { + nextState = getNextState(currState, timeSinceLastNotif, record); + } else { + nextState = getNextState(currState, timeSinceLastNotif); + } // Reset to default state if number of posted notifications exceed this value when muted int numPosted = mNumPosted.getOrDefault(key, 0) + 1; @@ -1334,6 +1355,14 @@ public final class NotificationAttentionHelper { mVolumeStates.put(key, nextState); } + @PolitenessState int getNextState(@PolitenessState final int currState, + final long timeSinceLastNotif, final NotificationRecord record) { + if (mExemptionProvider.isExempted(record)) { + return POLITE_STATE_DEFAULT; + } + return getNextState(currState, timeSinceLastNotif); + } + @Override public void onUserInteraction(final NotificationRecord record) { super.onUserInteraction(record); @@ -1354,8 +1383,9 @@ public final class NotificationAttentionHelper { private long mLastAvalancheTriggerTimestamp = 0; StrategyAvalanche(int timeoutPolite, int timeoutMuted, int volumePolite, - int volumeMuted, int timeoutAvalanche, PolitenessStrategy appStrategy) { - super(timeoutPolite, timeoutMuted, volumePolite, volumeMuted); + int volumeMuted, int timeoutAvalanche, PolitenessStrategy appStrategy, + ExemptionProvider exemptionProvider) { + super(timeoutPolite, timeoutMuted, volumePolite, volumeMuted, exemptionProvider); mTimeoutAvalanche = timeoutAvalanche; mAppStrategy = appStrategy; @@ -1528,7 +1558,7 @@ public final class NotificationAttentionHelper { return true; } - return false; + return mExemptionProvider.isExempted(record); } private boolean isAvalancheExempted(final NotificationRecord record) { diff --git a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java b/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java index b2e861cf2876..cdc1a5e738a0 100644 --- a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java +++ b/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java @@ -16,6 +16,7 @@ package com.android.server.ondeviceintelligence; +import static android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService.DEVICE_CONFIG_UPDATE_BUNDLE_KEY; import static android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService.MODEL_LOADED_BUNDLE_KEY; import static android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService.MODEL_UNLOADED_BUNDLE_KEY; import static android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService.REGISTER_MODEL_UPDATE_CALLBACK_BUNDLE_KEY; @@ -115,9 +116,10 @@ public class OnDeviceIntelligenceManagerService extends SystemService { /** Handler message to {@link #resetTemporaryServices()} */ private static final int MSG_RESET_TEMPORARY_SERVICE = 0; - /** Handler message to clean up temporary broadcast keys. */ private static final int MSG_RESET_BROADCAST_KEYS = 1; + /** Handler message to clean up temporary config namespace. */ + private static final int MSG_RESET_CONFIG_NAMESPACE = 2; /** Default value in absence of {@link DeviceConfig} override. */ private static final boolean DEFAULT_SERVICE_ENABLED = true; @@ -129,6 +131,7 @@ public class OnDeviceIntelligenceManagerService extends SystemService { private final Executor resourceClosingExecutor = Executors.newCachedThreadPool(); private final Executor callbackExecutor = Executors.newCachedThreadPool(); private final Executor broadcastExecutor = Executors.newCachedThreadPool(); + private final Executor mConfigExecutor = Executors.newCachedThreadPool(); private final Context mContext; @@ -145,6 +148,12 @@ public class OnDeviceIntelligenceManagerService extends SystemService { private String[] mTemporaryBroadcastKeys; @GuardedBy("mLock") private String mBroadcastPackageName; + @GuardedBy("mLock") + private String mTemporaryConfigNamespace; + + private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener = + this::sendUpdatedConfig; + /** * Handler used to reset the temporary service names. @@ -593,12 +602,14 @@ public class OnDeviceIntelligenceManagerService extends SystemService { @NonNull IOnDeviceSandboxedInferenceService service) { try { ensureRemoteIntelligenceServiceInitialized(); + service.registerRemoteStorageService( + getIRemoteStorageService()); mRemoteOnDeviceIntelligenceService.run( IOnDeviceIntelligenceService::notifyInferenceServiceConnected); broadcastExecutor.execute( () -> registerModelLoadingBroadcasts(service)); - service.registerRemoteStorageService( - getIRemoteStorageService()); + mConfigExecutor.execute( + () -> registerDeviceConfigChangeListener()); } catch (RemoteException ex) { Slog.w(TAG, "Failed to send connected event", ex); } @@ -658,6 +669,58 @@ public class OnDeviceIntelligenceManagerService extends SystemService { } } + private void registerDeviceConfigChangeListener() { + Log.e(TAG, "registerDeviceConfigChangeListener"); + String configNamespace = getConfigNamespace(); + if (configNamespace.isEmpty()) { + Slog.e(TAG, "config_defaultOnDeviceIntelligenceDeviceConfigNamespace is empty"); + return; + } + DeviceConfig.addOnPropertiesChangedListener( + configNamespace, + mConfigExecutor, + mOnPropertiesChangedListener); + } + + private String getConfigNamespace() { + synchronized (mLock) { + if (mTemporaryConfigNamespace != null) { + return mTemporaryConfigNamespace; + } + + return mContext.getResources().getString( + R.string.config_defaultOnDeviceIntelligenceDeviceConfigNamespace); + } + } + + private void sendUpdatedConfig( + DeviceConfig.Properties props) { + Log.e(TAG, "sendUpdatedConfig"); + + PersistableBundle persistableBundle = new PersistableBundle(); + for (String key : props.getKeyset()) { + persistableBundle.putString(key, props.getString(key, "")); + } + Bundle bundle = new Bundle(); + bundle.putParcelable(DEVICE_CONFIG_UPDATE_BUNDLE_KEY, persistableBundle); + ensureRemoteInferenceServiceInitialized(); + Log.e(TAG, "sendUpdatedConfig: BUNDLE: " + bundle); + + mRemoteInferenceService.run(service -> service.updateProcessingState(bundle, + new IProcessingUpdateStatusCallback.Stub() { + @Override + public void onSuccess(PersistableBundle result) { + Slog.d(TAG, "Config update successful." + result); + } + + @Override + public void onFailure(int errorCode, String errorMessage) { + Slog.e(TAG, "Config update failed with code [" + + String.valueOf(errorCode) + "] and message = " + errorMessage); + } + })); + } + @NonNull private IRemoteStorageService.Stub getIRemoteStorageService() { return new IRemoteStorageService.Stub() { @@ -849,8 +912,23 @@ public class OnDeviceIntelligenceManagerService extends SystemService { } @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE) + public void setTemporaryDeviceConfigNamespace(@NonNull String configNamespace, + int durationMs) { + Objects.requireNonNull(configNamespace); + enforceShellOnly(Binder.getCallingUid(), "setTemporaryDeviceConfigNamespace"); + mContext.enforceCallingPermission( + Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG); + synchronized (mLock) { + mTemporaryConfigNamespace = configNamespace; + if (durationMs != -1) { + getTemporaryHandler().sendEmptyMessageDelayed(MSG_RESET_CONFIG_NAMESPACE, + durationMs); + } + } + } + + @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE) public void resetTemporaryServices() { - enforceShellOnly(Binder.getCallingUid(), "resetTemporaryServices"); mContext.enforceCallingPermission( Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG); synchronized (mLock) { @@ -937,17 +1015,17 @@ public class OnDeviceIntelligenceManagerService extends SystemService { mTemporaryHandler = new Handler(Looper.getMainLooper(), null, true) { @Override public void handleMessage(Message msg) { - if (msg.what == MSG_RESET_TEMPORARY_SERVICE) { - synchronized (mLock) { + synchronized (mLock) { + if (msg.what == MSG_RESET_TEMPORARY_SERVICE) { resetTemporaryServices(); - } - } else if (msg.what == MSG_RESET_BROADCAST_KEYS) { - synchronized (mLock) { + } else if (msg.what == MSG_RESET_BROADCAST_KEYS) { mTemporaryBroadcastKeys = null; mBroadcastPackageName = SYSTEM_PACKAGE; + } else if (msg.what == MSG_RESET_CONFIG_NAMESPACE) { + mTemporaryConfigNamespace = null; + } else { + Slog.wtf(TAG, "invalid handler msg: " + msg); } - } else { - Slog.wtf(TAG, "invalid handler msg: " + msg); } } }; diff --git a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceShellCommand.java b/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceShellCommand.java index 5744b5c3c2c4..d2c84fa1b18a 100644 --- a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceShellCommand.java +++ b/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceShellCommand.java @@ -17,6 +17,7 @@ package com.android.server.ondeviceintelligence; import android.annotation.NonNull; +import android.os.Binder; import android.os.ShellCommand; import java.io.PrintWriter; @@ -45,6 +46,8 @@ final class OnDeviceIntelligenceShellCommand extends ShellCommand { return getConfiguredServices(); case "set-model-broadcasts": return setBroadcastKeys(); + case "set-deviceconfig-namespace": + return setDeviceConfigNamespace(); default: return handleDefaultCommands(cmd); } @@ -69,6 +72,10 @@ final class OnDeviceIntelligenceShellCommand extends ShellCommand { + "[ReceiverPackageName] " + "[DURATION] To set the names of broadcast intent keys that are to be " + "emitted for cts tests."); + pw.println( + " set-deviceconfig-namespace [DeviceConfigNamespace] " + + "[DURATION] To set the device config namespace " + + "to use for cts tests."); } private int setTemporaryServices() { @@ -78,6 +85,8 @@ final class OnDeviceIntelligenceShellCommand extends ShellCommand { if (getRemainingArgsCount() == 0 && intelligenceServiceName == null && inferenceServiceName == null) { + OnDeviceIntelligenceManagerService.enforceShellOnly(Binder.getCallingUid(), + "resetTemporaryServices"); mService.resetTemporaryServices(); out.println("OnDeviceIntelligenceManagerService temporary reset. "); return 0; @@ -120,4 +129,16 @@ final class OnDeviceIntelligenceShellCommand extends ShellCommand { return 0; } + private int setDeviceConfigNamespace() { + final PrintWriter out = getOutPrintWriter(); + final String configNamespace = getNextArg(); + + final int duration = Integer.parseInt(getNextArgRequired()); + mService.setTemporaryDeviceConfigNamespace(configNamespace, duration); + out.println("OnDeviceIntelligence DeviceConfig Namespace temporarily set to " + + configNamespace + + " for " + duration + "ms"); + return 0; + } + }
\ No newline at end of file diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java index 908b47df35e1..472f228b689f 100644 --- a/services/core/java/com/android/server/pm/InstallPackageHelper.java +++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java @@ -37,7 +37,7 @@ import static android.content.pm.PackageManager.INSTALL_FAILED_UID_CHANGED; import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE; import static android.content.pm.PackageManager.INSTALL_STAGED; import static android.content.pm.PackageManager.INSTALL_SUCCEEDED; -import static android.content.pm.PackageManager.PROPERTY_ANDROID_SAFETY_LABEL_PATH; +import static android.content.pm.PackageManager.PROPERTY_ANDROID_SAFETY_LABEL; import static android.content.pm.PackageManager.UNINSTALL_REASON_UNKNOWN; import static android.content.pm.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V4; import static android.content.pm.parsing.ApkLiteParseUtils.isApkFile; @@ -505,6 +505,7 @@ final class InstallPackageHelper { // metadata file path for the new package. if (oldPkgSetting != null) { pkgSetting.setAppMetadataFilePath(null); + pkgSetting.setAppMetadataSource(APP_METADATA_SOURCE_UNKNOWN); } // If the app metadata file path is not null then this is a system app with a preloaded app // metadata file on the system image. Do not reset the path and source if this is the @@ -523,7 +524,7 @@ final class InstallPackageHelper { } } else if (Flags.aslInApkAppMetadataSource()) { Map<String, PackageManager.Property> properties = pkg.getProperties(); - if (properties.containsKey(PROPERTY_ANDROID_SAFETY_LABEL_PATH)) { + if (properties.containsKey(PROPERTY_ANDROID_SAFETY_LABEL)) { // ASL file extraction is done in post-install pkgSetting.setAppMetadataFilePath(appMetadataFile.getAbsolutePath()); pkgSetting.setAppMetadataSource(APP_METADATA_SOURCE_APK); diff --git a/services/core/java/com/android/server/pm/PackageFreezer.java b/services/core/java/com/android/server/pm/PackageFreezer.java index 7c5615771607..0afda4598bcb 100644 --- a/services/core/java/com/android/server/pm/PackageFreezer.java +++ b/services/core/java/com/android/server/pm/PackageFreezer.java @@ -18,6 +18,7 @@ package com.android.server.pm; import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.pm.Flags; import android.content.pm.PackageManager; import dalvik.system.CloseGuard; @@ -76,8 +77,13 @@ final class PackageFreezer implements AutoCloseable { ps = mPm.mSettings.getPackageLPr(mPackageName); } if (ps != null) { - mPm.killApplication(ps.getPackageName(), ps.getAppId(), userId, killReason, - exitInfoReason); + if (Flags.waitApplicationKilled()) { + mPm.killApplicationSync(ps.getPackageName(), ps.getAppId(), userId, killReason, + exitInfoReason); + } else { + mPm.killApplication(ps.getPackageName(), ps.getAppId(), userId, killReason, + exitInfoReason); + } } mCloseGuard.open("close"); } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 121cf3f231b0..f8fceda0582c 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -53,6 +53,7 @@ import android.annotation.StringRes; import android.annotation.UserIdInt; import android.annotation.WorkerThread; import android.app.ActivityManager; +import android.app.ActivityManagerInternal; import android.app.AppOpsManager; import android.app.ApplicationExitInfo; import android.app.ApplicationPackageManager; @@ -3132,6 +3133,20 @@ public class PackageManagerService implements PackageSender, TestUtilityService } } + void killApplicationSync(String pkgName, @AppIdInt int appId, + @UserIdInt int userId, String reason, int exitInfoReason) { + ActivityManagerInternal mAmi = LocalServices.getService(ActivityManagerInternal.class); + if (mAmi != null) { + if (Thread.holdsLock(mLock)) { + // holds PM's lock, go back killApplication to avoid it run into watchdog reset. + Slog.e(TAG, "Holds PM's locker, unable kill application synchronized"); + killApplication(pkgName, appId, userId, reason, exitInfoReason); + } else { + mAmi.killApplicationSync(pkgName, appId, userId, reason, exitInfoReason); + } + } + } + @Override public void notifyPackageAdded(String packageName, int uid) { mPackageObserverHelper.notifyAdded(packageName, uid); @@ -4003,7 +4018,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService final PackageMetrics.ComponentStateMetrics componentStateMetrics = new PackageMetrics.ComponentStateMetrics(setting, UserHandle.getUid(userId, packageSetting.getAppId()), - packageSetting.getEnabled(userId)); + packageSetting.getEnabled(userId), callingUid); if (!setEnabledSettingInternalLocked(computer, packageSetting, setting, userId, callingPackage)) { continue; diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java index b369f03d002f..23ae98325cb9 100644 --- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java +++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java @@ -19,7 +19,7 @@ package com.android.server.pm; import static android.content.pm.PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE; import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE; import static android.content.pm.PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE; -import static android.content.pm.PackageManager.PROPERTY_ANDROID_SAFETY_LABEL_PATH; +import static android.content.pm.PackageManager.PROPERTY_ANDROID_SAFETY_LABEL; import static android.content.pm.SigningDetails.CertCapabilities.SHARED_USER_ID; import static android.system.OsConstants.O_CREAT; import static android.system.OsConstants.O_RDWR; @@ -71,8 +71,12 @@ import android.content.pm.parsing.ApkLiteParseUtils; import android.content.pm.parsing.PackageLite; import android.content.pm.parsing.result.ParseResult; import android.content.pm.parsing.result.ParseTypeImpl; +import android.content.res.ApkAssets; +import android.content.res.AssetManager; +import android.content.res.Resources; import android.os.Binder; import android.os.Build; +import android.os.CancellationSignal; import android.os.Debug; import android.os.Environment; import android.os.FileUtils; @@ -93,6 +97,7 @@ import android.system.Os; import android.util.ArraySet; import android.util.AtomicFile; import android.util.Base64; +import android.util.DisplayMetrics; import android.util.Log; import android.util.LogPrinter; import android.util.Printer; @@ -147,11 +152,10 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Function; import java.util.function.Predicate; import java.util.zip.GZIPInputStream; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; /** * Class containing helper methods for the PackageManagerService. @@ -1668,11 +1672,11 @@ public class PackageManagerServiceUtils { return true; } Map<String, Property> properties = pkg.getProperties(); - if (!properties.containsKey(PROPERTY_ANDROID_SAFETY_LABEL_PATH)) { + if (!properties.containsKey(PROPERTY_ANDROID_SAFETY_LABEL)) { return false; } - Property fileInAPkPathProperty = properties.get(PROPERTY_ANDROID_SAFETY_LABEL_PATH); - if (!fileInAPkPathProperty.isString()) { + Property fileInApkProperty = properties.get(PROPERTY_ANDROID_SAFETY_LABEL); + if (!fileInApkProperty.isResourceId()) { return false; } if (isSystem && !appMetadataFile.getParentFile().exists()) { @@ -1684,28 +1688,46 @@ public class PackageManagerServiceUtils { return false; } } - String fileInApkPath = fileInAPkPathProperty.getString(); List<AndroidPackageSplit> splits = pkg.getSplits(); + AssetManager.Builder builder = new AssetManager.Builder(); for (int i = 0; i < splits.size(); i++) { - try (ZipFile zipFile = new ZipFile(splits.get(i).getPath())) { - ZipEntry zipEntry = zipFile.getEntry(fileInApkPath); - if (zipEntry != null - && (isSystem || zipEntry.getSize() <= getAppMetadataSizeLimit())) { - try (InputStream in = zipFile.getInputStream(zipEntry)) { - try (FileOutputStream out = new FileOutputStream(appMetadataFile)) { - FileUtils.copy(in, out); - Os.chmod(appMetadataFile.getAbsolutePath(), - APP_METADATA_FILE_ACCESS_MODE); - return true; + try { + builder.addApkAssets(ApkAssets.loadFromPath(splits.get(i).getPath())); + } catch (IOException e) { + Slog.e(TAG, "Failed to load resources from APK " + splits.get(i).getPath()); + } + } + AssetManager assetManager = builder.build(); + DisplayMetrics displayMetrics = new DisplayMetrics(); + displayMetrics.setToDefaults(); + Resources res = new Resources(assetManager, displayMetrics, null); + AtomicBoolean copyFailed = new AtomicBoolean(false); + try (InputStream in = res.openRawResource(fileInApkProperty.getResourceId())) { + try (FileOutputStream out = new FileOutputStream(appMetadataFile)) { + if (isSystem) { + FileUtils.copy(in, out); + } else { + long sizeLimit = getAppMetadataSizeLimit(); + CancellationSignal signal = new CancellationSignal(); + FileUtils.copy(in, out, signal, Runnable::run, (long progress) -> { + if (progress > sizeLimit) { + copyFailed.set(true); + signal.cancel(); } - } + }); } - } catch (Exception e) { - Slog.e(TAG, e.getMessage()); + Os.chmod(appMetadataFile.getAbsolutePath(), + APP_METADATA_FILE_ACCESS_MODE); + } + } catch (Exception e) { + Slog.e(TAG, e.getMessage()); + copyFailed.set(true); + } finally { + if (copyFailed.get()) { appMetadataFile.delete(); } } - return false; + return !copyFailed.get(); } public static void linkFilesToOldDirs(@NonNull Installer installer, diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index 7a36f6dabe06..0a8b2b2c6219 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -328,6 +328,8 @@ class PackageManagerShellCommand extends ShellCommand { return runGetPrivappDenyPermissions(); case "get-oem-permissions": return runGetOemPermissions(); + case "get-signature-permission-allowlist": + return runGetSignaturePermissionAllowlist(); case "trim-caches": return runTrimCaches(); case "create-user": @@ -2920,6 +2922,54 @@ class PackageManagerShellCommand extends ShellCommand { return 0; } + private int runGetSignaturePermissionAllowlist() { + final var partition = getNextArg(); + if (partition == null) { + getErrPrintWriter().println("Error: no partition specified."); + return 1; + } + final var permissionAllowlist = + SystemConfig.getInstance().getPermissionAllowlist(); + final ArrayMap<String, ArrayMap<String, Boolean>> allowlist; + switch (partition) { + case "system": + allowlist = permissionAllowlist.getSignatureAppAllowlist(); + break; + case "vendor": + allowlist = permissionAllowlist.getVendorSignatureAppAllowlist(); + break; + case "product": + allowlist = permissionAllowlist.getProductSignatureAppAllowlist(); + break; + case "system-ext": + allowlist = permissionAllowlist.getSystemExtSignatureAppAllowlist(); + break; + default: + getErrPrintWriter().println("Error: unknown partition: " + partition); + return 1; + } + final var ipw = new IndentingPrintWriter(getOutPrintWriter(), " "); + final var allowlistSize = allowlist.size(); + for (var allowlistIndex = 0; allowlistIndex < allowlistSize; allowlistIndex++) { + final var packageName = allowlist.keyAt(allowlistIndex); + final var permissions = allowlist.valueAt(allowlistIndex); + ipw.print("Package: "); + ipw.println(packageName); + ipw.increaseIndent(); + final var permissionsSize = permissions.size(); + for (var permissionsIndex = 0; permissionsIndex < permissionsSize; permissionsIndex++) { + final var permissionName = permissions.keyAt(permissionsIndex); + final var granted = permissions.valueAt(permissionsIndex); + if (granted) { + ipw.print("Permission: "); + ipw.println(permissionName); + } + } + ipw.decreaseIndent(); + } + return 0; + } + private int runTrimCaches() throws RemoteException { String size = getNextArg(); if (size == null) { @@ -4852,6 +4902,10 @@ class PackageManagerShellCommand extends ShellCommand { pw.println(" get-oem-permissions TARGET-PACKAGE"); pw.println(" Prints all OEM permissions for a package."); pw.println(""); + pw.println(" get-signature-permission-allowlist PARTITION"); + pw.println(" Prints the signature permission allowlist for a partition."); + pw.println(" PARTITION is one of system, vendor, product and system-ext"); + pw.println(""); pw.println(" trim-caches DESIRED_FREE_SPACE [internal|UUID]"); pw.println(" Trim cache files to reach the given free space."); pw.println(""); diff --git a/services/core/java/com/android/server/pm/PackageMetrics.java b/services/core/java/com/android/server/pm/PackageMetrics.java index 20598f91a51d..2081f73e7336 100644 --- a/services/core/java/com/android/server/pm/PackageMetrics.java +++ b/services/core/java/com/android/server/pm/PackageMetrics.java @@ -358,6 +358,7 @@ final class PackageMetrics { public static class ComponentStateMetrics { public int mUid; + public int mCallingUid; public int mComponentOldState; public int mComponentNewState; public boolean mIsForWholeApp; @@ -365,13 +366,14 @@ final class PackageMetrics { @Nullable private String mClassName; ComponentStateMetrics(@NonNull PackageManager.ComponentEnabledSetting setting, int uid, - int componentOldState) { + int componentOldState, int callingUid) { mUid = uid; mComponentOldState = componentOldState; mComponentNewState = setting.getEnabledState(); mIsForWholeApp = !setting.isComponent(); mPackageName = setting.getPackageName(); mClassName = setting.getClassName(); + mCallingUid = callingUid; } public boolean isSameComponent(ActivityInfo activityInfo) { @@ -412,14 +414,15 @@ final class PackageMetrics { componentStateMetrics.mComponentOldState, componentStateMetrics.mComponentNewState, isLauncher, - componentStateMetrics.mIsForWholeApp); + componentStateMetrics.mIsForWholeApp, + componentStateMetrics.mCallingUid); } } private static void reportComponentStateChanged(int uid, int componentOldState, - int componentNewState, boolean isLauncher, boolean isForWholeApp) { + int componentNewState, boolean isLauncher, boolean isForWholeApp, int callingUid) { FrameworkStatsLog.write(FrameworkStatsLog.COMPONENT_STATE_CHANGED_REPORTED, - uid, componentOldState, componentNewState, isLauncher, isForWholeApp); + uid, componentOldState, componentNewState, isLauncher, isForWholeApp, callingUid); } private static List<ResolveInfo> getHomeActivitiesResolveInfoAsUser(@NonNull Computer computer, diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 41d6288d4411..8d6d774a9959 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -2142,7 +2142,8 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile ComponentName unflattenOriginalComponentName = ComponentName.unflattenFromString( originalComponentName); if (unflattenOriginalComponentName == null) { - Slog.d(TAG, "Incorrect component name from the attributes"); + Slog.wtf(TAG, "Incorrect component name: " + originalComponentName + + " from the attributes"); continue; } diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 4ff345fbedf9..ebdca5bcec6f 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -1924,16 +1924,20 @@ public class UserManagerService extends IUserManager.Stub { private void showConfirmCredentialToDisableQuietMode( @UserIdInt int userId, @Nullable IntentSender target, @Nullable String callingPackage) { if (android.app.admin.flags.Flags.quietModeCredentialBugFix()) { - // TODO (b/308121702) It may be brittle to rely on user states to check profile state - int state; - synchronized (mUserStates) { - state = mUserStates.get(userId, UserState.STATE_NONE); - } - if (state != UserState.STATE_NONE) { - Slog.i(LOG_TAG, - "showConfirmCredentialToDisableQuietMode() called too early, user " + userId - + " is still alive."); - return; + if (!android.multiuser.Flags.restrictQuietModeCredentialBugFixToManagedProfiles() + || getUserInfo(userId).isManagedProfile()) { + // TODO (b/308121702) It may be brittle to rely on user states to check managed + // profile state + int state; + synchronized (mUserStates) { + state = mUserStates.get(userId, UserState.STATE_NONE); + } + if (state != UserState.STATE_NONE) { + Slog.i(LOG_TAG, + "showConfirmCredentialToDisableQuietMode() called too early, managed " + + "user " + userId + " is still alive."); + return; + } } } // otherwise, we show a profile challenge to trigger decryption of the user diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java index 49c4000d7308..9a4155122402 100644 --- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java +++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java @@ -463,11 +463,17 @@ public class BatteryStatsImpl extends BatteryStats { public static class BatteryStatsConfig { static final int RESET_ON_UNPLUG_HIGH_BATTERY_LEVEL_FLAG = 1 << 0; static final int RESET_ON_UNPLUG_AFTER_SIGNIFICANT_CHARGE_FLAG = 1 << 1; - static final long DEFAULT_POWER_STATS_COLLECTION_THROTTLE_PERIOD = - TimeUnit.HOURS.toMillis(1); private final int mFlags; - private SparseLongArray mPowerStatsThrottlePeriods; + private final Long mDefaultPowerStatsThrottlePeriod; + private final Map<String, Long> mPowerStatsThrottlePeriods; + + @VisibleForTesting + public BatteryStatsConfig() { + mFlags = 0; + mDefaultPowerStatsThrottlePeriod = 0L; + mPowerStatsThrottlePeriods = Map.of(); + } private BatteryStatsConfig(Builder builder) { int flags = 0; @@ -478,6 +484,7 @@ public class BatteryStatsImpl extends BatteryStats { flags |= RESET_ON_UNPLUG_AFTER_SIGNIFICANT_CHARGE_FLAG; } mFlags = flags; + mDefaultPowerStatsThrottlePeriod = builder.mDefaultPowerStatsThrottlePeriod; mPowerStatsThrottlePeriods = builder.mPowerStatsThrottlePeriods; } @@ -485,7 +492,7 @@ public class BatteryStatsImpl extends BatteryStats { * Returns whether a BatteryStats reset should occur on unplug when the battery level is * high. */ - boolean shouldResetOnUnplugHighBatteryLevel() { + public boolean shouldResetOnUnplugHighBatteryLevel() { return (mFlags & RESET_ON_UNPLUG_HIGH_BATTERY_LEVEL_FLAG) == RESET_ON_UNPLUG_HIGH_BATTERY_LEVEL_FLAG; } @@ -494,14 +501,18 @@ public class BatteryStatsImpl extends BatteryStats { * Returns whether a BatteryStats reset should occur on unplug if the battery charge a * significant amount since it has been plugged in. */ - boolean shouldResetOnUnplugAfterSignificantCharge() { + public boolean shouldResetOnUnplugAfterSignificantCharge() { return (mFlags & RESET_ON_UNPLUG_AFTER_SIGNIFICANT_CHARGE_FLAG) == RESET_ON_UNPLUG_AFTER_SIGNIFICANT_CHARGE_FLAG; } - long getPowerStatsThrottlePeriod(@BatteryConsumer.PowerComponent int powerComponent) { - return mPowerStatsThrottlePeriods.get(powerComponent, - DEFAULT_POWER_STATS_COLLECTION_THROTTLE_PERIOD); + /** + * Returns the minimum amount of time (in millis) to wait between passes + * of power stats collection for the specified power component. + */ + public long getPowerStatsThrottlePeriod(String powerComponentName) { + return mPowerStatsThrottlePeriods.getOrDefault(powerComponentName, + mDefaultPowerStatsThrottlePeriod); } /** @@ -510,18 +521,19 @@ public class BatteryStatsImpl extends BatteryStats { public static class Builder { private boolean mResetOnUnplugHighBatteryLevel; private boolean mResetOnUnplugAfterSignificantCharge; - private SparseLongArray mPowerStatsThrottlePeriods; + public static final long DEFAULT_POWER_STATS_THROTTLE_PERIOD = + TimeUnit.HOURS.toMillis(1); + public static final long DEFAULT_POWER_STATS_THROTTLE_PERIOD_CPU = + TimeUnit.MINUTES.toMillis(1); + private long mDefaultPowerStatsThrottlePeriod = DEFAULT_POWER_STATS_THROTTLE_PERIOD; + private final Map<String, Long> mPowerStatsThrottlePeriods = new HashMap<>(); public Builder() { mResetOnUnplugHighBatteryLevel = true; mResetOnUnplugAfterSignificantCharge = true; - mPowerStatsThrottlePeriods = new SparseLongArray(); - setPowerStatsThrottlePeriodMillis(BatteryConsumer.POWER_COMPONENT_CPU, - TimeUnit.MINUTES.toMillis(1)); - setPowerStatsThrottlePeriodMillis(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO, - TimeUnit.HOURS.toMillis(1)); - setPowerStatsThrottlePeriodMillis(BatteryConsumer.POWER_COMPONENT_WIFI, - TimeUnit.HOURS.toMillis(1)); + setPowerStatsThrottlePeriodMillis(BatteryConsumer.powerComponentIdToString( + BatteryConsumer.POWER_COMPONENT_CPU), + DEFAULT_POWER_STATS_THROTTLE_PERIOD_CPU); } /** @@ -553,9 +565,18 @@ public class BatteryStatsImpl extends BatteryStats { * Sets the minimum amount of time (in millis) to wait between passes * of power stats collection for the specified power component. */ - public Builder setPowerStatsThrottlePeriodMillis( - @BatteryConsumer.PowerComponent int powerComponent, long periodMs) { - mPowerStatsThrottlePeriods.put(powerComponent, periodMs); + public Builder setPowerStatsThrottlePeriodMillis(String powerComponentName, + long periodMs) { + mPowerStatsThrottlePeriods.put(powerComponentName, periodMs); + return this; + } + + /** + * Sets the minimum amount of time (in millis) to wait between passes + * of power stats collection for any components not configured explicitly. + */ + public Builder setDefaultPowerStatsThrottlePeriodMillis(long periodMs) { + mDefaultPowerStatsThrottlePeriod = periodMs; return this; } } @@ -1586,8 +1607,7 @@ public class BatteryStatsImpl extends BatteryStats { protected final Constants mConstants; @VisibleForTesting - @GuardedBy("this") - protected BatteryStatsConfig mBatteryStatsConfig; + protected final BatteryStatsConfig mBatteryStatsConfig; @GuardedBy("this") private AlarmManager mAlarmManager = null; @@ -1933,6 +1953,11 @@ public class BatteryStatsImpl extends BatteryStats { } @Override + public long getPowerStatsCollectionThrottlePeriod(String powerComponentName) { + return mBatteryStatsConfig.getPowerStatsThrottlePeriod(powerComponentName); + } + + @Override public PowerStatsUidResolver getUidResolver() { return mPowerStatsUidResolver; } @@ -11167,19 +11192,14 @@ public class BatteryStatsImpl extends BatteryStats { mConstants.MAX_HISTORY_FILES, mConstants.MAX_HISTORY_BUFFER, mStepDetailsCalculator, mClock, mMonotonicClock, traceDelegate, eventLogger); - mCpuPowerStatsCollector = new CpuPowerStatsCollector(mPowerStatsCollectorInjector, - mBatteryStatsConfig.getPowerStatsThrottlePeriod( - BatteryConsumer.POWER_COMPONENT_CPU)); + mCpuPowerStatsCollector = new CpuPowerStatsCollector(mPowerStatsCollectorInjector); mCpuPowerStatsCollector.addConsumer(this::recordPowerStats); mMobileRadioPowerStatsCollector = new MobileRadioPowerStatsCollector( - mPowerStatsCollectorInjector, mBatteryStatsConfig.getPowerStatsThrottlePeriod( - BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)); + mPowerStatsCollectorInjector); mMobileRadioPowerStatsCollector.addConsumer(this::recordPowerStats); - mWifiPowerStatsCollector = new WifiPowerStatsCollector( - mPowerStatsCollectorInjector, mBatteryStatsConfig.getPowerStatsThrottlePeriod( - BatteryConsumer.POWER_COMPONENT_WIFI)); + mWifiPowerStatsCollector = new WifiPowerStatsCollector(mPowerStatsCollectorInjector); mWifiPowerStatsCollector.addConsumer(this::recordPowerStats); mStartCount++; diff --git a/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java index f53a1b0682c0..b5ef67b44e75 100644 --- a/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java +++ b/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java @@ -59,6 +59,7 @@ public class CpuPowerStatsCollector extends PowerStatsCollector { KernelCpuStatsReader getKernelCpuStatsReader(); ConsumedEnergyRetriever getConsumedEnergyRetriever(); IntSupplier getVoltageSupplier(); + long getPowerStatsCollectionThrottlePeriod(String powerComponentName); default int getDefaultCpuPowerBrackets() { return DEFAULT_CPU_POWER_BRACKETS; @@ -94,9 +95,11 @@ public class CpuPowerStatsCollector extends PowerStatsCollector { private int mLastVoltageMv; private long[] mLastConsumedEnergyUws; - public CpuPowerStatsCollector(Injector injector, long throttlePeriodMs) { - super(injector.getHandler(), throttlePeriodMs, injector.getUidResolver(), - injector.getClock()); + CpuPowerStatsCollector(Injector injector) { + super(injector.getHandler(), injector.getPowerStatsCollectionThrottlePeriod( + BatteryConsumer.powerComponentIdToString( + BatteryConsumer.POWER_COMPONENT_CPU)), + injector.getUidResolver(), injector.getClock()); mInjector = injector; } diff --git a/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsCollector.java index 7bc681752802..a96e01bdeadf 100644 --- a/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsCollector.java +++ b/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsCollector.java @@ -73,6 +73,7 @@ public class MobileRadioPowerStatsCollector extends PowerStatsCollector { Handler getHandler(); Clock getClock(); PowerStatsUidResolver getUidResolver(); + long getPowerStatsCollectionThrottlePeriod(String powerComponentName); PackageManager getPackageManager(); ConsumedEnergyRetriever getConsumedEnergyRetriever(); IntSupplier getVoltageSupplier(); @@ -104,8 +105,11 @@ public class MobileRadioPowerStatsCollector extends PowerStatsCollector { private long mLastCallDuration; private long mLastScanDuration; - public MobileRadioPowerStatsCollector(Injector injector, long throttlePeriodMs) { - super(injector.getHandler(), throttlePeriodMs, injector.getUidResolver(), + MobileRadioPowerStatsCollector(Injector injector) { + super(injector.getHandler(), injector.getPowerStatsCollectionThrottlePeriod( + BatteryConsumer.powerComponentIdToString( + BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)), + injector.getUidResolver(), injector.getClock()); mInjector = injector; } diff --git a/services/core/java/com/android/server/power/stats/WifiPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/WifiPowerStatsCollector.java index 632105352ad2..bd04199fc227 100644 --- a/services/core/java/com/android/server/power/stats/WifiPowerStatsCollector.java +++ b/services/core/java/com/android/server/power/stats/WifiPowerStatsCollector.java @@ -56,6 +56,7 @@ public class WifiPowerStatsCollector extends PowerStatsCollector { Handler getHandler(); Clock getClock(); PowerStatsUidResolver getUidResolver(); + long getPowerStatsCollectionThrottlePeriod(String powerComponentName); PackageManager getPackageManager(); ConsumedEnergyRetriever getConsumedEnergyRetriever(); IntSupplier getVoltageSupplier(); @@ -92,9 +93,11 @@ public class WifiPowerStatsCollector extends PowerStatsCollector { private final SparseArray<WifiScanTimes> mLastScanTimes = new SparseArray<>(); private long mLastWifiActiveDuration; - public WifiPowerStatsCollector(Injector injector, long throttlePeriodMs) { - super(injector.getHandler(), throttlePeriodMs, injector.getUidResolver(), - injector.getClock()); + WifiPowerStatsCollector(Injector injector) { + super(injector.getHandler(), injector.getPowerStatsCollectionThrottlePeriod( + BatteryConsumer.powerComponentIdToString( + BatteryConsumer.POWER_COMPONENT_WIFI)), + injector.getUidResolver(), injector.getClock()); mInjector = injector; } diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java index 3b9ad1915478..c1b825b3f8d1 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -836,9 +836,7 @@ public class StatsPullAtomService extends SystemService { registerEventListeners(); }); } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) { - if (true) { - initNetworkStatsManager(); - } + initNetworkStatsManager(); BackgroundThread.getHandler().post(() -> { // Network stats related pullers can only be initialized after service is ready. initAndRegisterNetworkStatsPullers(); @@ -859,9 +857,6 @@ public class StatsPullAtomService extends SystemService { mContext.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE); mStatsSubscriptionsListener = new StatsSubscriptionsListener(mSubscriptionManager); mStorageManager = (StorageManager) mContext.getSystemService(StorageManager.class); - if (false) { - initNetworkStatsManager(); - } // Initialize DiskIO mStoragedUidIoStatsReader = new StoragedUidIoStatsReader(); @@ -1043,10 +1038,8 @@ public class StatsPullAtomService extends SystemService { */ @NonNull private NetworkStatsManager getNetworkStatsManager() { - if (true) { - if (mNetworkStatsManager == null) { - throw new IllegalStateException("NetworkStatsManager is not ready"); - } + if (mNetworkStatsManager == null) { + throw new IllegalStateException("NetworkStatsManager is not ready"); } return mNetworkStatsManager; } diff --git a/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java b/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java index f6afc52fd8d8..3393d3e049e1 100644 --- a/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java +++ b/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java @@ -150,7 +150,11 @@ public final class AccessibilityWindowsPopulator extends WindowInfosListener { @Override public void onWindowInfosChanged(InputWindowHandle[] windowHandles, DisplayInfo[] displayInfos) { - mHandler.post(() -> onWindowInfosChangedInternal(windowHandles, displayInfos)); + if (com.android.server.accessibility.Flags.removeOnWindowInfosChangedHandler()) { + onWindowInfosChangedInternal(windowHandles, displayInfos); + } else { + mHandler.post(() -> onWindowInfosChangedInternal(windowHandles, displayInfos)); + } } private void onWindowInfosChangedInternal(InputWindowHandle[] windowHandles, diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 2b32a30c31b4..76e7f535c60f 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -339,7 +339,6 @@ import android.service.contentcapture.ActivityEvent; import android.service.dreams.DreamActivity; import android.service.voice.IVoiceInteractionSession; import android.util.ArraySet; -import android.util.DisplayMetrics; import android.util.EventLog; import android.util.Log; import android.util.MergedConfiguration; @@ -2123,14 +2122,14 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (mWmService.mFlags.mInsetsDecoupledConfiguration) { // When the stable configuration is the default behavior, override for the legacy apps // without forward override flag. - mResolveConfigHint.mUseOverrideInsetsForStableBounds = + mResolveConfigHint.mUseOverrideInsetsForConfig = !info.isChangeEnabled(INSETS_DECOUPLED_CONFIGURATION_ENFORCED) && !info.isChangeEnabled( OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION); } else { // When the stable configuration is not the default behavior, forward overriding the // listed apps. - mResolveConfigHint.mUseOverrideInsetsForStableBounds = + mResolveConfigHint.mUseOverrideInsetsForConfig = info.isChangeEnabled(OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION); } @@ -5682,6 +5681,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } else if (mTransitionController.inFinishingTransition(this)) { mTransitionChangeFlags |= FLAGS_IS_OCCLUDED_NO_ANIMATION; } + } else { + mTransitionChangeFlags &= ~FLAG_IS_OCCLUDED; } return; } @@ -6529,8 +6530,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // and the token could be null. return; } - if (r.mDisplayContent.mDisplayRotationCompatPolicy != null) { - r.mDisplayContent.mDisplayRotationCompatPolicy.onActivityRefreshed(r); + if (r.mDisplayContent.mActivityRefresher != null) { + r.mDisplayContent.mActivityRefresher.onActivityRefreshed(r); } } @@ -8490,7 +8491,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mCompatDisplayInsets = new CompatDisplayInsets( mDisplayContent, this, letterboxedContainerBounds, - mResolveConfigHint.mUseOverrideInsetsForStableBounds); + mResolveConfigHint.mUseOverrideInsetsForConfig); } private void clearSizeCompatModeAttributes() { @@ -8570,8 +8571,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A final int parentWindowingMode = newParentConfiguration.windowConfiguration.getWindowingMode(); - applySizeOverrideIfNeeded(newParentConfiguration, parentWindowingMode, resolvedConfig); - // Bubble activities should always fill their parent and should not be letterboxed. final boolean isFixedOrientationLetterboxAllowed = !getLaunchedFromBubble() && (parentWindowingMode == WINDOWING_MODE_MULTI_WINDOW @@ -8671,6 +8670,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A resolvedConfig.windowConfiguration.setMaxBounds(mTmpBounds); } + applySizeOverrideIfNeeded(newParentConfiguration, parentWindowingMode, resolvedConfig); + logAppCompatState(); } @@ -8689,14 +8690,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (mDisplayContent == null) { return; } - final Rect parentBounds = newParentConfiguration.windowConfiguration.getBounds(); int rotation = newParentConfiguration.windowConfiguration.getRotation(); if (rotation == ROTATION_UNDEFINED && !isFixedRotationTransforming()) { rotation = mDisplayContent.getRotation(); } - if (!mResolveConfigHint.mUseOverrideInsetsForStableBounds - || getCompatDisplayInsets() != null || isFloating(parentWindowingMode) - || rotation == ROTATION_UNDEFINED) { + if (!mResolveConfigHint.mUseOverrideInsetsForConfig + || getCompatDisplayInsets() != null || shouldCreateCompatDisplayInsets() + || isFloating(parentWindowingMode) || rotation == ROTATION_UNDEFINED) { // If the insets configuration decoupled logic is not enabled for the app, or the app // already has a compat override, or the context doesn't contain enough info to // calculate the override, skip the override. @@ -8713,53 +8713,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } // Override starts here. - final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270); - final int dw = rotated ? mDisplayContent.mBaseDisplayHeight - : mDisplayContent.mBaseDisplayWidth; - final int dh = rotated ? mDisplayContent.mBaseDisplayWidth - : mDisplayContent.mBaseDisplayHeight; - final Rect nonDecorInsets = mDisplayContent.getDisplayPolicy() - .getDecorInsetsInfo(rotation, dw, dh).mOverrideNonDecorInsets; - // This should be the only place override the configuration for ActivityRecord. Override - // the value if not calculated yet. - Rect outAppBounds = inOutConfig.windowConfiguration.getAppBounds(); - if (outAppBounds == null || outAppBounds.isEmpty()) { - inOutConfig.windowConfiguration.setAppBounds(parentBounds); - outAppBounds = inOutConfig.windowConfiguration.getAppBounds(); - outAppBounds.inset(nonDecorInsets); - } - float density = inOutConfig.densityDpi; - if (density == Configuration.DENSITY_DPI_UNDEFINED) { - density = newParentConfiguration.densityDpi; - } - density *= DisplayMetrics.DENSITY_DEFAULT_SCALE; - if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED) { - final int overrideScreenWidthDp = (int) (outAppBounds.width() / density + 0.5f); - inOutConfig.screenWidthDp = overrideScreenWidthDp; - } - if (inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) { - final int overrideScreenHeightDp = (int) (outAppBounds.height() / density + 0.5f); - inOutConfig.screenHeightDp = overrideScreenHeightDp; - } - if (inOutConfig.smallestScreenWidthDp - == Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED - && parentWindowingMode == WINDOWING_MODE_FULLSCREEN) { - // For the case of PIP transition and multi-window environment, the - // smallestScreenWidthDp is handled already. Override only if the app is in - // fullscreen. - final DisplayInfo info = new DisplayInfo(mDisplayContent.getDisplayInfo()); - mDisplayContent.computeSizeRanges(info, rotated, dw, dh, - mDisplayContent.getDisplayMetrics().density, - inOutConfig, true /* overrideConfig */); - } - - // It's possible that screen size will be considered in different orientation with or - // without considering the system bar insets. Override orientation as well. - if (inOutConfig.orientation == ORIENTATION_UNDEFINED) { - inOutConfig.orientation = - (inOutConfig.screenWidthDp <= inOutConfig.screenHeightDp) - ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE; - } + computeConfigByResolveHint(inOutConfig, newParentConfiguration); } private void computeConfigByResolveHint(@NonNull Configuration resolvedConfig, @@ -9015,7 +8969,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (mDisplayContent == null) { return true; } - if (!mResolveConfigHint.mUseOverrideInsetsForStableBounds) { + if (!mResolveConfigHint.mUseOverrideInsetsForConfig) { // No insets should be considered any more. return true; } @@ -9034,7 +8988,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A final Task task = getTask(); task.calculateInsetFrames(outNonDecorBounds /* outNonDecorBounds */, outStableBounds /* outStableBounds */, parentBounds /* bounds */, di, - mResolveConfigHint.mUseOverrideInsetsForStableBounds); + mResolveConfigHint.mUseOverrideInsetsForConfig); final int orientationWithInsets = outStableBounds.height() >= outStableBounds.width() ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE; // If orientation does not match the orientation with insets applied, then a @@ -9091,7 +9045,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A getResolvedOverrideConfiguration().windowConfiguration.getBounds(); final int stableBoundsOrientation = stableBounds.width() > stableBounds.height() ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT; - final int parentOrientation = mResolveConfigHint.mUseOverrideInsetsForStableBounds + final int parentOrientation = mResolveConfigHint.mUseOverrideInsetsForConfig ? stableBoundsOrientation : newParentConfig.orientation; // If the activity requires a different orientation (either by override or activityInfo), @@ -9116,7 +9070,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return; } - final Rect parentAppBounds = mResolveConfigHint.mUseOverrideInsetsForStableBounds + final Rect parentAppBounds = mResolveConfigHint.mUseOverrideInsetsForConfig ? outNonDecorBounds : newParentConfig.windowConfiguration.getAppBounds(); // TODO(b/182268157): Explore using only one type of parentBoundsWithInsets, either app // bounds or stable bounds to unify aspect ratio logic. @@ -10042,7 +9996,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } else { scheduleConfigurationChanged(newMergedOverrideConfig, newActivityWindowInfo); } - notifyDisplayCompatPolicyAboutConfigurationChange( + notifyActivityRefresherAboutConfigurationChange( mLastReportedConfiguration.getMergedConfiguration(), mTmpConfig); return true; } @@ -10109,18 +10063,18 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } else { scheduleConfigurationChanged(newMergedOverrideConfig, newActivityWindowInfo); } - notifyDisplayCompatPolicyAboutConfigurationChange( + notifyActivityRefresherAboutConfigurationChange( mLastReportedConfiguration.getMergedConfiguration(), mTmpConfig); return true; } - private void notifyDisplayCompatPolicyAboutConfigurationChange( + private void notifyActivityRefresherAboutConfigurationChange( Configuration newConfig, Configuration lastReportedConfig) { - if (mDisplayContent.mDisplayRotationCompatPolicy == null + if (mDisplayContent.mActivityRefresher == null || !shouldBeResumed(/* activeActivity */ null)) { return; } - mDisplayContent.mDisplayRotationCompatPolicy.onActivityConfigurationChanging( + mDisplayContent.mActivityRefresher.onActivityConfigurationChanging( this, newConfig, lastReportedConfig); } diff --git a/services/core/java/com/android/server/wm/ActivityRefresher.java b/services/core/java/com/android/server/wm/ActivityRefresher.java new file mode 100644 index 000000000000..23a97089fd60 --- /dev/null +++ b/services/core/java/com/android/server/wm/ActivityRefresher.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.wm; + +import static android.app.servertransaction.ActivityLifecycleItem.ON_PAUSE; +import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP; + +import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES; + +import android.annotation.NonNull; +import android.app.servertransaction.RefreshCallbackItem; +import android.app.servertransaction.ResumeActivityItem; +import android.content.res.Configuration; +import android.os.Handler; +import android.os.RemoteException; + +import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.util.ArrayUtils; + +import java.util.ArrayList; + +/** + * Class that refreshes the activity (through stop/pause -> resume) based on configuration change. + * + * <p>This class queries all of its {@link Evaluator}s and restarts the activity if any of them + * return {@code true} in {@link Evaluator#shouldRefreshActivity}. {@link ActivityRefresher} cycles + * through either stop or pause and then resume, based on the global config and per-app override. + */ +class ActivityRefresher { + // Delay for ensuring that onActivityRefreshed is always called after an activity refresh. The + // client process may not always report the event back to the server, such as process is + // crashed or got killed. + private static final long REFRESH_CALLBACK_TIMEOUT_MS = 2000L; + + @NonNull private final WindowManagerService mWmService; + @NonNull private final Handler mHandler; + @NonNull private final ArrayList<Evaluator> mEvaluators = new ArrayList<>(); + + ActivityRefresher(@NonNull WindowManagerService wmService, @NonNull Handler handler) { + mWmService = wmService; + mHandler = handler; + } + + void addEvaluator(@NonNull Evaluator evaluator) { + mEvaluators.add(evaluator); + } + + void removeEvaluator(@NonNull Evaluator evaluator) { + mEvaluators.remove(evaluator); + } + + /** + * "Refreshes" activity by going through "stopped -> resumed" or "paused -> resumed" cycle. + * This allows to clear cached values in apps (e.g. display or camera rotation) that influence + * camera preview and can lead to sideways or stretching issues persisting even after force + * rotation. + */ + void onActivityConfigurationChanging(@NonNull ActivityRecord activity, + @NonNull Configuration newConfig, @NonNull Configuration lastReportedConfig) { + if (!shouldRefreshActivity(activity, newConfig, lastReportedConfig)) { + return; + } + + final boolean cycleThroughStop = + mWmService.mLetterboxConfiguration + .isCameraCompatRefreshCycleThroughStopEnabled() + && !activity.mLetterboxUiController + .shouldRefreshActivityViaPauseForCameraCompat(); + + activity.mLetterboxUiController.setIsRefreshRequested(true); + ProtoLog.v(WM_DEBUG_STATES, + "Refreshing activity for freeform camera compatibility treatment, " + + "activityRecord=%s", activity); + final RefreshCallbackItem refreshCallbackItem = RefreshCallbackItem.obtain( + activity.token, cycleThroughStop ? ON_STOP : ON_PAUSE); + final ResumeActivityItem resumeActivityItem = ResumeActivityItem.obtain( + activity.token, /* isForward */ false, /* shouldSendCompatFakeFocus */ false); + try { + activity.mAtmService.getLifecycleManager().scheduleTransactionAndLifecycleItems( + activity.app.getThread(), refreshCallbackItem, resumeActivityItem); + mHandler.postDelayed(() -> { + synchronized (mWmService.mGlobalLock) { + onActivityRefreshed(activity); + } + }, REFRESH_CALLBACK_TIMEOUT_MS); + } catch (RemoteException e) { + activity.mLetterboxUiController.setIsRefreshRequested(false); + } + } + + boolean isActivityRefreshing(@NonNull ActivityRecord activity) { + return activity.mLetterboxUiController.isRefreshRequested(); + } + + void onActivityRefreshed(@NonNull ActivityRecord activity) { + // TODO(b/333060789): can we tell that refresh did not happen by observing the activity + // state? + activity.mLetterboxUiController.setIsRefreshRequested(false); + } + + private boolean shouldRefreshActivity(@NonNull ActivityRecord activity, + @NonNull Configuration newConfig, @NonNull Configuration lastReportedConfig) { + return mWmService.mLetterboxConfiguration.isCameraCompatRefreshEnabled() + && activity.mLetterboxUiController.shouldRefreshActivityForCameraCompat() + && ArrayUtils.find(mEvaluators.toArray(), evaluator -> + ((Evaluator) evaluator) + .shouldRefreshActivity(activity, newConfig, lastReportedConfig)) != null; + } + + /** + * Interface for classes that would like to refresh the recently updated activity, based on the + * configuration change. + */ + interface Evaluator { + boolean shouldRefreshActivity(@NonNull ActivityRecord activity, + @NonNull Configuration newConfig, @NonNull Configuration lastReportedConfig); + } +} diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 5079ec18bb0b..e49cb386ac49 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -478,6 +478,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp final DisplayRotationCompatPolicy mDisplayRotationCompatPolicy; @Nullable final CameraStateMonitor mCameraStateMonitor; + @Nullable + final ActivityRefresher mActivityRefresher; DisplayFrames mDisplayFrames; final DisplayUpdater mDisplayUpdater; @@ -1233,13 +1235,15 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp mWmService.mLetterboxConfiguration.isCameraCompatTreatmentEnabledAtBuildTime(); if (shouldCreateDisplayRotationCompatPolicy) { mCameraStateMonitor = new CameraStateMonitor(this, mWmService.mH); + mActivityRefresher = new ActivityRefresher(mWmService, mWmService.mH); mDisplayRotationCompatPolicy = new DisplayRotationCompatPolicy( - this, mWmService.mH, mCameraStateMonitor); + this, mCameraStateMonitor, mActivityRefresher); mCameraStateMonitor.startListeningToCameraState(); } else { // These are to satisfy the `final` check. mCameraStateMonitor = null; + mActivityRefresher = null; mDisplayRotationCompatPolicy = null; } diff --git a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java index eacf9a3fa759..e0cc064fcacc 100644 --- a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java @@ -18,8 +18,6 @@ package com.android.server.wm; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; -import static android.app.servertransaction.ActivityLifecycleItem.ON_PAUSE; -import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR; @@ -32,19 +30,14 @@ import static android.content.res.Configuration.ORIENTATION_UNDEFINED; import static android.view.Display.TYPE_INTERNAL; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION; -import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES; import static com.android.server.wm.DisplayRotationReversionController.REVERSION_TYPE_CAMERA_COMPAT; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StringRes; -import android.app.servertransaction.RefreshCallbackItem; -import android.app.servertransaction.ResumeActivityItem; import android.content.pm.ActivityInfo.ScreenOrientation; import android.content.pm.PackageManager; import android.content.res.Configuration; -import android.os.Handler; -import android.os.RemoteException; import android.widget.Toast; import com.android.internal.R; @@ -64,48 +57,38 @@ import com.android.server.UiThread; * R.bool.config_isWindowManagerCameraCompatTreatmentEnabled} is {@code true}. */ // TODO(b/261444714): Consider moving Camera-specific logic outside of the WM Core path -class DisplayRotationCompatPolicy implements CameraStateMonitor.CameraCompatStateListener { - - // Delay for updating display rotation after Camera connection is closed. Needed to avoid - // rotation flickering when an app is flipping between front and rear cameras or when size - // compat mode is restarted. - // TODO(b/263114289): Consider associating this delay with a specific activity so that if - // the new non-camera activity started on top of the camer one we can rotate faster. - private static final int CAMERA_CLOSED_ROTATION_UPDATE_DELAY_MS = 2000; - // Delay for updating display rotation after Camera connection is opened. This delay is - // selected to be long enough to avoid conflicts with transitions on the app's side. - // Using a delay < CAMERA_CLOSED_ROTATION_UPDATE_DELAY_MS to avoid flickering when an app - // is flipping between front and rear cameras (in case requested orientation changes at - // runtime at the same time) or when size compat mode is restarted. - private static final int CAMERA_OPENED_ROTATION_UPDATE_DELAY_MS = - CAMERA_CLOSED_ROTATION_UPDATE_DELAY_MS / 2; - // Delay for ensuring that onActivityRefreshed is always called after an activity refresh. The - // client process may not always report the event back to the server, such as process is - // crashed or got killed. - private static final int REFRESH_CALLBACK_TIMEOUT_MS = 2000; +final class DisplayRotationCompatPolicy implements CameraStateMonitor.CameraCompatStateListener, + ActivityRefresher.Evaluator { + @NonNull private final DisplayContent mDisplayContent; + @NonNull private final WindowManagerService mWmService; + @NonNull private final CameraStateMonitor mCameraStateMonitor; - private final Handler mHandler; + @NonNull + private final ActivityRefresher mActivityRefresher; @ScreenOrientation private int mLastReportedOrientation = SCREEN_ORIENTATION_UNSET; - DisplayRotationCompatPolicy(@NonNull DisplayContent displayContent, Handler handler, - @NonNull CameraStateMonitor cameraStateMonitor) { + DisplayRotationCompatPolicy(@NonNull DisplayContent displayContent, + @NonNull CameraStateMonitor cameraStateMonitor, + @NonNull ActivityRefresher activityRefresher) { // This constructor is called from DisplayContent constructor. Don't use any fields in // DisplayContent here since they aren't guaranteed to be set. - mHandler = handler; mDisplayContent = displayContent; mWmService = displayContent.mWmService; mCameraStateMonitor = cameraStateMonitor; mCameraStateMonitor.addCameraStateListener(this); + mActivityRefresher = activityRefresher; + mActivityRefresher.addEvaluator(this); } /** Releases camera state listener. */ void dispose() { mCameraStateMonitor.removeCameraStateListener(this); + mActivityRefresher.removeEvaluator(this); } /** @@ -169,47 +152,6 @@ class DisplayRotationCompatPolicy implements CameraStateMonitor.CameraCompatStat } /** - * "Refreshes" activity by going through "stopped -> resumed" or "paused -> resumed" cycle. - * This allows to clear cached values in apps (e.g. display or camera rotation) that influence - * camera preview and can lead to sideways or stretching issues persisting even after force - * rotation. - */ - void onActivityConfigurationChanging(ActivityRecord activity, Configuration newConfig, - Configuration lastReportedConfig) { - if (!isTreatmentEnabledForDisplay() - || !mWmService.mLetterboxConfiguration.isCameraCompatRefreshEnabled() - || !shouldRefreshActivity(activity, newConfig, lastReportedConfig)) { - return; - } - boolean cycleThroughStop = - mWmService.mLetterboxConfiguration - .isCameraCompatRefreshCycleThroughStopEnabled() - && !activity.mLetterboxUiController - .shouldRefreshActivityViaPauseForCameraCompat(); - try { - activity.mLetterboxUiController.setIsRefreshAfterRotationRequested(true); - ProtoLog.v(WM_DEBUG_STATES, - "Refreshing activity for camera compatibility treatment, " - + "activityRecord=%s", activity); - final RefreshCallbackItem refreshCallbackItem = RefreshCallbackItem.obtain( - activity.token, cycleThroughStop ? ON_STOP : ON_PAUSE); - final ResumeActivityItem resumeActivityItem = ResumeActivityItem.obtain( - activity.token, /* isForward */ false, /* shouldSendCompatFakeFocus */ false); - activity.mAtmService.getLifecycleManager().scheduleTransactionAndLifecycleItems( - activity.app.getThread(), refreshCallbackItem, resumeActivityItem); - mHandler.postDelayed( - () -> onActivityRefreshed(activity), - REFRESH_CALLBACK_TIMEOUT_MS); - } catch (RemoteException e) { - activity.mLetterboxUiController.setIsRefreshAfterRotationRequested(false); - } - } - - void onActivityRefreshed(@NonNull ActivityRecord activity) { - activity.mLetterboxUiController.setIsRefreshAfterRotationRequested(false); - } - - /** * Notifies that animation in {@link ScreenRotationAnimation} has finished. * * <p>This class uses this signal as a trigger for notifying the user about forced rotation @@ -276,14 +218,16 @@ class DisplayRotationCompatPolicy implements CameraStateMonitor.CameraCompatStat // Refreshing only when configuration changes after rotation or camera split screen aspect ratio // treatment is enabled - private boolean shouldRefreshActivity(ActivityRecord activity, Configuration newConfig, - Configuration lastReportedConfig) { + @Override + public boolean shouldRefreshActivity(@NonNull ActivityRecord activity, + @NonNull Configuration newConfig, @NonNull Configuration lastReportedConfig) { final boolean displayRotationChanged = (newConfig.windowConfiguration.getDisplayRotation() != lastReportedConfig.windowConfiguration.getDisplayRotation()); - return (displayRotationChanged - || activity.mLetterboxUiController.isCameraCompatSplitScreenAspectRatioAllowed()) + return isTreatmentEnabledForDisplay() && isTreatmentEnabledForActivity(activity) - && activity.mLetterboxUiController.shouldRefreshActivityForCameraCompat(); + && activity.mLetterboxUiController.shouldRefreshActivityForCameraCompat() + && (displayRotationChanged + || activity.mLetterboxUiController.isCameraCompatSplitScreenAspectRatioAllowed()); } /** @@ -310,7 +254,6 @@ class DisplayRotationCompatPolicy implements CameraStateMonitor.CameraCompatStat && activity.mLetterboxUiController.shouldForceRotateForCameraCompat(); } - /** * Whether camera compat treatment is applicable for the given activity. * @@ -429,6 +372,6 @@ class DisplayRotationCompatPolicy implements CameraStateMonitor.CameraCompatStat || !mCameraStateMonitor.isCameraWithIdRunningForActivity(topActivity, cameraId)) { return false; } - return topActivity.mLetterboxUiController.isRefreshAfterRotationRequested(); + return mActivityRefresher.isActivityRefreshing(topActivity); } } diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java index 4400ed23a1b7..2288fe998b58 100644 --- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java @@ -198,7 +198,7 @@ class InsetsSourceProvider { if (mControllable) { mWindowContainer.setControllableInsetProvider(this); if (mPendingControlTarget != mControlTarget) { - updateControlForTarget(mPendingControlTarget, true /* force */); + mStateController.notifyControlTargetChanged(mPendingControlTarget, this); } } } diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java index 57827c567b5b..16d7b4fb6eed 100644 --- a/services/core/java/com/android/server/wm/LetterboxUiController.java +++ b/services/core/java/com/android/server/wm/LetterboxUiController.java @@ -260,7 +260,7 @@ final class LetterboxUiController { // Whether activity "refresh" was requested but not finished in // ActivityRecord#activityResumedLocked following the camera compat force rotation in // DisplayRotationCompatPolicy. - private boolean mIsRefreshAfterRotationRequested; + private boolean mIsRefreshRequested; @NonNull private final OptProp mIgnoreRequestedOrientationOptProp; @@ -571,15 +571,14 @@ final class LetterboxUiController { } /** - * Whether activity "refresh" was requested but not finished in {@link #activityResumedLocked} - * following the camera compat force rotation in {@link DisplayRotationCompatPolicy}. + * Whether activity "refresh" was requested but not finished in {@link #activityResumedLocked}. */ - boolean isRefreshAfterRotationRequested() { - return mIsRefreshAfterRotationRequested; + boolean isRefreshRequested() { + return mIsRefreshRequested; } - void setIsRefreshAfterRotationRequested(boolean isRequested) { - mIsRefreshAfterRotationRequested = isRequested; + void setIsRefreshRequested(boolean isRequested) { + mIsRefreshRequested = isRequested; } boolean isOverrideRespectRequestedOrientationEnabled() { @@ -1068,7 +1067,7 @@ final class LetterboxUiController { * thin letteboxing */ boolean allowVerticalReachabilityForThinLetterbox() { - if (!Flags.disableThinLetterboxingReachability()) { + if (!Flags.disableThinLetterboxingPolicy()) { return true; } // When the flag is enabled we allow vertical reachability only if the @@ -1081,7 +1080,7 @@ final class LetterboxUiController { * thin letteboxing */ boolean allowHorizontalReachabilityForThinLetterbox() { - if (!Flags.disableThinLetterboxingReachability()) { + if (!Flags.disableThinLetterboxingPolicy()) { return true; } // When the flag is enabled we allow horizontal reachability only if the diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java index a444c96eea97..26e4eaaedfe2 100644 --- a/services/core/java/com/android/server/wm/TaskFragment.java +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -40,6 +40,8 @@ import static android.os.Process.INVALID_UID; import static android.os.Process.SYSTEM_UID; import static android.os.UserHandle.USER_NULL; import static android.view.Display.INVALID_DISPLAY; +import static android.view.Surface.ROTATION_270; +import static android.view.Surface.ROTATION_90; import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND; @@ -2222,7 +2224,7 @@ class TaskFragment extends WindowContainer<WindowContainer> { static class ConfigOverrideHint { @Nullable DisplayInfo mTmpOverrideDisplayInfo; @Nullable ActivityRecord.CompatDisplayInsets mTmpCompatInsets; - boolean mUseOverrideInsetsForStableBounds; + boolean mUseOverrideInsetsForConfig; } void computeConfigResourceOverrides(@NonNull Configuration inOutConfig, @@ -2255,11 +2257,11 @@ class TaskFragment extends WindowContainer<WindowContainer> { @NonNull Configuration parentConfig, @Nullable ConfigOverrideHint overrideHint) { DisplayInfo overrideDisplayInfo = null; ActivityRecord.CompatDisplayInsets compatInsets = null; - boolean useOverrideInsetsForStableBounds = false; + boolean useOverrideInsetsForConfig = false; if (overrideHint != null) { overrideDisplayInfo = overrideHint.mTmpOverrideDisplayInfo; compatInsets = overrideHint.mTmpCompatInsets; - useOverrideInsetsForStableBounds = overrideHint.mUseOverrideInsetsForStableBounds; + useOverrideInsetsForConfig = overrideHint.mUseOverrideInsetsForConfig; if (overrideDisplayInfo != null) { // Make sure the screen related configs can be computed by the provided // display info. @@ -2323,6 +2325,7 @@ class TaskFragment extends WindowContainer<WindowContainer> { } } + boolean insetsOverrideApplied = false; if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED || inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) { if (!customContainerPolicy && WindowConfiguration.isFloating(windowingMode)) { @@ -2339,7 +2342,7 @@ class TaskFragment extends WindowContainer<WindowContainer> { // The non decor inset are areas that could never be removed in Honeycomb. See // {@link WindowManagerPolicy#getNonDecorInsetsLw}. calculateInsetFrames(mTmpNonDecorBounds, mTmpStableBounds, mTmpFullBounds, di, - useOverrideInsetsForStableBounds); + useOverrideInsetsForConfig); } else { // Apply the given non-decor and stable insets to calculate the corresponding bounds // for screen size of configuration. @@ -2356,8 +2359,21 @@ class TaskFragment extends WindowContainer<WindowContainer> { intersectWithInsetsIfFits(mTmpStableBounds, mTmpBounds, compatInsets.mStableInsets[rotation]); outAppBounds.set(mTmpNonDecorBounds); + } else if (useOverrideInsetsForConfig) { + final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270); + final int dw = rotated ? mDisplayContent.mBaseDisplayHeight + : mDisplayContent.mBaseDisplayWidth; + final int dh = rotated ? mDisplayContent.mBaseDisplayWidth + : mDisplayContent.mBaseDisplayHeight; + final DisplayPolicy.DecorInsets.Info decorInsets = mDisplayContent + .getDisplayPolicy().getDecorInsetsInfo(rotation, dw, dh); + mTmpStableBounds.set(outAppBounds); + mTmpStableBounds.inset(decorInsets.mOverrideConfigInsets); + outAppBounds.inset(decorInsets.mOverrideNonDecorInsets); + mTmpNonDecorBounds.set(outAppBounds); + // Record the override apply to avoid duplicated check. + insetsOverrideApplied = true; } else { - // Set to app bounds because it excludes decor insets. mTmpNonDecorBounds.set(outAppBounds); mTmpStableBounds.set(outAppBounds); } @@ -2399,6 +2415,11 @@ class TaskFragment extends WindowContainer<WindowContainer> { // from the parent task would result in applications loaded wrong resource. inOutConfig.smallestScreenWidthDp = Math.min(inOutConfig.screenWidthDp, inOutConfig.screenHeightDp); + } else if (insetsOverrideApplied) { + // The smallest width should also consider insets. If the insets are overridden, + // use the overridden value. + inOutConfig.smallestScreenWidthDp = + Math.min(inOutConfig.screenWidthDp, inOutConfig.screenHeightDp); } // otherwise, it will just inherit } diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index 74ca9ad687ea..97f1e19e35b1 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -122,7 +122,6 @@ static struct { jmethodID interceptMotionBeforeQueueingNonInteractive; jmethodID interceptKeyBeforeDispatching; jmethodID dispatchUnhandledKey; - jmethodID onPointerDisplayIdChanged; jmethodID onPointerDownOutsideFocus; jmethodID getVirtualKeyQuietTimeMillis; jmethodID getExcludedDeviceNames; @@ -786,12 +785,6 @@ void NativeInputManager::notifyPointerDisplayIdChanged(ui::LogicalDisplayId poin } // release lock mInputManager->getReader().requestRefreshConfiguration( InputReaderConfiguration::Change::DISPLAY_INFO); - - // Notify the system. - JNIEnv* env = jniEnv(); - env->CallVoidMethod(mServiceObj, gServiceClassInfo.onPointerDisplayIdChanged, pointerDisplayId, - position.x, position.y); - checkAndClearExceptionFromCallback(env, "onPointerDisplayIdChanged"); } void NativeInputManager::notifyStickyModifierStateChanged(uint32_t modifierState, @@ -2933,9 +2926,6 @@ int register_android_server_InputManager(JNIEnv* env) { "dispatchUnhandledKey", "(Landroid/os/IBinder;Landroid/view/KeyEvent;I)Landroid/view/KeyEvent;"); - GET_METHOD_ID(gServiceClassInfo.onPointerDisplayIdChanged, clazz, "onPointerDisplayIdChanged", - "(IFF)V"); - GET_METHOD_ID(gServiceClassInfo.notifyStickyModifierStateChanged, clazz, "notifyStickyModifierStateChanged", "(II)V"); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java index d114337f9c6c..d733762e90e5 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java @@ -217,7 +217,7 @@ final class DevicePolicyEngine { <V> void setLocalPolicy( @NonNull PolicyDefinition<V> policyDefinition, @NonNull EnforcingAdmin enforcingAdmin, - @Nullable PolicyValue<V> value, + @NonNull PolicyValue<V> value, int userId, boolean skipEnforcePolicy) { Objects.requireNonNull(policyDefinition); @@ -313,6 +313,7 @@ final class DevicePolicyEngine { } updateDeviceAdminServiceOnPolicyAddLocked(enforcingAdmin); write(); + applyToInheritableProfiles(policyDefinition, enforcingAdmin, value, userId); } // TODO: add more documentation on broadcasts/callbacks to use to get current enforced values @@ -400,7 +401,7 @@ final class DevicePolicyEngine { * else remove the policy from child. */ private <V> void applyToInheritableProfiles(PolicyDefinition<V> policyDefinition, - EnforcingAdmin enforcingAdmin, PolicyValue<V> value, int userId) { + EnforcingAdmin enforcingAdmin, @Nullable PolicyValue<V> value, int userId) { if (policyDefinition.isInheritable()) { Binder.withCleanCallingIdentity(() -> { List<UserInfo> userInfos = mUserManager.getProfiles(userId); @@ -1742,14 +1743,17 @@ final class DevicePolicyEngine { } } - <V> void reapplyAllPoliciesLocked() { + <V> void reapplyAllPoliciesOnBootLocked() { for (PolicyKey policy : mGlobalPolicies.keySet()) { PolicyState<?> policyState = mGlobalPolicies.get(policy); // Policy definition and value will always be of the same type PolicyDefinition<V> policyDefinition = (PolicyDefinition<V>) policyState.getPolicyDefinition(); - PolicyValue<V> policyValue = (PolicyValue<V>) policyState.getCurrentResolvedPolicy(); - enforcePolicy(policyDefinition, policyValue, UserHandle.USER_ALL); + if (!policyDefinition.shouldSkipEnforcementIfNotChanged()) { + PolicyValue<V> policyValue = + (PolicyValue<V>) policyState.getCurrentResolvedPolicy(); + enforcePolicy(policyDefinition, policyValue, UserHandle.USER_ALL); + } } for (int i = 0; i < mLocalPolicies.size(); i++) { int userId = mLocalPolicies.keyAt(i); @@ -1758,10 +1762,11 @@ final class DevicePolicyEngine { // Policy definition and value will always be of the same type PolicyDefinition<V> policyDefinition = (PolicyDefinition<V>) policyState.getPolicyDefinition(); - PolicyValue<V> policyValue = - (PolicyValue<V>) policyState.getCurrentResolvedPolicy(); - enforcePolicy(policyDefinition, policyValue, userId); - + if (!policyDefinition.shouldSkipEnforcementIfNotChanged()) { + PolicyValue<V> policyValue = + (PolicyValue<V>) policyState.getCurrentResolvedPolicy(); + enforcePolicy(policyDefinition, policyValue, userId); + } } } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 2b93d2132a8f..85d2a0df4fdc 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -3351,7 +3351,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { break; case SystemService.PHASE_SYSTEM_SERVICES_READY: synchronized (getLockObject()) { - mDevicePolicyEngine.reapplyAllPoliciesLocked(); + mDevicePolicyEngine.reapplyAllPoliciesOnBootLocked(); } break; case SystemService.PHASE_ACTIVITY_MANAGER_READY: @@ -11443,7 +11443,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } setBackwardsCompatibleAppRestrictions( caller, packageName, restrictions, caller.getUserHandle()); - } else if (Flags.dmrhCanSetAppRestriction()) { + } else if (Flags.dmrhSetAppRestrictions()) { final boolean isRoleHolder; if (who != null) { // DO or PO @@ -11484,10 +11484,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { new BundlePolicyValue(restrictions), affectedUserId); } - Intent changeIntent = new Intent(Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED); - changeIntent.setPackage(packageName); - changeIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); - mContext.sendBroadcastAsUser(changeIntent, UserHandle.of(affectedUserId)); } else { mInjector.binderWithCleanCallingIdentity(() -> { mUserManager.setApplicationRestrictions(packageName, restrictions, @@ -12845,7 +12841,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return Bundle.EMPTY; } return policies.get(enforcingAdmin).getValue(); - } else if (Flags.dmrhCanSetAppRestriction()) { + } else if (Flags.dmrhSetAppRestrictions()) { final boolean isRoleHolder; if (who != null) { // Caller is DO or PO. They cannot call this on parent @@ -15770,8 +15766,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { PolicyDefinition.APPLICATION_RESTRICTIONS(packageName), userId); List<Bundle> restrictions = new ArrayList<>(); - for (EnforcingAdmin admin : policies.keySet()) { - restrictions.add(policies.get(admin).getValue()); + for (PolicyValue<Bundle> policyValue: policies.values()) { + Bundle value = policyValue.getValue(); + // Probably not necessary since setApplicationRestrictions only sets non-empty + // Bundle, but just in case. + if (value != null && !value.isEmpty()) { + restrictions.add(value); + } } return mInjector.binderWithCleanCallingIdentity(() -> { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/EnterpriseSpecificIdCalculator.java b/services/devicepolicy/java/com/android/server/devicepolicy/EnterpriseSpecificIdCalculator.java index 1000bfa5f6c9..cbd28475bc26 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/EnterpriseSpecificIdCalculator.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/EnterpriseSpecificIdCalculator.java @@ -52,7 +52,18 @@ class EnterpriseSpecificIdCalculator { EnterpriseSpecificIdCalculator(Context context) { TelephonyManager telephonyService = context.getSystemService(TelephonyManager.class); Preconditions.checkState(telephonyService != null, "Unable to access telephony service"); - mImei = telephonyService.getImei(0); + + String imei; + try { + imei = telephonyService.getImei(0); + } catch (UnsupportedOperationException doesNotSupportGms) { + // Instead of catching the exception, we could check for FEATURE_TELEPHONY_GSM. + // However that runs the risk of changing a device's existing ESID if on these devices + // telephonyService.getImei() actually returns non-null even when the device does not + // declare FEATURE_TELEPHONY_GSM. + imei = null; + } + mImei = imei; String meid; try { meid = telephonyService.getMeid(0); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java index 8d980b5031e2..8bec3847d8ca 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java @@ -51,6 +51,7 @@ import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; final class PolicyDefinition<V> { @@ -82,6 +83,10 @@ final class PolicyDefinition<V> { // them. private static final int POLICY_FLAG_USER_RESTRICTION_POLICY = 1 << 4; + // Only invoke the policy enforcer callback when the policy value changes, and do not invoke the + // callback in other cases such as device reboots. + private static final int POLICY_FLAG_SKIP_ENFORCEMENT_IF_UNCHANGED = 1 << 5; + private static final MostRestrictive<Boolean> FALSE_MORE_RESTRICTIVE = new MostRestrictive<>( List.of(new BooleanPolicyValue(false), new BooleanPolicyValue(true))); @@ -231,11 +236,11 @@ final class PolicyDefinition<V> { // Don't need to take in a resolution mechanism since its never used, but might // need some refactoring to not always assume a non-null mechanism. new MostRecent<>(), - POLICY_FLAG_LOCAL_ONLY_POLICY | POLICY_FLAG_NON_COEXISTABLE_POLICY, - // Application restrictions are now stored and retrieved from DPMS, so no - // enforcing is required, however DPMS calls into UM to set restrictions for - // backwards compatibility. - (Bundle value, Context context, Integer userId, PolicyKey policyKey) -> true, + // Only invoke the enforcement callback during policy change and not other state + POLICY_FLAG_LOCAL_ONLY_POLICY | POLICY_FLAG_INHERITABLE + | POLICY_FLAG_NON_COEXISTABLE_POLICY + | POLICY_FLAG_SKIP_ENFORCEMENT_IF_UNCHANGED, + PolicyEnforcerCallbacks::setApplicationRestrictions, new BundlePolicySerializer()); /** @@ -581,6 +586,10 @@ final class PolicyDefinition<V> { return (mPolicyFlags & POLICY_FLAG_USER_RESTRICTION_POLICY) != 0; } + boolean shouldSkipEnforcementIfNotChanged() { + return (mPolicyFlags & POLICY_FLAG_SKIP_ENFORCEMENT_IF_UNCHANGED) != 0; + } + @Nullable PolicyValue<V> resolvePolicy(LinkedHashMap<EnforcingAdmin, PolicyValue<V>> adminsPolicy) { return mResolutionMechanism.resolve(adminsPolicy); @@ -610,7 +619,7 @@ final class PolicyDefinition<V> { * {@link Object#equals} implementation. */ private PolicyDefinition( - PolicyKey key, + @NonNull PolicyKey key, ResolutionMechanism<V> resolutionMechanism, QuadFunction<V, Context, Integer, PolicyKey, Boolean> policyEnforcerCallback, PolicySerializer<V> policySerializer) { @@ -622,11 +631,12 @@ final class PolicyDefinition<V> { * {@link Object#equals} and {@link Object#hashCode()} implementation. */ private PolicyDefinition( - PolicyKey policyKey, + @NonNull PolicyKey policyKey, ResolutionMechanism<V> resolutionMechanism, int policyFlags, QuadFunction<V, Context, Integer, PolicyKey, Boolean> policyEnforcerCallback, PolicySerializer<V> policySerializer) { + Objects.requireNonNull(policyKey); mPolicyKey = policyKey; mResolutionMechanism = resolutionMechanism; mPolicyFlags = policyFlags; diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java index 09eef451c547..04d277e6e667 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java @@ -37,11 +37,13 @@ import android.app.admin.flags.Flags; import android.app.usage.UsageStatsManagerInternal; import android.content.ComponentName; import android.content.Context; +import android.content.Intent; import android.content.IntentFilter; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.os.Binder; +import android.os.Bundle; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; @@ -172,6 +174,29 @@ final class PolicyEnforcerCallbacks { return true; } + + /** + * Application restrictions are stored and retrieved from DPMS, so no enforcing (aka pushing + * it to UMS) is required. Only need to send broadcast to the target user here as we rely on + * the inheritable policy propagation logic in PolicyEngine to apply this policy to multiple + * profiles. The broadcast should only be sent when an application restriction is set, so we + * rely on the POLICY_FLAG_SKIP_ENFORCEMENT_IF_UNCHANGED flag so DPE only invokes this callback + * when the policy is set, and not during system boot or other situations. + */ + static boolean setApplicationRestrictions(Bundle bundle, Context context, Integer userId, + PolicyKey policyKey) { + Binder.withCleanCallingIdentity(() -> { + PackagePolicyKey key = (PackagePolicyKey) policyKey; + String packageName = key.getPackageName(); + Objects.requireNonNull(packageName); + Intent changeIntent = new Intent(Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED); + changeIntent.setPackage(packageName); + changeIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); + context.sendBroadcastAsUser(changeIntent, UserHandle.of(userId)); + }); + return true; + } + private static class BlockingCallback { private final CountDownLatch mLatch = new CountDownLatch(1); private final AtomicReference<Boolean> mValue = new AtomicReference<>(); diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodInfoUtilsTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodInfoUtilsTest.java new file mode 100644 index 000000000000..50804da5a293 --- /dev/null +++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodInfoUtilsTest.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.inputmethod; + +import static com.android.server.inputmethod.TestUtils.TEST_IME_ID1; +import static com.android.server.inputmethod.TestUtils.TEST_IME_ID2; +import static com.android.server.inputmethod.TestUtils.createFakeInputMethodInfo; +import static com.android.server.inputmethod.TestUtils.createFakeSubtypes; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +import android.os.Parcel; +import android.os.Parcelable; +import android.view.inputmethod.InputMethodInfo; + +import androidx.annotation.NonNull; + +import org.junit.Test; + +import java.util.Arrays; +import java.util.Objects; + +public final class InputMethodInfoUtilsTest { + + @Test + public void testMarshalSameObject() { + final var imi = createFakeInputMethodInfo(TEST_IME_ID1, createFakeSubtypes(3)); + final byte[] buf = InputMethodInfoUtils.marshal(imi); + + assertArrayEquals("The same value must be returned when called multiple times", + buf, InputMethodInfoUtils.marshal(imi)); + assertArrayEquals("The same value must be returned when called multiple times", + buf, InputMethodInfoUtils.marshal(imi)); + } + + @Test + public void testMarshalDifferentObjects() { + final var imi1 = createFakeInputMethodInfo(TEST_IME_ID1, createFakeSubtypes(3)); + final var imi2 = createFakeInputMethodInfo(TEST_IME_ID2, createFakeSubtypes(0)); + + assertFalse("Different inputs must yield different byte patterns", Arrays.equals( + InputMethodInfoUtils.marshal(imi1), InputMethodInfoUtils.marshal(imi2))); + } + + @NonNull + private static <T> T readTypedObject(byte[] data, @NonNull Parcelable.Creator<T> creator) { + Parcel parcel = null; + try { + parcel = Parcel.obtain(); + parcel.unmarshall(data, 0, data.length); + parcel.setDataPosition(0); + return Objects.requireNonNull(parcel.readTypedObject(creator)); + } finally { + if (parcel != null) { + parcel.recycle(); + } + } + } + + @Test + public void testUnmarshalSameObject() { + final var imi = createFakeInputMethodInfo(TEST_IME_ID1, createFakeSubtypes(3)); + final var cloned = readTypedObject(InputMethodInfoUtils.marshal(imi), + InputMethodInfo.CREATOR); + assertEquals(imi.getPackageName(), cloned.getPackageName()); + assertEquals(imi.getSubtypeCount(), cloned.getSubtypeCount()); + } +} diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java index 28a99f2f8e87..3b25cb13e66c 100644 --- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java +++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java @@ -127,6 +127,7 @@ public class InputMethodManagerServiceTestBase { protected IInputMethodInvoker mMockInputMethodInvoker; protected InputMethodManagerService mInputMethodManagerService; protected ServiceThread mServiceThread; + protected ServiceThread mPackageMonitorThread; protected boolean mIsLargeScreen; private InputManagerGlobal.TestSession mInputManagerGlobalSession; @@ -222,10 +223,17 @@ public class InputMethodManagerServiceTestBase { mServiceThread = new ServiceThread( - "TestServiceThread", - Process.THREAD_PRIORITY_FOREGROUND, /* allowIo */ - false); - mInputMethodManagerService = new InputMethodManagerService(mContext, mServiceThread, + "immstest1", + Process.THREAD_PRIORITY_FOREGROUND, + true /* allowIo */); + mPackageMonitorThread = + new ServiceThread( + "immstest2", + Process.THREAD_PRIORITY_FOREGROUND, + true /* allowIo */); + mInputMethodManagerService = new InputMethodManagerService(mContext, + InputMethodManagerService.shouldEnableExperimentalConcurrentMultiUserMode(mContext), + mServiceThread, mPackageMonitorThread, unusedUserId -> mMockInputMethodBindingController); spyOn(mInputMethodManagerService); @@ -259,6 +267,10 @@ public class InputMethodManagerServiceTestBase { mInputMethodManagerService.mInputMethodDeviceConfigs.destroy(); } + if (mPackageMonitorThread != null) { + mPackageMonitorThread.quitSafely(); + } + if (mServiceThread != null) { mServiceThread.quitSafely(); } diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodMapTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodMapTest.java new file mode 100644 index 000000000000..be7042177a35 --- /dev/null +++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodMapTest.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.inputmethod; + +import static com.android.server.inputmethod.TestUtils.TEST_IME_ID1; +import static com.android.server.inputmethod.TestUtils.TEST_IME_ID2; +import static com.android.server.inputmethod.TestUtils.TEST_IME_ID3; +import static com.android.server.inputmethod.TestUtils.createFakeInputMethodInfo; +import static com.android.server.inputmethod.TestUtils.createFakeSubtypes; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.util.ArrayMap; +import android.view.inputmethod.InputMethodInfo; + +import androidx.annotation.NonNull; + +import org.junit.Test; + +public final class InputMethodMapTest { + + @NonNull + private static InputMethodMap toMap(InputMethodInfo... list) { + final ArrayMap<String, InputMethodInfo> map = new ArrayMap<>(); + for (var imi : list) { + map.put(imi.getId(), imi); + } + return InputMethodMap.of(map); + } + + @Test + public void testAreSameSameObject() { + final var imi1 = createFakeInputMethodInfo(TEST_IME_ID1, createFakeSubtypes(0)); + final var imi2 = createFakeInputMethodInfo(TEST_IME_ID2, createFakeSubtypes(3)); + final var map = toMap(imi1, imi2); + assertTrue("Must return true for the same instance", + InputMethodMap.areSame(map, map)); + } + + @Test + public void testAreSameEquivalentObject() { + final var imi1 = createFakeInputMethodInfo(TEST_IME_ID1, createFakeSubtypes(0)); + final var imi2 = createFakeInputMethodInfo(TEST_IME_ID2, createFakeSubtypes(3)); + assertTrue("Must return true for the equivalent instances", + InputMethodMap.areSame(toMap(imi1, imi2), toMap(imi1, imi2))); + + assertTrue("Must return true for the equivalent instances", + InputMethodMap.areSame(toMap(imi1, imi2), toMap(imi2, imi1))); + } + + @Test + public void testAreSameDifferentKeys() { + final var imi1 = createFakeInputMethodInfo(TEST_IME_ID1, createFakeSubtypes(0)); + final var imi2 = createFakeInputMethodInfo(TEST_IME_ID2, createFakeSubtypes(3)); + final var imi3 = createFakeInputMethodInfo(TEST_IME_ID3, createFakeSubtypes(3)); + assertFalse("Must return false if keys are different", + InputMethodMap.areSame(toMap(imi1), toMap(imi1, imi2))); + assertFalse("Must return false if keys are different", + InputMethodMap.areSame(toMap(imi1, imi2), toMap(imi1))); + assertFalse("Must return false if keys are different", + InputMethodMap.areSame(toMap(imi1, imi2), toMap(imi1, imi3))); + } + + @Test + public void testAreSameDifferentValues() { + final var imi1_without_subtypes = + createFakeInputMethodInfo(TEST_IME_ID1, createFakeSubtypes(0)); + final var imi1_with_subtypes = + createFakeInputMethodInfo(TEST_IME_ID1, createFakeSubtypes(3)); + final var imi2 = createFakeInputMethodInfo(TEST_IME_ID2, createFakeSubtypes(3)); + assertFalse("Must return false if values are different", + InputMethodMap.areSame(toMap(imi1_without_subtypes), toMap(imi1_with_subtypes))); + assertFalse("Must return false if values are different", + InputMethodMap.areSame( + toMap(imi1_without_subtypes, imi2), + toMap(imi1_with_subtypes, imi2))); + } +} diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/TestUtils.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/TestUtils.java new file mode 100644 index 000000000000..c51ff87fdae5 --- /dev/null +++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/TestUtils.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.inputmethod; + +import android.content.ComponentName; +import android.content.pm.ApplicationInfo; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; +import android.view.inputmethod.InputMethodInfo; +import android.view.inputmethod.InputMethodSubtype; + +import androidx.annotation.NonNull; + +import java.util.ArrayList; +import java.util.Objects; + +public final class TestUtils { + /** + * {@link ComponentName} for fake {@link InputMethodInfo}. + */ + @NonNull + public static final ComponentName TEST_IME_ID1 = Objects.requireNonNull( + ComponentName.unflattenFromString("com.android.test.testime1/.InputMethod")); + + /** + * {@link ComponentName} for fake {@link InputMethodInfo}. + */ + @NonNull + public static final ComponentName TEST_IME_ID2 = Objects.requireNonNull( + ComponentName.unflattenFromString("com.android.test.testime2/.InputMethod")); + + /** + * {@link ComponentName} for fake {@link InputMethodInfo}. + */ + @NonNull + public static final ComponentName TEST_IME_ID3 = Objects.requireNonNull( + ComponentName.unflattenFromString("com.android.test.testime3/.InputMethod")); + + /** + * Creates a list of fake {@link InputMethodSubtype} for unit testing for the given number. + * + * @param count The number of fake {@link InputMethodSubtype} objects + * @return The list of fake {@link InputMethodSubtype} objects + */ + @NonNull + public static ArrayList<InputMethodSubtype> createFakeSubtypes(int count) { + final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(count); + for (int i = 0; i < count; ++i) { + subtypes.add( + new InputMethodSubtype.InputMethodSubtypeBuilder() + .setSubtypeId(i + 0x100) + .setLanguageTag("en-US") + .setSubtypeNameOverride("TestSubtype" + i) + .build()); + } + return subtypes; + } + + /** + * Creates a fake {@link InputMethodInfo} for unit testing. + * + * @param componentName {@link ComponentName} of the fake {@link InputMethodInfo} + * @param subtypes A list of (fake) {@link InputMethodSubtype} + * @return a fake {@link InputMethodInfo} object + */ + @NonNull + public static InputMethodInfo createFakeInputMethodInfo( + @NonNull ComponentName componentName, @NonNull ArrayList<InputMethodSubtype> subtypes) { + final ApplicationInfo ai = new ApplicationInfo(); + ai.packageName = componentName.getPackageName(); + ai.enabled = true; + + final ServiceInfo si = new ServiceInfo(); + si.applicationInfo = ai; + si.enabled = true; + si.packageName = componentName.getPackageName(); + si.name = componentName.getClassName(); + si.exported = true; + si.nonLocalizedLabel = "Fake Label"; + + final ResolveInfo ri = new ResolveInfo(); + ri.serviceInfo = si; + + return new InputMethodInfo(ri, false /* isAuxIme */, null /* settingsActivity */, + subtypes, 0 /* isDefaultResId */, false /* forceDefault */); + } +} diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsResetTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsResetTest.java index d29bf1abd7a3..3635e9a749e2 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsResetTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsResetTest.java @@ -19,6 +19,7 @@ package com.android.server.power.stats; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import android.content.Context; import android.os.BatteryManager; @@ -49,9 +50,9 @@ public class BatteryStatsResetTest { private static final int BATTERY_CHARGE_RATE_SECONDS_PER_LEVEL = 100; private MockClock mMockClock; + private BatteryStatsImpl.BatteryStatsConfig mConfig; private MockBatteryStatsImpl mBatteryStatsImpl; - /** * Battery status. Must be one of the following: * {@link BatteryManager#BATTERY_STATUS_UNKNOWN} @@ -91,8 +92,9 @@ public class BatteryStatsResetTest { @Before public void setUp() throws IOException { + mConfig = mock(BatteryStatsImpl.BatteryStatsConfig.class); mMockClock = new MockClock(); - mBatteryStatsImpl = new MockBatteryStatsImpl(mMockClock, + mBatteryStatsImpl = new MockBatteryStatsImpl(mConfig, mMockClock, Files.createTempDirectory("BatteryStatsResetTest").toFile()); mBatteryStatsImpl.onSystemReady(mock(Context.class)); @@ -110,10 +112,7 @@ public class BatteryStatsResetTest { @Test public void testResetOnUnplug_highBatteryLevel() { - mBatteryStatsImpl.setBatteryStatsConfig( - new BatteryStatsImpl.BatteryStatsConfig.Builder() - .setResetOnUnplugHighBatteryLevel(true) - .build()); + when(mConfig.shouldResetOnUnplugHighBatteryLevel()).thenReturn(true); long expectedResetTimeUs = 0; @@ -137,10 +136,7 @@ public class BatteryStatsResetTest { assertThat(mBatteryStatsImpl.getStatsStartRealtime()).isEqualTo(expectedResetTimeUs); // disable high battery level reset on unplug. - mBatteryStatsImpl.setBatteryStatsConfig( - new BatteryStatsImpl.BatteryStatsConfig.Builder() - .setResetOnUnplugHighBatteryLevel(false) - .build()); + when(mConfig.shouldResetOnUnplugHighBatteryLevel()).thenReturn(false); dischargeToLevel(60); @@ -153,10 +149,7 @@ public class BatteryStatsResetTest { @Test public void testResetOnUnplug_significantCharge() { - mBatteryStatsImpl.setBatteryStatsConfig( - new BatteryStatsImpl.BatteryStatsConfig.Builder() - .setResetOnUnplugAfterSignificantCharge(true) - .build()); + when(mConfig.shouldResetOnUnplugAfterSignificantCharge()).thenReturn(true); long expectedResetTimeUs = 0; unplugBattery(); @@ -186,10 +179,7 @@ public class BatteryStatsResetTest { assertThat(mBatteryStatsImpl.getStatsStartRealtime()).isEqualTo(expectedResetTimeUs); // disable reset on unplug after significant charge. - mBatteryStatsImpl.setBatteryStatsConfig( - new BatteryStatsImpl.BatteryStatsConfig.Builder() - .setResetOnUnplugAfterSignificantCharge(false) - .build()); + when(mConfig.shouldResetOnUnplugAfterSignificantCharge()).thenReturn(false); // Battery level dropped below 20%. dischargeToLevel(15); @@ -256,11 +246,9 @@ public class BatteryStatsResetTest { @Test public void testResetWhilePluggedIn_longPlugIn() { // disable high battery level reset on unplug. - mBatteryStatsImpl.setBatteryStatsConfig( - new BatteryStatsImpl.BatteryStatsConfig.Builder() - .setResetOnUnplugHighBatteryLevel(false) - .setResetOnUnplugAfterSignificantCharge(false) - .build()); + when(mConfig.shouldResetOnUnplugHighBatteryLevel()).thenReturn(false); + when(mConfig.shouldResetOnUnplugAfterSignificantCharge()).thenReturn(false); + long expectedResetTimeUs = 0; plugBattery(BatteryManager.BATTERY_PLUGGED_USB); diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java index 2d7cb2245c0a..6edfedee9e5b 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java @@ -98,10 +98,12 @@ public class BatteryUsageStatsRule implements TestRule { mFreqsByPolicy.put(0, new int[]{300000, 1000000, 2000000}); mFreqsByPolicy.put(4, new int[]{300000, 1000000, 2500000, 3000000}); mBatteryStatsConfigBuilder = new BatteryStatsImpl.BatteryStatsConfig.Builder() - .setPowerStatsThrottlePeriodMillis(BatteryConsumer.POWER_COMPONENT_CPU, - 10000) - .setPowerStatsThrottlePeriodMillis(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO, - 10000); + .setPowerStatsThrottlePeriodMillis( + BatteryConsumer.powerComponentIdToString( + BatteryConsumer.POWER_COMPONENT_CPU), 10000) + .setPowerStatsThrottlePeriodMillis( + BatteryConsumer.powerComponentIdToString( + BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO), 10000); } private void initBatteryStats() { @@ -290,7 +292,8 @@ public class BatteryUsageStatsRule implements TestRule { public BatteryUsageStatsRule setPowerStatsThrottlePeriodMillis(int powerComponent, long throttleMs) { - mBatteryStatsConfigBuilder.setPowerStatsThrottlePeriodMillis(powerComponent, throttleMs); + mBatteryStatsConfigBuilder.setPowerStatsThrottlePeriodMillis( + BatteryConsumer.powerComponentIdToString(powerComponent), throttleMs); return this; } diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java index b5bdafeb4696..d1105a4a9077 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java @@ -129,6 +129,11 @@ public class CpuPowerStatsCollectorTest { } @Override + public long getPowerStatsCollectionThrottlePeriod(String powerComponentName) { + return 0; + } + + @Override public int getDefaultCpuPowerBrackets() { return mDefaultCpuPowerBrackets; } @@ -418,8 +423,8 @@ public class CpuPowerStatsCollectorTest { private CpuPowerStatsCollector createCollector(int defaultCpuPowerBrackets, int defaultCpuPowerBracketsPerEnergyConsumer) { CpuPowerStatsCollector collector = new CpuPowerStatsCollector( - new TestInjector(defaultCpuPowerBrackets, defaultCpuPowerBracketsPerEnergyConsumer), - 0); + new TestInjector(defaultCpuPowerBrackets, defaultCpuPowerBracketsPerEnergyConsumer) + ); collector.addConsumer(stats -> mCollectedStats = stats); collector.setEnabled(true); return collector; diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsCollectorTest.java index ca58db1bcad5..0275319a40e2 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsCollectorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsCollectorTest.java @@ -123,6 +123,11 @@ public class MobileRadioPowerStatsCollectorTest { } @Override + public long getPowerStatsCollectionThrottlePeriod(String powerComponentName) { + return 0; + } + + @Override public PackageManager getPackageManager() { return mPackageManager; } @@ -348,7 +353,7 @@ public class MobileRadioPowerStatsCollectorTest { } private PowerStats collectPowerStats(boolean perNetworkTypeData) throws Throwable { - MobileRadioPowerStatsCollector collector = new MobileRadioPowerStatsCollector(mInjector, 0); + MobileRadioPowerStatsCollector collector = new MobileRadioPowerStatsCollector(mInjector); collector.setEnabled(true); when(mConsumedEnergyRetriever.getEnergyConsumerIds( diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsProcessorTest.java index d80ffb4a66c8..29ef3b6551a6 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsProcessorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsProcessorTest.java @@ -114,6 +114,11 @@ public class MobileRadioPowerStatsProcessorTest { } @Override + public long getPowerStatsCollectionThrottlePeriod(String powerComponentName) { + return 0; + } + + @Override public PackageManager getPackageManager() { return mPackageManager; } @@ -186,7 +191,7 @@ public class MobileRadioPowerStatsProcessorTest { aggregatedStats.setUidState(APP_UID, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND, 0); aggregatedStats.setUidState(APP_UID2, STATE_PROCESS_STATE, PROCESS_STATE_CACHED, 0); - MobileRadioPowerStatsCollector collector = new MobileRadioPowerStatsCollector(mInjector, 0); + MobileRadioPowerStatsCollector collector = new MobileRadioPowerStatsCollector(mInjector); collector.setEnabled(true); // Initial empty ModemActivityInfo. @@ -425,7 +430,7 @@ public class MobileRadioPowerStatsProcessorTest { aggregatedStats.setUidState(APP_UID, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND, 0); aggregatedStats.setUidState(APP_UID2, STATE_PROCESS_STATE, PROCESS_STATE_CACHED, 0); - MobileRadioPowerStatsCollector collector = new MobileRadioPowerStatsCollector(mInjector, 0); + MobileRadioPowerStatsCollector collector = new MobileRadioPowerStatsCollector(mInjector); collector.setEnabled(true); // Initial empty ModemActivityInfo. diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java index 1d48975c086e..2c03f9d1a9aa 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java @@ -72,6 +72,11 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl { this(DEFAULT_CONFIG, clock, historyDirectory, handler, new PowerStatsUidResolver()); } + MockBatteryStatsImpl(BatteryStatsConfig config, Clock clock, File historyDirectory) { + this(config, clock, historyDirectory, new Handler(Looper.getMainLooper()), + new PowerStatsUidResolver()); + } + MockBatteryStatsImpl(BatteryStatsConfig config, Clock clock, File historyDirectory, Handler handler, PowerStatsUidResolver powerStatsUidResolver) { super(config, clock, new MonotonicClock(0, clock), historyDirectory, handler, @@ -137,13 +142,6 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl { return MOBILE_RADIO_POWER_STATE_UPDATE_FREQ_MS; } - public MockBatteryStatsImpl setBatteryStatsConfig(BatteryStatsConfig config) { - synchronized (this) { - mBatteryStatsConfig = config; - } - return this; - } - public MockBatteryStatsImpl setNetworkStats(NetworkStats networkStats) { mNetworkStats = networkStats; return this; diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PhoneCallPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PhoneCallPowerStatsProcessorTest.java index dadcf3f3871e..69d655bc35c0 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/PhoneCallPowerStatsProcessorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/PhoneCallPowerStatsProcessorTest.java @@ -98,6 +98,11 @@ public class PhoneCallPowerStatsProcessorTest { } @Override + public long getPowerStatsCollectionThrottlePeriod(String powerComponentName) { + return 0; + } + + @Override public PackageManager getPackageManager() { return mPackageManager; } @@ -175,7 +180,7 @@ public class PhoneCallPowerStatsProcessorTest { aggregatedPowerStats.setDeviceState(STATE_POWER, POWER_STATE_OTHER, 0); aggregatedPowerStats.setDeviceState(STATE_SCREEN, SCREEN_STATE_ON, 0); - MobileRadioPowerStatsCollector collector = new MobileRadioPowerStatsCollector(mInjector, 0); + MobileRadioPowerStatsCollector collector = new MobileRadioPowerStatsCollector(mInjector); collector.setEnabled(true); // Initial empty ModemActivityInfo. diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerStatsCollectorTest.java index b4c012b3460f..a280cfe176a3 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerStatsCollectorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerStatsCollectorTest.java @@ -138,6 +138,11 @@ public class WifiPowerStatsCollectorTest { } @Override + public long getPowerStatsCollectionThrottlePeriod(String powerComponentName) { + return 0; + } + + @Override public PackageManager getPackageManager() { return mPackageManager; } @@ -317,7 +322,7 @@ public class WifiPowerStatsCollectorTest { private PowerStats collectPowerStats(boolean hasPowerReporting) { when(mWifiManager.isEnhancedPowerReportingSupported()).thenReturn(hasPowerReporting); - WifiPowerStatsCollector collector = new WifiPowerStatsCollector(mInjector, 0); + WifiPowerStatsCollector collector = new WifiPowerStatsCollector(mInjector); collector.setEnabled(true); when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.WIFI)) diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerStatsProcessorTest.java index 257a1a67f7b0..3ceaf357150e 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerStatsProcessorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerStatsProcessorTest.java @@ -142,6 +142,11 @@ public class WifiPowerStatsProcessorTest { } @Override + public long getPowerStatsCollectionThrottlePeriod(String powerComponentName) { + return 0; + } + + @Override public PackageManager getPackageManager() { return mPackageManager; } @@ -195,7 +200,7 @@ public class WifiPowerStatsProcessorTest { PowerComponentAggregatedPowerStats aggregatedStats = createAggregatedPowerStats(processor); - WifiPowerStatsCollector collector = new WifiPowerStatsCollector(mInjector, 0); + WifiPowerStatsCollector collector = new WifiPowerStatsCollector(mInjector); collector.setEnabled(true); // Initial empty WifiActivityEnergyInfo. @@ -307,7 +312,7 @@ public class WifiPowerStatsProcessorTest { PowerComponentAggregatedPowerStats aggregatedStats = createAggregatedPowerStats(processor); - WifiPowerStatsCollector collector = new WifiPowerStatsCollector(mInjector, 0); + WifiPowerStatsCollector collector = new WifiPowerStatsCollector(mInjector); collector.setEnabled(true); // Initial empty WifiActivityEnergyInfo. @@ -420,7 +425,7 @@ public class WifiPowerStatsProcessorTest { PowerComponentAggregatedPowerStats aggregatedStats = createAggregatedPowerStats(processor); - WifiPowerStatsCollector collector = new WifiPowerStatsCollector(mInjector, 0); + WifiPowerStatsCollector collector = new WifiPowerStatsCollector(mInjector); collector.setEnabled(true); // Establish a baseline diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java index cb4fc753aa4d..ca15aa2a9d52 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java @@ -30,7 +30,9 @@ import static com.android.window.flags.Flags.FLAG_ALWAYS_DRAW_MAGNIFICATION_FULL import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; @@ -42,6 +44,7 @@ import static org.mockito.Mockito.doCallRealMethod; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -102,6 +105,7 @@ import com.android.internal.accessibility.common.ShortcutConstants.UserShortcutT import com.android.internal.accessibility.util.AccessibilityUtils; import com.android.internal.accessibility.util.ShortcutUtils; import com.android.internal.compat.IPlatformCompat; +import com.android.internal.content.PackageMonitor; import com.android.server.LocalServices; import com.android.server.accessibility.AccessibilityManagerService.AccessibilityDisplayListener; import com.android.server.accessibility.magnification.FullScreenMagnificationController; @@ -1620,6 +1624,67 @@ public class AccessibilityManagerServiceTest { .containsExactlyElementsIn(Set.of(daltonizerTile)); } + @Test + @EnableFlags(Flags.FLAG_MANAGER_PACKAGE_MONITOR_LOGIC_FIX) + public void onHandleForceStop_dontDoIt_packageEnabled_returnsTrue() { + setupShortcutTargetServices(); + AccessibilityUserState userState = mA11yms.getCurrentUserState(); + userState.mEnabledServices.addAll( + userState.mInstalledServices.stream().map( + (AccessibilityServiceInfo::getComponentName)).toList()); + String[] packages = userState.mEnabledServices.stream().map( + ComponentName::getPackageName).toList().toArray(new String[0]); + + PackageMonitor monitor = spy(mA11yms.getPackageMonitor()); + when(monitor.getChangingUserId()).thenReturn(UserHandle.USER_SYSTEM); + mA11yms.setPackageMonitor(monitor); + + assertTrue(mA11yms.getPackageMonitor().onHandleForceStop( + new Intent(), + packages, + UserHandle.USER_SYSTEM, + false + )); + } + + @Test + @EnableFlags(Flags.FLAG_MANAGER_PACKAGE_MONITOR_LOGIC_FIX) + public void onHandleForceStop_doIt_packageEnabled_returnsFalse() { + setupShortcutTargetServices(); + AccessibilityUserState userState = mA11yms.getCurrentUserState(); + userState.mEnabledServices.addAll( + userState.mInstalledServices.stream().map( + (AccessibilityServiceInfo::getComponentName)).toList()); + String[] packages = userState.mEnabledServices.stream().map( + ComponentName::getPackageName).toList().toArray(new String[0]); + + PackageMonitor monitor = spy(mA11yms.getPackageMonitor()); + when(monitor.getChangingUserId()).thenReturn(UserHandle.USER_SYSTEM); + mA11yms.setPackageMonitor(monitor); + + assertFalse(mA11yms.getPackageMonitor().onHandleForceStop( + new Intent(), + packages, + UserHandle.USER_SYSTEM, + true + )); + } + + @Test + @EnableFlags(Flags.FLAG_MANAGER_PACKAGE_MONITOR_LOGIC_FIX) + public void onHandleForceStop_dontDoIt_packageNotEnabled_returnsFalse() { + PackageMonitor monitor = spy(mA11yms.getPackageMonitor()); + when(monitor.getChangingUserId()).thenReturn(UserHandle.USER_SYSTEM); + mA11yms.setPackageMonitor(monitor); + + assertFalse(mA11yms.getPackageMonitor().onHandleForceStop( + new Intent(), + new String[]{ "FOO", "BAR"}, + UserHandle.USER_SYSTEM, + false + )); + } + private static AccessibilityServiceInfo mockAccessibilityServiceInfo( ComponentName componentName) { return mockAccessibilityServiceInfo( @@ -1630,7 +1695,7 @@ public class AccessibilityManagerServiceTest { ComponentName componentName, boolean isSystemApp, boolean isAlwaysOnService) { AccessibilityServiceInfo accessibilityServiceInfo = - Mockito.spy(new AccessibilityServiceInfo()); + spy(new AccessibilityServiceInfo()); accessibilityServiceInfo.setComponentName(componentName); ResolveInfo mockResolveInfo = Mockito.mock(ResolveInfo.class); when(accessibilityServiceInfo.getResolveInfo()).thenReturn(mockResolveInfo); diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java index b5f0a527de7b..b50684bb7a25 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java @@ -26,6 +26,8 @@ import static com.android.server.hdmi.Constants.ADDR_TV; import static com.android.server.hdmi.HdmiCecLocalDevice.ActiveSource; import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC; import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_WAKE_UP_MESSAGE; +import static com.android.server.hdmi.HdmiControlService.STANDBY_SCREEN_OFF; +import static com.android.server.hdmi.HdmiControlService.WAKE_UP_SCREEN_ON; import static com.google.common.truth.Truth.assertThat; @@ -1780,9 +1782,17 @@ public class HdmiCecLocalDeviceTvTest { HdmiCecMessage activeSourceFromTv = HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000); - mHdmiControlService.getHdmiCecNetwork().clearLocalDevices(); + // Go to standby to invalidate the active source on the local device s.t. the + // RequestActiveSourceAction will start. + mHdmiControlService.onStandby(STANDBY_SCREEN_OFF); + mTestLooper.dispatchAll(); + mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS); - mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); + mHdmiControlService.onWakeUp(WAKE_UP_SCREEN_ON); + mTestLooper.dispatchAll(); + + // Skip the LauncherX API timeout. + mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS * 2); mTestLooper.dispatchAll(); assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource); @@ -1791,6 +1801,10 @@ public class HdmiCecLocalDeviceTvTest { mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS); mTestLooper.dispatchAll(); + // Assume there was a retry and the action did not finish earlier. + mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS); + mTestLooper.dispatchAll(); + assertThat(mNativeWrapper.getResultMessages()).doesNotContain(activeSourceFromTv); } @@ -1800,9 +1814,18 @@ public class HdmiCecLocalDeviceTvTest { HdmiCecMessageBuilder.buildRequestActiveSource(ADDR_TV); HdmiCecMessage activeSourceFromTv = HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000); - mHdmiControlService.getHdmiCecNetwork().clearLocalDevices(); + + // Go to standby to invalidate the active source on the local device s.t. the + // RequestActiveSourceAction will start. + mHdmiControlService.onStandby(STANDBY_SCREEN_OFF); + mTestLooper.dispatchAll(); + mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS); - mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); + mHdmiControlService.onWakeUp(WAKE_UP_SCREEN_ON); + mTestLooper.dispatchAll(); + + // Skip the LauncherX API timeout. + mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS * 2); mTestLooper.dispatchAll(); assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource); @@ -1827,8 +1850,18 @@ public class HdmiCecLocalDeviceTvTest { HdmiCecMessageBuilder.buildRequestActiveSource(ADDR_TV); HdmiCecMessage activeSourceFromTv = HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000); - mHdmiControlService.getHdmiCecNetwork().clearLocalDevices(); - mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); + + // Go to standby to invalidate the active source on the local device s.t. the + // RequestActiveSourceAction will start. + mHdmiControlService.onStandby(STANDBY_SCREEN_OFF); + mTestLooper.dispatchAll(); + + mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS); + mHdmiControlService.onWakeUp(WAKE_UP_SCREEN_ON); + mTestLooper.dispatchAll(); + + // Skip the LauncherX API timeout. + mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS * 2); mTestLooper.dispatchAll(); assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource); @@ -1847,8 +1880,16 @@ public class HdmiCecLocalDeviceTvTest { HdmiCecMessageBuilder.buildRequestActiveSource(ADDR_TV); HdmiCecMessage activeSourceFromTv = HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000); - mHdmiControlService.getHdmiCecNetwork().clearLocalDevices(); - mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); + + // Go to standby to invalidate the active source on the local device s.t. the + // RequestActiveSourceAction will start. + mHdmiControlService.onStandby(STANDBY_SCREEN_OFF); + mTestLooper.dispatchAll(); + + mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS); + mHdmiControlService.onWakeUp(WAKE_UP_SCREEN_ON); + mTestLooper.dispatchAll(); + HdmiDeviceInfo playbackDevice = HdmiDeviceInfo.cecDeviceBuilder() .setLogicalAddress(ADDR_PLAYBACK_1) .setPhysicalAddress(0x1000) @@ -1862,6 +1903,10 @@ public class HdmiCecLocalDeviceTvTest { mHdmiControlService.getHdmiCecNetwork().addCecDevice(playbackDevice); mTestLooper.dispatchAll(); + // Skip the LauncherX API timeout. + mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS * 2); + mTestLooper.dispatchAll(); + assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource); mNativeWrapper.clearResultMessages(); mHdmiCecLocalDeviceTv.deviceSelect(playbackDevice.getId(), null); @@ -1874,6 +1919,41 @@ public class HdmiCecLocalDeviceTvTest { assertThat(mNativeWrapper.getResultMessages()).doesNotContain(activeSourceFromTv); } + @Test + public void onAddressAllocated_sendSourceChangingMessage_noRequestActiveSourceMessage() { + HdmiCecMessage requestActiveSource = + HdmiCecMessageBuilder.buildRequestActiveSource(ADDR_TV); + HdmiCecMessage activeSourceFromTv = + HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000); + HdmiCecMessage setStreamPathFromTv = + HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV, 0x2000); + + // Go to standby to invalidate the active source on the local device s.t. the + // RequestActiveSourceAction will start. + mHdmiControlService.onStandby(STANDBY_SCREEN_OFF); + mTestLooper.dispatchAll(); + + mHdmiControlService.onWakeUp(WAKE_UP_SCREEN_ON); + mTestLooper.dispatchAll(); + + // Even if the device at the end of this path doesn't answer to this message, TV should not + // continue the RequestActiveSourceAction. + mHdmiControlService.sendCecCommand(setStreamPathFromTv); + + // Skip the LauncherX API timeout. + mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS * 2); + mTestLooper.dispatchAll(); + + assertThat(mNativeWrapper.getResultMessages()).doesNotContain(requestActiveSource); + mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS); + mTestLooper.dispatchAll(); + + // Assume there was a retry and the action did not finish earlier. + mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS); + mTestLooper.dispatchAll(); + + assertThat(mNativeWrapper.getResultMessages()).doesNotContain(activeSourceFromTv); + } @Test public void newDeviceConnectedIfOnlyOneGiveOsdNameSent() { @@ -1900,7 +1980,12 @@ public class HdmiCecLocalDeviceTvTest { HdmiCecMessage activeSourceFromTv = HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000); - mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_WAKE_UP_MESSAGE); + // Go to standby to invalidate the active source on the local device s.t. the + // TV will send <Active Source> when it selects its internal source. + mHdmiControlService.onStandby(STANDBY_SCREEN_OFF); + mTestLooper.dispatchAll(); + + mHdmiControlService.onWakeUp(WAKE_UP_SCREEN_ON); mTestLooper.dispatchAll(); mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS); @@ -1930,6 +2015,8 @@ public class HdmiCecLocalDeviceTvTest { public void handleStandby_fromActiveSource_standby() { mPowerManager.setInteractive(true); mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); + mTestLooper.dispatchAll(); + mHdmiControlService.setActiveSource(ADDR_PLAYBACK_1, 0x1000, "HdmiCecLocalDeviceTvTest"); mTestLooper.dispatchAll(); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java index a3d57c3df51e..70a003814036 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java @@ -24,6 +24,8 @@ import static android.app.NotificationManager.IMPORTANCE_HIGH; import static android.app.NotificationManager.IMPORTANCE_LOW; import static android.app.NotificationManager.IMPORTANCE_MIN; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS; +import static android.content.pm.PackageManager.PERMISSION_DENIED; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.media.AudioAttributes.USAGE_NOTIFICATION; import static android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE; @@ -52,6 +54,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.Manifest.permission; import android.annotation.SuppressLint; import android.app.ActivityManager; import android.app.KeyguardManager; @@ -190,6 +193,8 @@ public class NotificationAttentionHelperTest extends UiServiceTestCase { getContext().addMockSystemService(Vibrator.class, mVibrator); getContext().addMockSystemService(PackageManager.class, mPackageManager); when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)).thenReturn(false); + when(mPackageManager.checkPermission(eq(permission.RECEIVE_EMERGENCY_BROADCAST), + anyString())).thenReturn(PERMISSION_DENIED); when(mAudioManager.isAudioFocusExclusive()).thenReturn(false); when(mAudioManager.getRingtonePlayer()).thenReturn(mRingtonePlayer); @@ -2363,6 +2368,72 @@ public class NotificationAttentionHelperTest extends UiServiceTestCase { } @Test + public void testBeepVolume_politeNotif_Avalanche_exemptEmergency() throws Exception { + mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS); + mSetFlagsRule.enableFlags(Flags.FLAG_CROSS_APP_POLITE_NOTIFICATIONS); + mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS_ATTN_UPDATE); + TestableFlagResolver flagResolver = new TestableFlagResolver(); + flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME1, 50); + flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0); + initAttentionHelper(flagResolver); + + // Trigger avalanche trigger intent + final Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED); + intent.putExtra("state", false); + mAvalancheBroadcastReceiver.onReceive(getContext(), intent); + + NotificationRecord r = getBeepyNotification(); + + // Grant RECEIVE_EMERGENCY_BROADCAST to notification's package + when(mPackageManager.checkPermission(eq(permission.RECEIVE_EMERGENCY_BROADCAST), + eq(r.getSbn().getPackageName()))).thenReturn(PERMISSION_GRANTED); + + // Should beep at 100% volume + mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS); + verifyBeepVolume(1.0f); + assertNotEquals(-1, r.getLastAudiblyAlertedMs()); + verify(mAccessibilityService, times(1)).sendAccessibilityEvent(any(), anyInt()); + } + + @Test + public void testBeepVolume_politeNotif_exemptEmergency() throws Exception { + mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS); + mSetFlagsRule.disableFlags(Flags.FLAG_CROSS_APP_POLITE_NOTIFICATIONS); + mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS_ATTN_UPDATE); + TestableFlagResolver flagResolver = new TestableFlagResolver(); + flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME1, 50); + flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0); + // NOTIFICATION_COOLDOWN_ALL setting is enabled + Settings.System.putInt(getContext().getContentResolver(), + Settings.System.NOTIFICATION_COOLDOWN_ALL, 1); + initAttentionHelper(flagResolver); + + NotificationRecord r = getBeepyNotification(); + + // Grant RECEIVE_EMERGENCY_BROADCAST to notification's package + when(mPackageManager.checkPermission(eq(permission.RECEIVE_EMERGENCY_BROADCAST), + eq(r.getSbn().getPackageName()))).thenReturn(PERMISSION_GRANTED); + + // set up internal state + mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS); + Mockito.reset(mRingtonePlayer); + + // update should beep at 100% volume + NotificationRecord r2 = getBeepyNotification(); + mAttentionHelper.buzzBeepBlinkLocked(r2, DEFAULT_SIGNALS); + assertNotEquals(-1, r2.getLastAudiblyAlertedMs()); + verifyBeepVolume(1.0f); + + // 2nd update should beep at 100% volume + Mockito.reset(mRingtonePlayer); + mAttentionHelper.buzzBeepBlinkLocked(r2, DEFAULT_SIGNALS); + assertNotEquals(-1, r2.getLastAudiblyAlertedMs()); + verifyBeepVolume(1.0f); + + verify(mAccessibilityService, times(3)).sendAccessibilityEvent(any(), anyInt()); + } + + @Test public void testBeepVolume_politeNotif_applyPerApp() throws Exception { mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS); mSetFlagsRule.disableFlags(Flags.FLAG_CROSS_APP_POLITE_NOTIFICATIONS); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRefresherTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRefresherTests.java new file mode 100644 index 000000000000..12ab3e1bdf19 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRefresherTests.java @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.wm; + +import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; +import static android.app.servertransaction.ActivityLifecycleItem.ON_PAUSE; +import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.servertransaction.RefreshCallbackItem; +import android.app.servertransaction.ResumeActivityItem; +import android.content.ComponentName; +import android.content.res.Configuration; +import android.os.Handler; +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Tests for {@link ActivityRefresher}. + * + * <p>Build/Install/Run: + * atest WmTests:ActivityRefresherTests + */ +@SmallTest +@Presubmit +@RunWith(WindowTestRunner.class) +public class ActivityRefresherTests extends WindowTestsBase { + private Handler mMockHandler; + private LetterboxConfiguration mLetterboxConfiguration; + + private ActivityRecord mActivity; + private ActivityRefresher mActivityRefresher; + + private ActivityRefresher.Evaluator mEvaluatorFalse = + (activity, newConfig, lastReportedConfig) -> false; + + private ActivityRefresher.Evaluator mEvaluatorTrue = + (activity, newConfig, lastReportedConfig) -> true; + + private final Configuration mNewConfig = new Configuration(); + private final Configuration mOldConfig = new Configuration(); + + @Before + public void setUp() throws Exception { + mLetterboxConfiguration = mDisplayContent.mWmService.mLetterboxConfiguration; + spyOn(mLetterboxConfiguration); + when(mLetterboxConfiguration.isCameraCompatTreatmentEnabled()) + .thenReturn(true); + when(mLetterboxConfiguration.isCameraCompatRefreshEnabled()) + .thenReturn(true); + when(mLetterboxConfiguration.isCameraCompatRefreshCycleThroughStopEnabled()) + .thenReturn(true); + + mMockHandler = mock(Handler.class); + when(mMockHandler.postDelayed(any(Runnable.class), anyLong())).thenAnswer( + invocation -> { + ((Runnable) invocation.getArgument(0)).run(); + return null; + }); + + mActivityRefresher = new ActivityRefresher(mDisplayContent.mWmService, mMockHandler); + } + + @Test + public void testShouldRefreshActivity_refreshDisabled() throws Exception { + when(mLetterboxConfiguration.isCameraCompatRefreshEnabled()) + .thenReturn(false); + configureActivityAndDisplay(); + mActivityRefresher.addEvaluator(mEvaluatorTrue); + + mActivityRefresher.onActivityConfigurationChanging(mActivity, mNewConfig, mOldConfig); + + assertActivityRefreshRequested(/* refreshRequested= */ false); + } + + @Test + public void testShouldRefreshActivity_refreshDisabledForActivity() throws Exception { + configureActivityAndDisplay(); + when(mActivity.mLetterboxUiController.shouldRefreshActivityForCameraCompat()) + .thenReturn(false); + mActivityRefresher.addEvaluator(mEvaluatorTrue); + + mActivityRefresher.onActivityConfigurationChanging(mActivity, mNewConfig, mOldConfig); + + assertActivityRefreshRequested(/* refreshRequested= */ false); + } + + @Test + public void testShouldRefreshActivity_noRefreshTriggerers() throws Exception { + configureActivityAndDisplay(); + + mActivityRefresher.onActivityConfigurationChanging(mActivity, mNewConfig, mOldConfig); + + assertActivityRefreshRequested(/* refreshRequested= */ false); + } + + @Test + public void testShouldRefreshActivity_refreshTriggerersReturnFalse() throws Exception { + configureActivityAndDisplay(); + mActivityRefresher.addEvaluator(mEvaluatorFalse); + + mActivityRefresher.onActivityConfigurationChanging(mActivity, mNewConfig, mOldConfig); + + assertActivityRefreshRequested(/* refreshRequested= */ false); + } + + @Test + public void testShouldRefreshActivity_anyRefreshTriggerersReturnTrue() throws Exception { + configureActivityAndDisplay(); + mActivityRefresher.addEvaluator(mEvaluatorFalse); + mActivityRefresher.addEvaluator(mEvaluatorTrue); + + mActivityRefresher.onActivityConfigurationChanging(mActivity, mNewConfig, mOldConfig); + + assertActivityRefreshRequested(/* refreshRequested= */ true); + } + + @Test + public void testOnActivityConfigurationChanging_cycleThroughStopDisabled() + throws Exception { + mActivityRefresher.addEvaluator(mEvaluatorTrue); + when(mLetterboxConfiguration.isCameraCompatRefreshCycleThroughStopEnabled()) + .thenReturn(false); + configureActivityAndDisplay(); + + mActivityRefresher.onActivityConfigurationChanging(mActivity, mNewConfig, mOldConfig); + + assertActivityRefreshRequested(/* refreshRequested */ true, /* cycleThroughStop */ false); + } + + @Test + public void testOnActivityConfigurationChanging_cycleThroughPauseEnabledForApp() + throws Exception { + configureActivityAndDisplay(); + mActivityRefresher.addEvaluator(mEvaluatorTrue); + doReturn(true).when(mActivity.mLetterboxUiController) + .shouldRefreshActivityViaPauseForCameraCompat(); + + mActivityRefresher.onActivityConfigurationChanging(mActivity, mNewConfig, mOldConfig); + + assertActivityRefreshRequested(/* refreshRequested */ true, /* cycleThroughStop */ false); + } + + @Test + public void testOnActivityRefreshed_setIsRefreshRequestedToFalse() throws Exception { + configureActivityAndDisplay(); + mActivityRefresher.addEvaluator(mEvaluatorTrue); + doReturn(true).when(mActivity.mLetterboxUiController) + .shouldRefreshActivityViaPauseForCameraCompat(); + + mActivityRefresher.onActivityRefreshed(mActivity); + + assertActivityRefreshRequested(false); + } + + private void assertActivityRefreshRequested(boolean refreshRequested) throws Exception { + assertActivityRefreshRequested(refreshRequested, /* cycleThroughStop*/ true); + } + + private void assertActivityRefreshRequested(boolean refreshRequested, + boolean cycleThroughStop) throws Exception { + verify(mActivity.mLetterboxUiController, times(refreshRequested ? 1 : 0)) + .setIsRefreshRequested(true); + + final RefreshCallbackItem refreshCallbackItem = RefreshCallbackItem.obtain(mActivity.token, + cycleThroughStop ? ON_STOP : ON_PAUSE); + final ResumeActivityItem resumeActivityItem = ResumeActivityItem.obtain(mActivity.token, + /* isForward */ false, /* shouldSendCompatFakeFocus */ false); + + verify(mActivity.mAtmService.getLifecycleManager(), times(refreshRequested ? 1 : 0)) + .scheduleTransactionAndLifecycleItems(mActivity.app.getThread(), + refreshCallbackItem, resumeActivityItem); + } + + private void configureActivityAndDisplay() { + mActivity = new TaskBuilder(mSupervisor) + .setCreateActivity(true) + .setDisplay(mDisplayContent) + // Set the component to be that of the test class in order to enable compat changes + .setComponent(ComponentName.createRelative(mContext, + ActivityRefresherTests.class.getName())) + .setWindowingMode(WINDOWING_MODE_FREEFORM) + .build() + .getTopMostActivity(); + + spyOn(mActivity.mLetterboxUiController); + doReturn(true).when( + mActivity.mLetterboxUiController).shouldRefreshActivityForCameraCompat(); + + doReturn(true).when(mActivity).inFreeformWindowingMode(); + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java index 262ba8bb32f3..c76acd7e1d6b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java @@ -91,6 +91,7 @@ public final class DisplayRotationCompatPolicyTests extends WindowTestsBase { private CameraManager mMockCameraManager; private Handler mMockHandler; private LetterboxConfiguration mLetterboxConfiguration; + private ActivityRefresher mActivityRefresher; private DisplayRotationCompatPolicy mDisplayRotationCompatPolicy; private CameraManager.AvailabilityCallback mCameraAvailabilityCallback; @@ -132,8 +133,9 @@ public final class DisplayRotationCompatPolicyTests extends WindowTestsBase { }); CameraStateMonitor cameraStateMonitor = new CameraStateMonitor(mDisplayContent, mMockHandler); - mDisplayRotationCompatPolicy = - new DisplayRotationCompatPolicy(mDisplayContent, mMockHandler, cameraStateMonitor); + mActivityRefresher = new ActivityRefresher(mDisplayContent.mWmService, mMockHandler); + mDisplayRotationCompatPolicy = new DisplayRotationCompatPolicy(mDisplayContent, + cameraStateMonitor, mActivityRefresher); // Do not show the real toast. spyOn(mDisplayRotationCompatPolicy); @@ -606,7 +608,7 @@ public final class DisplayRotationCompatPolicyTests extends WindowTestsBase { private void assertActivityRefreshRequested(boolean refreshRequested, boolean cycleThroughStop) throws Exception { verify(mActivity.mLetterboxUiController, times(refreshRequested ? 1 : 0)) - .setIsRefreshAfterRotationRequested(true); + .setIsRefreshRequested(true); final RefreshCallbackItem refreshCallbackItem = RefreshCallbackItem.obtain(mActivity.token, cycleThroughStop ? ON_STOP : ON_PAUSE); @@ -628,7 +630,7 @@ public final class DisplayRotationCompatPolicyTests extends WindowTestsBase { private void callOnActivityConfigurationChanging( ActivityRecord activity, boolean isDisplayRotationChanging) { - mDisplayRotationCompatPolicy.onActivityConfigurationChanging(activity, + mActivityRefresher.onActivityConfigurationChanging(activity, /* newConfig */ createConfigurationWithDisplayRotation(ROTATION_0), /* newConfig */ createConfigurationWithDisplayRotation( isDisplayRotationChanging ? ROTATION_90 : ROTATION_0)); diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java index 0e1a1af9bc48..c69faede7580 100644 --- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java @@ -353,6 +353,17 @@ public class InsetsStateControllerTest extends WindowTestsBase { } @Test + public void testControlTargetChangedWhileProviderHasNoWindow() { + final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); + final InsetsSourceProvider provider = getController().getOrCreateSourceProvider( + ID_STATUS_BAR, statusBars()); + getController().onBarControlTargetChanged(app, null, null, null); + assertNull(getController().getControlsForDispatch(app)); + provider.setWindowContainer(createWindow(null, TYPE_APPLICATION, "statusBar"), null, null); + assertNotNull(getController().getControlsForDispatch(app)); + } + + @Test public void testTransientVisibilityOfFixedRotationState() { final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar"); final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java index 1195c934a6f7..6b17de4c8640 100644 --- a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java @@ -1594,7 +1594,7 @@ public class LetterboxUiControllerTest extends WindowTestsBase { } @Test - @EnableFlags(Flags.FLAG_DISABLE_THIN_LETTERBOXING_REACHABILITY) + @EnableFlags(Flags.FLAG_DISABLE_THIN_LETTERBOXING_POLICY) public void testAllowReachabilityForThinLetterboxWithFlagEnabled() { spyOn(mController); doReturn(true).when(mController).isVerticalThinLetterboxed(); @@ -1609,7 +1609,7 @@ public class LetterboxUiControllerTest extends WindowTestsBase { } @Test - @DisableFlags(Flags.FLAG_DISABLE_THIN_LETTERBOXING_REACHABILITY) + @DisableFlags(Flags.FLAG_DISABLE_THIN_LETTERBOXING_POLICY) public void testAllowReachabilityForThinLetterboxWithFlagDisabled() { spyOn(mController); doReturn(true).when(mController).isVerticalThinLetterboxed(); diff --git a/tests/FlickerTests/ActivityEmbedding/AndroidManifest.xml b/tests/FlickerTests/ActivityEmbedding/AndroidManifest.xml index 6f8f008cf85b..955b43a32827 100644 --- a/tests/FlickerTests/ActivityEmbedding/AndroidManifest.xml +++ b/tests/FlickerTests/ActivityEmbedding/AndroidManifest.xml @@ -19,7 +19,7 @@ xmlns:tools="http://schemas.android.com/tools" package="com.android.server.wm.flicker"> - <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"/> + <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="35"/> <!-- Read and write traces from external storage --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> @@ -46,6 +46,8 @@ <uses-permission android:name="android.permission.START_TASKS_FROM_RECENTS" /> <!-- Allow the test to connect to perfetto trace processor --> <uses-permission android:name="android.permission.INTERNET"/> + <!-- Allow to query for the Launcher TestInfo on SDK 30+ --> + <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" /> <!-- Allow the test to write directly to /sdcard/ and connect to trace processor --> <application android:requestLegacyExternalStorage="true" android:networkSecurityConfig="@xml/network_security_config" diff --git a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt index cf4edd50040b..67825d2df361 100644 --- a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt +++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt @@ -43,6 +43,7 @@ import org.junit.runners.Parameterized * * To run this test: `atest FlickerTestsOther:OpenTrampolineActivityTest` */ +@FlakyTest(bugId = 341209752) @RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @@ -168,7 +169,6 @@ class OpenTrampolineActivityTest(flicker: LegacyFlickerTest) : ActivityEmbedding } } - @FlakyTest(bugId = 290736037) /** Main activity should go from fullscreen to being a split with secondary activity. */ @Test fun mainActivityLayerGoesFromFullscreenToSplit() { @@ -203,7 +203,6 @@ class OpenTrampolineActivityTest(flicker: LegacyFlickerTest) : ActivityEmbedding } } - @FlakyTest(bugId = 288591571) @Test override fun visibleLayersShownMoreThanOneConsecutiveEntry() { super.visibleLayersShownMoreThanOneConsecutiveEntry() diff --git a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt index bc3696b3ed1c..eed9225d3da0 100644 --- a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt +++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt @@ -205,7 +205,8 @@ class SecondaryActivityEnterPipTest(flicker: LegacyFlickerTest) : it.visibleRegion(ComponentNameMatcher.PIP_CONTENT_OVERLAY) val secondaryVisibleRegion = it.visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) - overlayVisibleRegion.coversExactly(secondaryVisibleRegion.region) + // TODO(b/340992001): replace coverAtLeast with coverExactly + overlayVisibleRegion.coversAtLeast(secondaryVisibleRegion.region) } .then() .isInvisible(ComponentNameMatcher.PIP_CONTENT_OVERLAY) diff --git a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt index fb9258304870..379b45cdf08e 100644 --- a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt +++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt @@ -60,14 +60,16 @@ class EnterSystemSplitTest(flicker: LegacyFlickerTest) : ActivityEmbeddingTestBa testApp.launchViaIntent(wmHelper) testApp.launchSecondaryActivity(wmHelper) secondaryApp.launchViaIntent(wmHelper) - tapl.goHome() - wmHelper - .StateSyncBuilder() - .withAppTransitionIdle() - .withHomeActivityVisible() - .waitForAndVerify() startDisplayBounds = wmHelper.currentState.layerState.physicalDisplayBounds ?: error("Display not found") + + // Record the displayBounds before `goHome()` in case the launcher is fixed-portrait. + tapl.goHome() + wmHelper + .StateSyncBuilder() + .withAppTransitionIdle() + .withHomeActivityVisible() + .waitForAndVerify() } transitions { SplitScreenUtils.enterSplit( @@ -138,10 +140,6 @@ class EnterSystemSplitTest(flicker: LegacyFlickerTest) : ActivityEmbeddingTestBa check { "ActivityEmbeddingSplitHeight" } .that(leftAELayerRegion.region.bounds.height()) .isEqual(rightAELayerRegion.region.bounds.height()) - check { "SystemSplitHeight" } - .that(rightAELayerRegion.region.bounds.height()) - .isEqual(secondaryAppLayerRegion.region.bounds.height()) - // TODO(b/292283182): Remove this special case handling. check { "ActivityEmbeddingSplitWidth" } .that( abs( @@ -150,14 +148,6 @@ class EnterSystemSplitTest(flicker: LegacyFlickerTest) : ActivityEmbeddingTestBa ) ) .isLower(2) - check { "SystemSplitWidth" } - .that( - abs( - secondaryAppLayerRegion.region.bounds.width() - - 2 * rightAELayerRegion.region.bounds.width() - ) - ) - .isLower(2) } } @@ -170,15 +160,9 @@ class EnterSystemSplitTest(flicker: LegacyFlickerTest) : ActivityEmbeddingTestBa visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) val rightAEWindowRegion = visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) - // There's no window for the divider bar. - val secondaryAppLayerRegion = - visibleRegion(ActivityOptions.SplitScreen.Primary.COMPONENT.toFlickerComponent()) check { "ActivityEmbeddingSplitHeight" } .that(leftAEWindowRegion.region.bounds.height()) .isEqual(rightAEWindowRegion.region.bounds.height()) - check { "SystemSplitHeight" } - .that(rightAEWindowRegion.region.bounds.height()) - .isEqual(secondaryAppLayerRegion.region.bounds.height()) check { "ActivityEmbeddingSplitWidth" } .that( abs( @@ -187,14 +171,6 @@ class EnterSystemSplitTest(flicker: LegacyFlickerTest) : ActivityEmbeddingTestBa ) ) .isLower(2) - check { "SystemSplitWidth" } - .that( - abs( - secondaryAppLayerRegion.region.bounds.width() - - 2 * rightAEWindowRegion.region.bounds.width() - ) - ) - .isLower(2) } } diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_main_layout.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_main_layout.xml index 86c21906163f..917aec1e809d 100644 --- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_main_layout.xml +++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_main_layout.xml @@ -14,66 +14,71 @@ See the License for the specific language governing permissions and limitations under the License. --> -<LinearLayout +<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" - android:orientation="vertical" android:background="@android:color/holo_orange_light"> - <Button - android:id="@+id/launch_secondary_activity_button" - android:layout_width="wrap_content" - android:layout_height="48dp" - android:onClick="launchSecondaryActivity" - android:tag="LEFT_TO_RIGHT" - android:text="Launch Secondary Activity" /> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> - <Button - android:id="@+id/launch_secondary_activity_rtl_button" - android:layout_width="wrap_content" - android:layout_height="48dp" - android:onClick="launchSecondaryActivity" - android:tag="RIGHT_TO_LEFT" - android:text="Launch Secondary Activity in RTL" /> + <Button + android:id="@+id/launch_secondary_activity_button" + android:layout_width="wrap_content" + android:layout_height="48dp" + android:onClick="launchSecondaryActivity" + android:tag="LEFT_TO_RIGHT" + android:text="Launch Secondary Activity" /> - <Button - android:id="@+id/launch_secondary_activity_horizontally_button" - android:layout_width="wrap_content" - android:layout_height="48dp" - android:onClick="launchSecondaryActivity" - android:tag="BOTTOM_TO_TOP" - android:text="Launch Secondary Activity Horizontally" /> + <Button + android:id="@+id/launch_secondary_activity_rtl_button" + android:layout_width="wrap_content" + android:layout_height="48dp" + android:onClick="launchSecondaryActivity" + android:tag="RIGHT_TO_LEFT" + android:text="Launch Secondary Activity in RTL" /> - <Button - android:id="@+id/launch_placeholder_split_button" - android:layout_width="wrap_content" - android:layout_height="48dp" - android:onClick="launchPlaceholderSplit" - android:tag="LEFT_TO_RIGHT" - android:text="Launch Placeholder Split" /> + <Button + android:id="@+id/launch_secondary_activity_horizontally_button" + android:layout_width="wrap_content" + android:layout_height="48dp" + android:onClick="launchSecondaryActivity" + android:tag="BOTTOM_TO_TOP" + android:text="Launch Secondary Activity Horizontally" /> - <Button - android:id="@+id/launch_always_expand_activity_button" - android:layout_width="wrap_content" - android:layout_height="48dp" - android:onClick="launchAlwaysExpandActivity" - android:text="Launch Always Expand Activity" /> + <Button + android:id="@+id/launch_placeholder_split_button" + android:layout_width="wrap_content" + android:layout_height="48dp" + android:onClick="launchPlaceholderSplit" + android:tag="LEFT_TO_RIGHT" + android:text="Launch Placeholder Split" /> - <Button - android:id="@+id/launch_placeholder_split_rtl_button" - android:layout_width="wrap_content" - android:layout_height="48dp" - android:onClick="launchPlaceholderSplit" - android:tag="RIGHT_TO_LEFT" - android:text="Launch Placeholder Split in RTL" /> + <Button + android:id="@+id/launch_always_expand_activity_button" + android:layout_width="wrap_content" + android:layout_height="48dp" + android:onClick="launchAlwaysExpandActivity" + android:text="Launch Always Expand Activity" /> - <Button - android:id="@+id/launch_trampoline_button" - android:layout_width="wrap_content" - android:layout_height="48dp" - android:onClick="launchTrampolineActivity" - android:tag="LEFT_TO_RIGHT" - android:text="Launch Trampoline Activity" /> + <Button + android:id="@+id/launch_placeholder_split_rtl_button" + android:layout_width="wrap_content" + android:layout_height="48dp" + android:onClick="launchPlaceholderSplit" + android:tag="RIGHT_TO_LEFT" + android:text="Launch Placeholder Split in RTL" /> -</LinearLayout> + <Button + android:id="@+id/launch_trampoline_button" + android:layout_width="wrap_content" + android:layout_height="48dp" + android:onClick="launchTrampolineActivity" + android:tag="LEFT_TO_RIGHT" + android:text="Launch Trampoline Activity" /> + + </LinearLayout> +</ScrollView> diff --git a/tests/Input/src/android/hardware/input/KeyboardLayoutPreviewTests.kt b/tests/Input/src/android/hardware/input/KeyboardLayoutPreviewTests.kt index 3a2a3be0690d..ae32bdaf80d7 100644 --- a/tests/Input/src/android/hardware/input/KeyboardLayoutPreviewTests.kt +++ b/tests/Input/src/android/hardware/input/KeyboardLayoutPreviewTests.kt @@ -16,6 +16,8 @@ package android.hardware.input +import android.platform.test.annotations.DisableFlags +import android.platform.test.annotations.EnableFlags import android.content.ContextWrapper import android.graphics.drawable.Drawable import android.platform.test.annotations.Presubmit @@ -54,16 +56,16 @@ class KeyboardLayoutPreviewTests { } @Test + @EnableFlags(Flags.FLAG_KEYBOARD_LAYOUT_PREVIEW_FLAG) fun testKeyboardLayoutDrawable_hasCorrectDimensions() { - setFlagsRule.enableFlags(Flags.FLAG_KEYBOARD_LAYOUT_PREVIEW_FLAG) val drawable = createDrawable()!! assertEquals(WIDTH, drawable.intrinsicWidth) assertEquals(HEIGHT, drawable.intrinsicHeight) } @Test + @DisableFlags(Flags.FLAG_KEYBOARD_LAYOUT_PREVIEW_FLAG) fun testKeyboardLayoutDrawable_isNull_ifFlagOff() { - setFlagsRule.disableFlags(Flags.FLAG_KEYBOARD_LAYOUT_PREVIEW_FLAG) assertNull(createDrawable()) } }
\ No newline at end of file diff --git a/tests/Input/src/android/hardware/input/StickyModifierStateListenerTest.kt b/tests/Input/src/android/hardware/input/StickyModifierStateListenerTest.kt index e2b0c36ae694..bcd56ad0c669 100644 --- a/tests/Input/src/android/hardware/input/StickyModifierStateListenerTest.kt +++ b/tests/Input/src/android/hardware/input/StickyModifierStateListenerTest.kt @@ -21,6 +21,7 @@ import android.content.ContextWrapper import android.os.Handler import android.os.HandlerExecutor import android.os.test.TestLooper +import android.platform.test.annotations.EnableFlags import android.platform.test.annotations.Presubmit import android.platform.test.flag.junit.SetFlagsRule import android.view.KeyEvent @@ -50,6 +51,10 @@ import kotlin.test.fail */ @Presubmit @RunWith(MockitoJUnitRunner::class) +@EnableFlags( + com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_STICKY_KEYS_FLAG, + com.android.input.flags.Flags.FLAG_ENABLE_INPUT_FILTER_RUST_IMPL, +) class StickyModifierStateListenerTest { @get:Rule @@ -67,10 +72,6 @@ class StickyModifierStateListenerTest { @Before fun setUp() { - // Enable Sticky keys feature - rule.enableFlags(com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_STICKY_KEYS_FLAG) - rule.enableFlags(com.android.input.flags.Flags.FLAG_ENABLE_INPUT_FILTER_RUST_IMPL) - context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext())) inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManagerMock) inputManager = InputManager(context) diff --git a/tests/Internal/src/com/android/internal/protolog/PerfettoDataSourceTest.java b/tests/Internal/src/com/android/internal/protolog/ProtologDataSourceTest.java index 001a09a0225a..be9fb1b309f6 100644 --- a/tests/Internal/src/com/android/internal/protolog/PerfettoDataSourceTest.java +++ b/tests/Internal/src/com/android/internal/protolog/ProtologDataSourceTest.java @@ -34,7 +34,7 @@ import perfetto.protos.DataSourceConfigOuterClass; import perfetto.protos.ProtologCommon; import perfetto.protos.ProtologConfig; -public class PerfettoDataSourceTest { +public class ProtologDataSourceTest { @Before public void before() { assumeTrue(android.tracing.Flags.perfettoProtologTracing()); diff --git a/tests/UsbManagerTests/src/android/hardware/usb/DeviceFilterTest.java b/tests/UsbManagerTests/src/android/hardware/usb/DeviceFilterTest.java new file mode 100644 index 000000000000..d6f3148e64f1 --- /dev/null +++ b/tests/UsbManagerTests/src/android/hardware/usb/DeviceFilterTest.java @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.usb; + +import static com.google.common.truth.Truth.assertThat; + +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; + +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.hardware.usb.flags.Flags; + +import androidx.test.runner.AndroidJUnit4; + +import com.android.dx.mockito.inline.extended.ExtendedMockito; +import com.android.internal.util.XmlUtils; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.MockitoSession; +import org.mockito.quality.Strictness; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserFactory; +import org.xmlpull.v1.XmlSerializer; + +import java.io.StringReader; + +/** + * Unit tests for {@link android.hardware.usb.DeviceFilter}. + */ +@RunWith(AndroidJUnit4.class) +public class DeviceFilterTest { + + private static final int VID = 10; + private static final int PID = 11; + private static final int CLASS = 12; + private static final int SUBCLASS = 13; + private static final int PROTOCOL = 14; + private static final String MANUFACTURER = "Google"; + private static final String PRODUCT = "Test"; + private static final String SERIAL_NO = "4AL23"; + private static final String INTERFACE_NAME = "MTP"; + + private MockitoSession mStaticMockSession; + + @Before + public void setUp() throws Exception { + mStaticMockSession = ExtendedMockito.mockitoSession() + .mockStatic(Flags.class) + .strictness(Strictness.WARN) + .startMocking(); + + when(Flags.enableInterfaceNameDeviceFilter()).thenReturn(true); + } + + @After + public void tearDown() throws Exception { + mStaticMockSession.finishMocking(); + } + + @Test + public void testConstructorFromValues_interfaceNameIsInitialized() { + DeviceFilter deviceFilter = new DeviceFilter( + VID, PID, CLASS, SUBCLASS, PROTOCOL, MANUFACTURER, + PRODUCT, SERIAL_NO, INTERFACE_NAME + ); + + verifyDeviceFilterConfigurationExceptInterfaceName(deviceFilter); + assertThat(deviceFilter.mInterfaceName).isEqualTo(INTERFACE_NAME); + } + + @Test + public void testConstructorFromUsbDevice_interfaceNameIsNull() { + UsbDevice usbDevice = Mockito.mock(UsbDevice.class); + when(usbDevice.getVendorId()).thenReturn(VID); + when(usbDevice.getProductId()).thenReturn(PID); + when(usbDevice.getDeviceClass()).thenReturn(CLASS); + when(usbDevice.getDeviceSubclass()).thenReturn(SUBCLASS); + when(usbDevice.getDeviceProtocol()).thenReturn(PROTOCOL); + when(usbDevice.getManufacturerName()).thenReturn(MANUFACTURER); + when(usbDevice.getProductName()).thenReturn(PRODUCT); + when(usbDevice.getSerialNumber()).thenReturn(SERIAL_NO); + + DeviceFilter deviceFilter = new DeviceFilter(usbDevice); + + verifyDeviceFilterConfigurationExceptInterfaceName(deviceFilter); + assertThat(deviceFilter.mInterfaceName).isEqualTo(null); + } + + @Test + public void testConstructorFromDeviceFilter_interfaceNameIsInitialized() { + DeviceFilter originalDeviceFilter = new DeviceFilter( + VID, PID, CLASS, SUBCLASS, PROTOCOL, MANUFACTURER, + PRODUCT, SERIAL_NO, INTERFACE_NAME + ); + + DeviceFilter deviceFilter = new DeviceFilter(originalDeviceFilter); + + verifyDeviceFilterConfigurationExceptInterfaceName(deviceFilter); + assertThat(deviceFilter.mInterfaceName).isEqualTo(INTERFACE_NAME); + } + + + @Test + public void testReadFromXml_interfaceNamePresent_propertyIsInitialized() throws Exception { + DeviceFilter deviceFilter = getDeviceFilterFromXml("<usb-device interface-name=\"MTP\"/>"); + + assertThat(deviceFilter.mInterfaceName).isEqualTo("MTP"); + } + + @Test + public void testReadFromXml_interfaceNameAbsent_propertyIsNull() throws Exception { + DeviceFilter deviceFilter = getDeviceFilterFromXml("<usb-device vendor-id=\"1\" />"); + + assertThat(deviceFilter.mInterfaceName).isEqualTo(null); + } + + @Test + public void testWrite_withInterfaceName() throws Exception { + DeviceFilter deviceFilter = getDeviceFilterFromXml("<usb-device interface-name=\"MTP\"/>"); + XmlSerializer serializer = Mockito.mock(XmlSerializer.class); + + deviceFilter.write(serializer); + + verify(serializer).attribute(null, "interface-name", "MTP"); + } + + @Test + public void testWrite_withoutInterfaceName() throws Exception { + DeviceFilter deviceFilter = getDeviceFilterFromXml("<usb-device vendor-id=\"1\" />"); + XmlSerializer serializer = Mockito.mock(XmlSerializer.class); + + deviceFilter.write(serializer); + + verify(serializer, times(0)).attribute(eq(null), eq("interface-name"), any()); + } + + @Test + public void testToString() { + DeviceFilter deviceFilter = new DeviceFilter( + VID, PID, CLASS, SUBCLASS, PROTOCOL, MANUFACTURER, + PRODUCT, SERIAL_NO, INTERFACE_NAME + ); + + assertThat(deviceFilter.toString()).isEqualTo( + "DeviceFilter[mVendorId=10,mProductId=11,mClass=12,mSubclass=13,mProtocol=14," + + "mManufacturerName=Google,mProductName=Test,mSerialNumber=4AL23," + + "mInterfaceName=MTP]"); + } + + @Test + public void testMatch_interfaceNameMatches_returnTrue() throws Exception { + DeviceFilter deviceFilter = getDeviceFilterFromXml( + "<usb-device class=\"255\" subclass=\"255\" protocol=\"0\" " + + "interface-name=\"MTP\"/>"); + UsbDevice usbDevice = Mockito.mock(UsbDevice.class); + when(usbDevice.getInterfaceCount()).thenReturn(1); + when(usbDevice.getInterface(0)).thenReturn(new UsbInterface( + /* id= */ 0, + /* alternateSetting= */ 0, + /* name= */ "MTP", + /* class= */ 255, + /* subClass= */ 255, + /* protocol= */ 0)); + + assertTrue(deviceFilter.matches(usbDevice)); + } + + @Test + public void testMatch_interfaceNameMismatch_returnFalse() throws Exception { + DeviceFilter deviceFilter = getDeviceFilterFromXml( + "<usb-device class=\"255\" subclass=\"255\" protocol=\"0\" " + + "interface-name=\"MTP\"/>"); + UsbDevice usbDevice = Mockito.mock(UsbDevice.class); + when(usbDevice.getInterfaceCount()).thenReturn(1); + when(usbDevice.getInterface(0)).thenReturn(new UsbInterface( + /* id= */ 0, + /* alternateSetting= */ 0, + /* name= */ "UVC", + /* class= */ 255, + /* subClass= */ 255, + /* protocol= */ 0)); + + assertFalse(deviceFilter.matches(usbDevice)); + } + + @Test + public void testMatch_interfaceNameMismatchFlagDisabled_returnTrue() throws Exception { + when(Flags.enableInterfaceNameDeviceFilter()).thenReturn(false); + DeviceFilter deviceFilter = getDeviceFilterFromXml( + "<usb-device class=\"255\" subclass=\"255\" protocol=\"0\" " + + "interface-name=\"MTP\"/>"); + UsbDevice usbDevice = Mockito.mock(UsbDevice.class); + when(usbDevice.getInterfaceCount()).thenReturn(1); + when(usbDevice.getInterface(0)).thenReturn(new UsbInterface( + /* id= */ 0, + /* alternateSetting= */ 0, + /* name= */ "UVC", + /* class= */ 255, + /* subClass= */ 255, + /* protocol= */ 0)); + + assertTrue(deviceFilter.matches(usbDevice)); + } + + private void verifyDeviceFilterConfigurationExceptInterfaceName(DeviceFilter deviceFilter) { + assertThat(deviceFilter.mVendorId).isEqualTo(VID); + assertThat(deviceFilter.mProductId).isEqualTo(PID); + assertThat(deviceFilter.mClass).isEqualTo(CLASS); + assertThat(deviceFilter.mSubclass).isEqualTo(SUBCLASS); + assertThat(deviceFilter.mProtocol).isEqualTo(PROTOCOL); + assertThat(deviceFilter.mManufacturerName).isEqualTo(MANUFACTURER); + assertThat(deviceFilter.mProductName).isEqualTo(PRODUCT); + assertThat(deviceFilter.mSerialNumber).isEqualTo(SERIAL_NO); + } + + private DeviceFilter getDeviceFilterFromXml(String xml) throws Exception { + XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); + XmlPullParser parser = factory.newPullParser(); + parser.setInput(new StringReader(xml)); + XmlUtils.nextElement(parser); + + return DeviceFilter.read(parser); + } + +} |