Merge "phonewm: refactor Talkback shortcut logic into a controller" into main
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 3409838..117faa2 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -126,6 +126,12 @@
"exclude-annotation": "org.junit.Ignore"
}
]
+ },
+ {
+ "name": "vts_treble_vintf_framework_test"
+ },
+ {
+ "name": "vts_treble_vintf_vendor_test"
}
],
"postsubmit-ravenwood": [
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 9f56933..5ecc0b6 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -4091,7 +4091,7 @@
field @Deprecated public static final int INTENT_FILTER_VERIFICATION_SUCCESS = 1; // 0x1
field @Deprecated public static final int MASK_PERMISSION_FLAGS = 255; // 0xff
field public static final int MATCH_ANY_USER = 4194304; // 0x400000
- field @Deprecated public static final int MATCH_CLONE_PROFILE = 536870912; // 0x20000000
+ field public static final int MATCH_CLONE_PROFILE = 536870912; // 0x20000000
field @FlaggedApi("android.content.pm.fix_duplicated_flags") public static final long MATCH_CLONE_PROFILE_LONG = 17179869184L; // 0x400000000L
field public static final int MATCH_FACTORY_ONLY = 2097152; // 0x200000
field public static final int MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS = 536870912; // 0x20000000
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 39f2737..a3cd3dc 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2,6 +2,7 @@
package android {
public static final class Manifest.permission {
+ field @FlaggedApi("com.android.server.accessibility.motion_event_observing") public static final String ACCESSIBILITY_MOTION_EVENT_OBSERVING = "android.permission.ACCESSIBILITY_MOTION_EVENT_OBSERVING";
field public static final String ACCESS_NOTIFICATIONS = "android.permission.ACCESS_NOTIFICATIONS";
field public static final String ACTIVITY_EMBEDDING = "android.permission.ACTIVITY_EMBEDDING";
field public static final String ADJUST_RUNTIME_PERMISSIONS_POLICY = "android.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY";
@@ -99,6 +100,8 @@
public class AccessibilityServiceInfo implements android.os.Parcelable {
method @NonNull public android.content.ComponentName getComponentName();
+ method @FlaggedApi("android.view.accessibility.motion_event_observing") public int getObservedMotionEventSources();
+ method @FlaggedApi("android.view.accessibility.motion_event_observing") public void setObservedMotionEventSources(int);
}
}
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index 8ad6ea2..fc342fa 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -20,6 +20,7 @@
import static android.accessibilityservice.util.AccessibilityUtils.loadSafeAnimatedImage;
import static android.content.pm.PackageManager.FEATURE_FINGERPRINT;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -53,6 +54,7 @@
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.Flags;
import com.android.internal.R;
import com.android.internal.compat.IPlatformCompat;
@@ -630,7 +632,8 @@
InputDevice.SOURCE_TOUCH_NAVIGATION,
InputDevice.SOURCE_ROTARY_ENCODER,
InputDevice.SOURCE_JOYSTICK,
- InputDevice.SOURCE_SENSOR
+ InputDevice.SOURCE_SENSOR,
+ InputDevice.SOURCE_TOUCHSCREEN
})
@Retention(RetentionPolicy.SOURCE)
public @interface MotionEventSources {}
@@ -642,6 +645,8 @@
@MotionEventSources
private int mMotionEventSources = 0;
+ private int mObservedMotionEventSources = 0;
+
/**
* Creates a new instance.
*/
@@ -817,6 +822,9 @@
mInteractiveUiTimeout = other.mInteractiveUiTimeout;
flags = other.flags;
mMotionEventSources = other.mMotionEventSources;
+ if (Flags.motionEventObserving()) {
+ setObservedMotionEventSources(other.mObservedMotionEventSources);
+ }
// NOTE: Ensure that only properties that are safe to be modified by the service itself
// are included here (regardless of hidden setters, etc.).
}
@@ -1024,16 +1032,75 @@
*/
public void setMotionEventSources(@MotionEventSources int motionEventSources) {
mMotionEventSources = motionEventSources;
+ mObservedMotionEventSources = 0;
+ }
+
+ /**
+ * Sets the bit mask of {@link android.view.InputDevice} sources that the accessibility service
+ * wants to observe generic {@link android.view.MotionEvent}s from if it has already requested
+ * to listen to them using {@link #setMotionEventSources(int)}. Events from these sources will
+ * be sent to the rest of the input pipeline without being consumed by accessibility services.
+ * This service will still be able to see them.
+ *
+ * <p><strong>Note:</strong> you will need to call this function every time you call {@link
+ * #setMotionEventSources(int)}. Calling {@link #setMotionEventSources(int)} clears the list of
+ * observed motion event sources for this service.
+ *
+ * <p><strong>Note:</strong> {@link android.view.InputDevice} sources contain source class bits
+ * that complicate bitwise flag removal operations. To remove a specific source you should
+ * rebuild the entire value using bitwise OR operations on the individual source constants.
+ *
+ * <p>Including an {@link android.view.InputDevice} source that does not send {@link
+ * android.view.MotionEvent}s is effectively a no-op for that source, since you will not receive
+ * any events from that source.
+ *
+ * <p><strong>Note:</strong> Calling this function with a source that has not been listened to
+ * using {@link #setMotionEventSources(int)} will throw an exception.
+ *
+ * @see AccessibilityService#onMotionEvent
+ * @see #MotionEventSources
+ * @see #setMotionEventSources(int)
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_MOTION_EVENT_OBSERVING)
+ @TestApi
+ public void setObservedMotionEventSources(int observedMotionEventSources) {
+ // Confirm that any sources requested here have already been requested for listening.
+ if ((observedMotionEventSources & ~mMotionEventSources) != 0) {
+ String message =
+ String.format(
+ "Requested motion event sources for listening = 0x%x but requested"
+ + " motion event sources for observing = 0x%x.",
+ mMotionEventSources, observedMotionEventSources);
+ throw new IllegalArgumentException(message);
+ }
+ mObservedMotionEventSources = observedMotionEventSources;
+ }
+
+ /**
+ * Returns the bit mask of {@link android.view.InputDevice} sources that the accessibility
+ * service wants to observe generic {@link android.view.MotionEvent}s from if it has already
+ * requested to listen to them using {@link #setMotionEventSources(int)}. Events from these
+ * sources will be sent to the rest of the input pipeline without being consumed by
+ * accessibility services. This service will still be able to see them.
+ *
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_MOTION_EVENT_OBSERVING)
+ @MotionEventSources
+ @TestApi
+ public int getObservedMotionEventSources() {
+ return mObservedMotionEventSources;
}
/**
* The localized summary of the accessibility service.
- * <p>
- * <strong>Statically set from
- * {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
- * </p>
- * @return The localized summary if available, and {@code null} if a summary
- * has not been provided.
+ *
+ * <p><strong>Statically set from {@link AccessibilityService#SERVICE_META_DATA
+ * meta-data}.</strong>
+ *
+ * @return The localized summary if available, and {@code null} if a summary has not been
+ * provided.
*/
public CharSequence loadSummary(PackageManager packageManager) {
if (mSummaryResId == 0) {
@@ -1260,6 +1327,7 @@
parcel.writeString(mTileServiceName);
parcel.writeInt(mIntroResId);
parcel.writeInt(mMotionEventSources);
+ parcel.writeInt(mObservedMotionEventSources);
}
private void initFromParcel(Parcel parcel) {
@@ -1285,6 +1353,8 @@
mTileServiceName = parcel.readString();
mIntroResId = parcel.readInt();
mMotionEventSources = parcel.readInt();
+ // use the setter here because it throws an exception for invalid values.
+ setObservedMotionEventSources(parcel.readInt());
}
@Override
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index ffed405..4a9fa9e 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -6884,8 +6884,8 @@
* application package was involved.
*
* <p>If called while inside the handling of {@link #onNewIntent}, this function will
- * return the referrer that submitted that new intent to the activity. Otherwise, it
- * always returns the referrer of the original Intent.</p>
+ * return the referrer that submitted that new intent to the activity only after
+ * {@link #setIntent(Intent)} is called with the provided intent.</p>
*
* <p>Note that this is <em>not</em> a security feature -- you can not trust the
* referrer information, applications can spoof it.</p>
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index a510c77..013bcdd 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -2205,9 +2205,6 @@
private void visitUris(@NonNull Consumer<Uri> visitor) {
visitIconUri(visitor, getIcon());
- if (actionIntent != null) {
- actionIntent.visitUris(visitor);
- }
}
@Override
@@ -2901,21 +2898,6 @@
}
}
- // allPendingIntents should contain all associated intents after parcelling, but it may also
- // contain intents added by the app to extras for their own purposes. We only care about
- // checking the intents known and used by system_server, to avoid the confused deputy issue.
- List<PendingIntent> pendingIntents = Arrays.asList(contentIntent, deleteIntent,
- fullScreenIntent);
- for (PendingIntent intent : pendingIntents) {
- if (intent != null) {
- intent.visitUris(visitor);
- }
- }
-
- if (mBubbleMetadata != null) {
- mBubbleMetadata.visitUris(visitor);
- }
-
if (extras != null) {
visitIconUri(visitor, extras.getParcelable(EXTRA_LARGE_ICON_BIG, Icon.class));
visitIconUri(visitor, extras.getParcelable(EXTRA_PICTURE_ICON, Icon.class));
@@ -2987,28 +2969,15 @@
callPerson.visitUris(visitor);
}
visitIconUri(visitor, extras.getParcelable(EXTRA_VERIFICATION_ICON, Icon.class));
+ }
- // Extras for MediaStyle.
- PendingIntent deviceIntent = extras.getParcelable(EXTRA_MEDIA_REMOTE_INTENT,
- PendingIntent.class);
- if (deviceIntent != null) {
- deviceIntent.visitUris(visitor);
- }
+ if (mBubbleMetadata != null) {
+ visitIconUri(visitor, mBubbleMetadata.getIcon());
+ }
- if (extras.containsKey(WearableExtender.EXTRA_WEARABLE_EXTENSIONS)) {
- WearableExtender extender = new WearableExtender(this);
- extender.visitUris(visitor);
- }
-
- if (extras.containsKey(TvExtender.EXTRA_TV_EXTENDER)) {
- TvExtender extender = new TvExtender(this);
- extender.visitUris(visitor);
- }
-
- if (extras.containsKey(CarExtender.EXTRA_CAR_EXTENDER)) {
- CarExtender extender = new CarExtender(this);
- extender.visitUris(visitor);
- }
+ if (extras != null && extras.containsKey(WearableExtender.EXTRA_WEARABLE_EXTENSIONS)) {
+ WearableExtender extender = new WearableExtender(this);
+ extender.visitUris(visitor);
}
}
@@ -10589,16 +10558,6 @@
}
}
- private void visitUris(@NonNull Consumer<Uri> visitor) {
- visitIconUri(visitor, getIcon());
- if (mPendingIntent != null) {
- mPendingIntent.visitUris(visitor);
- }
- if (mDeleteIntent != null) {
- mDeleteIntent.visitUris(visitor);
- }
- }
-
/**
* Builder to construct a {@link BubbleMetadata} object.
*/
@@ -11797,9 +11756,6 @@
}
private void visitUris(@NonNull Consumer<Uri> visitor) {
- if (mDisplayIntent != null) {
- mDisplayIntent.visitUris(visitor);
- }
for (Action action : mActions) {
action.visitUris(visitor);
}
@@ -11952,19 +11908,12 @@
/**
* Returns the unread conversation conveyed by this notification.
- *
* @see #setUnreadConversation(UnreadConversation)
*/
public UnreadConversation getUnreadConversation() {
return mUnreadConversation;
}
- private void visitUris(@NonNull Consumer<Uri> visitor) {
- if (mUnreadConversation != null) {
- mUnreadConversation.visitUris(visitor);
- }
- }
-
/**
* A class which holds the unread messages from a conversation.
*/
@@ -12116,16 +12065,7 @@
onRead,
participants, b.getLong(KEY_TIMESTAMP));
}
-
- private void visitUris(@NonNull Consumer<Uri> visitor) {
- if (mReadPendingIntent != null) {
- mReadPendingIntent.visitUris(visitor);
- }
- if (mReplyPendingIntent != null) {
- mReplyPendingIntent.visitUris(visitor);
- }
- }
- }
+ };
/**
* Builder class for {@link CarExtender.UnreadConversation} objects.
@@ -12448,15 +12388,6 @@
public boolean isSuppressShowOverApps() {
return mSuppressShowOverApps;
}
-
- private void visitUris(@NonNull Consumer<Uri> visitor) {
- if (mContentIntent != null) {
- mContentIntent.visitUris(visitor);
- }
- if (mDeleteIntent != null) {
- mDeleteIntent.visitUris(visitor);
- }
- }
}
/**
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index 0261f0a..62209b0 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -44,8 +44,6 @@
import android.content.pm.PackageManager.ResolveInfoFlagsBits;
import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
-import android.net.Uri;
-import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
@@ -71,7 +69,6 @@
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executor;
-import java.util.function.Consumer;
/**
* A description of an Intent and target action to perform with it. Instances
@@ -1463,21 +1460,6 @@
return sb.toString();
}
- /**
- * See {@link Intent#visitUris(Consumer)}.
- *
- * @hide
- */
- public void visitUris(@NonNull Consumer<Uri> visitor) {
- if (android.app.Flags.visitRiskyUris()) {
- Intent intent = Binder.withCleanCallingIdentity(this::getIntent);
-
- if (intent != null) {
- intent.visitUris(visitor);
- }
- }
- }
-
/** @hide */
public void dumpDebug(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 90a2659..4a6349b1 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -9381,7 +9381,7 @@
@Deprecated
@SystemApi
@RequiresPermission(MANAGE_DEVICE_ADMINS)
- public boolean setActiveProfileOwner(@NonNull ComponentName admin, @Deprecated String ownerName)
+ public boolean setActiveProfileOwner(@NonNull ComponentName admin, String ownerName)
throws IllegalArgumentException {
throwIfParentInstance("setActiveProfileOwner");
if (mService != null) {
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index 6e45147..4cf9fca 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -16,6 +16,8 @@
package android.appwidget;
+import static android.appwidget.flags.Flags.remoteAdapterConversion;
+
import android.annotation.BroadcastBehavior;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -566,11 +568,9 @@
private void tryAdapterConversion(
FunctionalUtils.RemoteExceptionIgnoringConsumer<RemoteViews> action,
RemoteViews original, String failureMsg) {
- final boolean isConvertingAdapter = RemoteViews.isAdapterConversionEnabled()
+ if (remoteAdapterConversion()
&& (mHasPostedLegacyLists = mHasPostedLegacyLists
- || (original != null && original.hasLegacyLists()));
-
- if (isConvertingAdapter) {
+ || (original != null && original.hasLegacyLists()))) {
final RemoteViews viewsCopy = new RemoteViews(original);
Runnable updateWidgetWithTask = () -> {
try {
@@ -587,13 +587,12 @@
}
updateWidgetWithTask.run();
- return;
- }
-
- try {
- action.acceptOrThrow(original);
- } catch (RemoteException re) {
- throw re.rethrowFromSystemServer();
+ } else {
+ try {
+ action.acceptOrThrow(original);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
}
}
@@ -838,22 +837,20 @@
return;
}
- if (!RemoteViews.isAdapterConversionEnabled()) {
+ if (remoteAdapterConversion()) {
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ mHasPostedLegacyLists = true;
+ createUpdateExecutorIfNull().execute(() -> notifyCollectionWidgetChange(
+ appWidgetIds, viewId));
+ } else {
+ notifyCollectionWidgetChange(appWidgetIds, viewId);
+ }
+ } else {
try {
mService.notifyAppWidgetViewDataChanged(mPackageName, appWidgetIds, viewId);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
-
- return;
- }
-
- if (Looper.myLooper() == Looper.getMainLooper()) {
- mHasPostedLegacyLists = true;
- createUpdateExecutorIfNull().execute(() -> notifyCollectionWidgetChange(appWidgetIds,
- viewId));
- } else {
- notifyCollectionWidgetChange(appWidgetIds, viewId);
}
}
diff --git a/core/java/android/appwidget/flags.aconfig b/core/java/android/appwidget/flags.aconfig
index 6a735a4..c95b864 100644
--- a/core/java/android/appwidget/flags.aconfig
+++ b/core/java/android/appwidget/flags.aconfig
@@ -6,3 +6,10 @@
description: "Enable support for generated previews in AppWidgetManager"
bug: "306546610"
}
+
+flag {
+ name: "remote_adapter_conversion"
+ namespace: "app_widgets"
+ description: "Enable adapter conversion to RemoteCollectionItemsAdapter"
+ bug: "245950570"
+}
\ No newline at end of file
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index e4a03c5..d5b5f40 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -37,6 +37,7 @@
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
+import android.app.ActivityOptions;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.bluetooth.BluetoothAdapter;
@@ -709,7 +710,9 @@
IntentSender intentSender = mService
.requestNotificationAccess(component, mContext.getUserId())
.getIntentSender();
- mContext.startIntentSender(intentSender, null, 0, 0, 0);
+ mContext.startIntentSender(intentSender, null, 0, 0, 0,
+ ActivityOptions.makeBasic().setPendingIntentBackgroundActivityStartMode(
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED).toBundle());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
} catch (IntentSender.SendIntentException e) {
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 183b9b0..7af0be3 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -101,7 +101,6 @@
import java.util.Objects;
import java.util.Set;
import java.util.TimeZone;
-import java.util.function.Consumer;
/**
* An intent is an abstract description of an operation to be performed. It
@@ -8148,27 +8147,6 @@
}
}
- /**
- * Note all {@link Uri} that are referenced internally, with the expectation that Uri permission
- * grants will need to be issued to ensure the recipient of this object is able to render its
- * contents.
- * See b/281044385 for more context and examples about what happens when this isn't done
- * correctly.
- *
- * @hide
- */
- public void visitUris(@NonNull Consumer<Uri> visitor) {
- if (android.app.Flags.visitRiskyUris()) {
- visitor.accept(mData);
- if (mSelector != null) {
- mSelector.visitUris(visitor);
- }
- if (mOriginalIntent != null) {
- mOriginalIntent.visitUris(visitor);
- }
- }
- }
-
public static Intent getIntentOld(String uri) throws URISyntaxException {
Intent intent = getIntentOld(uri, 0);
intent.mLocalFlags |= LOCAL_FLAG_FROM_URI;
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index e224329..a5d16f2 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1225,12 +1225,10 @@
public static final int MATCH_DEBUG_TRIAGED_MISSING = MATCH_DIRECT_BOOT_AUTO;
/**
- * @deprecated Use {@link #MATCH_CLONE_PROFILE_LONG} instead.
+ * Use {@link #MATCH_CLONE_PROFILE_LONG} instead.
*
* @hide
*/
- @SuppressLint("UnflaggedApi") // Just adding the @Deprecated annotation
- @Deprecated
@SystemApi
public static final int MATCH_CLONE_PROFILE = 0x20000000;
@@ -6302,6 +6300,11 @@
/**
* Check whether a particular package has been granted a particular
* permission.
+ * <p>
+ * <strong>Note: </strong>This API returns the underlying permission state
+ * as-is and is mostly intended for permission managing system apps. To
+ * perform an access check for a certain app, please use the
+ * {@link Context#checkPermission} APIs instead.
*
* @param permName The name of the permission you are checking for.
* @param packageName The name of the package you are checking against.
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index 6c6b33b..57025c2 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -49,3 +49,10 @@
description: "Add support to unlock the private space using biometrics"
bug: "312184187"
}
+
+flag {
+ name: "support_autolock_for_private_space"
+ namespace: "profile_experiences"
+ description: "Add support to lock private space automatically after a time period"
+ bug: "303201022"
+}
diff --git a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
index 8196bf5..20b0932 100644
--- a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
@@ -40,7 +40,6 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemProperties;
-import android.util.FeatureFlagUtils;
import android.util.IntArray;
import android.util.Log;
import android.util.Pair;
@@ -722,6 +721,7 @@
switch(format) {
case ImageFormat.YUV_420_888:
case ImageFormat.JPEG:
+ case ImageFormat.JPEG_R:
break;
default:
throw new IllegalArgumentException("Unsupported format: " + format);
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 16ffaef..10a8022 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -1657,6 +1657,7 @@
*/
public abstract CpuScalingPolicies getCpuScalingPolicies();
+ @android.ravenwood.annotation.RavenwoodKeepWholeClass
public final static class HistoryTag {
public static final int HISTORY_TAG_POOL_OVERFLOW = -1;
@@ -1713,6 +1714,7 @@
* Optional detailed information that can go into a history step. This is typically
* generated each time the battery level changes.
*/
+ @android.ravenwood.annotation.RavenwoodKeepWholeClass
public final static class HistoryStepDetails {
// Time (in 1/100 second) spent in user space and the kernel since the last step.
public int userTime;
@@ -1797,6 +1799,7 @@
/**
* An extension to the history item describing a proc state change for a UID.
*/
+ @android.ravenwood.annotation.RavenwoodKeepWholeClass
public static final class ProcessStateChange {
public int uid;
public @BatteryConsumer.ProcessState int processState;
@@ -1850,6 +1853,7 @@
/**
* Battery history record.
*/
+ @android.ravenwood.annotation.RavenwoodKeepWholeClass
public static final class HistoryItem {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
public HistoryItem next;
diff --git a/core/java/android/os/ConditionVariable.java b/core/java/android/os/ConditionVariable.java
index a13eaa6..b5ed53b 100644
--- a/core/java/android/os/ConditionVariable.java
+++ b/core/java/android/os/ConditionVariable.java
@@ -29,6 +29,7 @@
* This class uses itself as the object to wait on, so if you wait()
* or notify() on a ConditionVariable, the results are undefined.
*/
[email protected]
public class ConditionVariable
{
private volatile boolean mCondition;
diff --git a/core/java/android/os/HidlSupport.java b/core/java/android/os/HidlSupport.java
index 7716055..91b796a 100644
--- a/core/java/android/os/HidlSupport.java
+++ b/core/java/android/os/HidlSupport.java
@@ -218,13 +218,6 @@
@SystemApi
public static native int getPidIfSharable();
- /**
- * Return true if HIDL is supported on this device and false if not.
- *
- * @hide
- */
- public static native boolean isHidlSupported();
-
/** @hide */
public HidlSupport() {}
}
diff --git a/core/java/android/os/HwBinder.java b/core/java/android/os/HwBinder.java
index bc19655..feed208 100644
--- a/core/java/android/os/HwBinder.java
+++ b/core/java/android/os/HwBinder.java
@@ -18,7 +18,6 @@
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
-import android.util.Log;
import libcore.util.NativeAllocationRegistry;
@@ -79,17 +78,6 @@
String iface,
String serviceName)
throws RemoteException, NoSuchElementException {
- if (!HidlSupport.isHidlSupported()
- && (iface.equals("[email protected]::IServiceManager")
- || iface.equals("[email protected]::IServiceManager")
- || iface.equals("[email protected]::IServiceManager"))) {
- Log.i(
- TAG,
- "Replacing Java hwservicemanager with a fake HwNoService"
- + " because HIDL is not supported on this device.");
- return new HwNoService();
- }
-
return getService(iface, serviceName, false /* retry */);
}
/**
diff --git a/core/java/android/os/HwNoService.java b/core/java/android/os/HwNoService.java
deleted file mode 100644
index 117c3ad..0000000
--- a/core/java/android/os/HwNoService.java
+++ /dev/null
@@ -1,52 +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 android.os;
-
-/**
- * A fake hwservicemanager that is used locally when HIDL isn't supported on the device.
- *
- * @hide
- */
-final class HwNoService implements IHwBinder, IHwInterface {
- /** @hide */
- @Override
- public void transact(int code, HwParcel request, HwParcel reply, int flags) {}
-
- /** @hide */
- @Override
- public IHwInterface queryLocalInterface(String descriptor) {
- return new HwNoService();
- }
-
- /** @hide */
- @Override
- public boolean linkToDeath(DeathRecipient recipient, long cookie) {
- return true;
- }
-
- /** @hide */
- @Override
- public boolean unlinkToDeath(DeathRecipient recipient) {
- return true;
- }
-
- /** @hide */
- @Override
- public IHwBinder asBinder() {
- return this;
- }
-}
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index f2930fe..8e860c3 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -465,9 +465,7 @@
private static native byte[] nativeMarshall(long nativePtr);
private static native void nativeUnmarshall(
long nativePtr, byte[] data, int offset, int length);
- @RavenwoodThrow
private static native int nativeCompareData(long thisNativePtr, long otherNativePtr);
- @RavenwoodThrow
private static native boolean nativeCompareDataInRange(
long ptrA, int offsetA, long ptrB, int offsetB, int length);
private static native void nativeAppendFrom(
diff --git a/core/java/android/os/vibrator/flags.aconfig b/core/java/android/os/vibrator/flags.aconfig
index 69d86a6..437668c 100644
--- a/core/java/android/os/vibrator/flags.aconfig
+++ b/core/java/android/os/vibrator/flags.aconfig
@@ -44,3 +44,10 @@
description: "Enables the independent keyboard vibration settings feature"
bug: "289107579"
}
+
+flag {
+ namespace: "haptics"
+ name: "adaptive_haptics_enabled"
+ description: "Enables the adaptive haptics feature"
+ bug: "305961689"
+}
diff --git a/core/java/android/service/persistentdata/OWNERS b/core/java/android/service/persistentdata/OWNERS
new file mode 100644
index 0000000..6dfb888
--- /dev/null
+++ b/core/java/android/service/persistentdata/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/pdb/OWNERS
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 5cbb42e..40382fd 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -76,13 +76,8 @@
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_OPTIMIZE_MEASURE;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
-import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
-import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
-import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL;
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
@@ -12097,11 +12092,9 @@
boolean desiredAction = motionEventAction == MotionEvent.ACTION_DOWN
|| motionEventAction == MotionEvent.ACTION_MOVE
|| motionEventAction == MotionEvent.ACTION_UP;
- boolean desiredType = windowType == TYPE_BASE_APPLICATION || windowType == TYPE_APPLICATION
- || windowType == TYPE_APPLICATION_STARTING || windowType == TYPE_DRAWN_APPLICATION
- || windowType == TYPE_NOTIFICATION_SHADE || windowType == TYPE_STATUS_BAR;
+ boolean undesiredType = windowType == TYPE_INPUT_METHOD;
// use toolkitSetFrameRate flag to gate the change
- return desiredAction && desiredType && sToolkitSetFrameRateReadOnlyFlagValue;
+ return desiredAction && !undesiredType && sToolkitSetFrameRateReadOnlyFlagValue;
}
/**
diff --git a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
index e057660..0cc19fb 100644
--- a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
+++ b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
@@ -59,6 +59,13 @@
}
flag {
+ name: "motion_event_observing"
+ namespace: "accessibility"
+ description: "Allows accessibility services to intercept but not consume motion events from specified sources."
+ bug: "297595990"
+}
+
+flag {
namespace: "accessibility"
name: "granular_scrolling"
description: "Allow the use of granular scrolling. This allows scrollable nodes to scroll by increments other than a full screen"
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index a31a610..8ad10af 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -16,6 +16,7 @@
package android.widget;
+import static android.appwidget.flags.Flags.remoteAdapterConversion;
import static android.view.inputmethod.Flags.FLAG_HOME_SCREEN_HANDWRITING_DELEGATOR;
import android.annotation.AttrRes;
@@ -36,7 +37,6 @@
import android.app.Activity;
import android.app.ActivityOptions;
import android.app.ActivityThread;
-import android.app.AppGlobals;
import android.app.Application;
import android.app.LoadedApk;
import android.app.PendingIntent;
@@ -108,7 +108,6 @@
import android.widget.CompoundButton.OnCheckedChangeListener;
import com.android.internal.R;
-import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.internal.util.Preconditions;
import com.android.internal.widget.IRemoteViewsFactory;
@@ -1015,11 +1014,6 @@
public int getActionTag() {
return SET_PENDING_INTENT_TEMPLATE_TAG;
}
-
- @Override
- public void visitUris(@NonNull Consumer<Uri> visitor) {
- mPendingIntentTemplate.visitUris(visitor);
- }
}
/**
@@ -1434,7 +1428,9 @@
@Override
public void visitUris(@NonNull Consumer<Uri> visitor) {
- mIntent.visitUris(visitor);
+ // TODO(b/281044385): Maybe visit intent URIs. This may require adding a dedicated
+ // visitUris method in the Intent class, since it can contain other intents. Otherwise,
+ // the basic thing to do here would be just visitor.accept(intent.getData()).
}
}
@@ -1514,7 +1510,7 @@
@Override
public void visitUris(@NonNull Consumer<Uri> visitor) {
- mResponse.visitUris(visitor);
+ // TODO(b/281044385): Maybe visit intent URIs in the RemoteResponse.
}
}
@@ -1563,11 +1559,6 @@
public int getActionTag() {
return SET_ON_STYLUS_HANDWRITING_RESPONSE_TAG;
}
-
- @Override
- public void visitUris(@NonNull Consumer<Uri> visitor) {
- mPendingIntent.visitUris(visitor);
- }
}
/**
@@ -1641,7 +1632,7 @@
@Override
public void visitUris(@NonNull Consumer<Uri> visitor) {
- mResponse.visitUris(visitor);
+ // TODO(b/281044385): Maybe visit intent URIs in the RemoteResponse.
}
}
@@ -2202,10 +2193,6 @@
final Icon icon = (Icon) getParameterValue(null);
if (icon != null) visitIconUri(icon, visitor);
break;
- case INTENT:
- final Intent intent = (Intent) getParameterValue(null);
- if (intent != null) intent.visitUris(visitor);
- break;
// TODO(b/281044385): Should we do anything about type BUNDLE?
}
}
@@ -4962,21 +4949,11 @@
*/
@Deprecated
public void setRemoteAdapter(@IdRes int viewId, Intent intent) {
- if (isAdapterConversionEnabled()) {
+ if (remoteAdapterConversion()) {
addAction(new SetRemoteCollectionItemListAdapterAction(viewId, intent));
- return;
+ } else {
+ addAction(new SetRemoteViewsAdapterIntent(viewId, intent));
}
- addAction(new SetRemoteViewsAdapterIntent(viewId, intent));
- }
-
- /**
- * @hide
- * @return True if the remote adapter conversion is enabled
- */
- public static boolean isAdapterConversionEnabled() {
- return AppGlobals.getIntCoreSetting(
- SystemUiDeviceConfigFlags.REMOTEVIEWS_ADAPTER_CONVERSION,
- SystemUiDeviceConfigFlags.REMOTEVIEWS_ADAPTER_CONVERSION_DEFAULT ? 1 : 0) != 0;
}
/**
@@ -6995,20 +6972,6 @@
mElementNames = parcel.createStringArrayList();
}
- /**
- * See {@link RemoteViews#visitUris(Consumer)}.
- *
- * @hide
- */
- public void visitUris(@NonNull Consumer<Uri> visitor) {
- if (mPendingIntent != null) {
- mPendingIntent.visitUris(visitor);
- }
- if (mFillIntent != null) {
- mFillIntent.visitUris(visitor);
- }
- }
-
private void handleViewInteraction(
View v,
InteractionHandler handler) {
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index 7f65c52..07beb11 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -16,7 +16,7 @@
flag {
name: "defer_display_updates"
- namespace: "window_manager"
+ namespace: "windowing_frontend"
description: "Feature flag for deferring DisplayManager updates to WindowManager if Shell transition is running"
bug: "259220649"
is_fixed_read_only: true
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index e494346..bd806bf 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -519,24 +519,6 @@
public static final String TASK_MANAGER_SHOW_FOOTER_DOT = "task_manager_show_footer_dot";
/**
- * (boolean) Whether to enable the adapter conversion in RemoteViews
- */
- public static final String REMOTEVIEWS_ADAPTER_CONVERSION =
- "CursorControlFeature__remoteviews_adapter_conversion";
-
- /**
- * The key name used in app core settings for {@link #REMOTEVIEWS_ADAPTER_CONVERSION}
- */
- public static final String KEY_REMOTEVIEWS_ADAPTER_CONVERSION =
- "systemui__remoteviews_adapter_conversion";
-
- /**
- * Default value for whether the adapter conversion is enabled or not. This is set for
- * RemoteViews and should not be a common practice.
- */
- public static final boolean REMOTEVIEWS_ADAPTER_CONVERSION_DEFAULT = false;
-
- /**
* (boolean) Whether the task manager should show a stop button if the app is allowlisted
* by the user.
*/
diff --git a/core/java/com/android/internal/os/BatteryStatsHistory.java b/core/java/com/android/internal/os/BatteryStatsHistory.java
index 7d78f29..0be9804 100644
--- a/core/java/com/android/internal/os/BatteryStatsHistory.java
+++ b/core/java/com/android/internal/os/BatteryStatsHistory.java
@@ -72,6 +72,7 @@
* All interfaces in BatteryStatsHistory should only be called by BatteryStatsImpl and protected by
* locks on BatteryStatsImpl object.
*/
[email protected]
public class BatteryStatsHistory {
private static final boolean DEBUG = false;
private static final String TAG = "BatteryStatsHistory";
@@ -259,6 +260,7 @@
* until the first change occurs.
*/
@VisibleForTesting
+ @android.ravenwood.annotation.RavenwoodKeepWholeClass
public static class TraceDelegate {
// Note: certain tests currently run as platform_app which is not allowed
// to set debug system properties. To ensure that system properties are set
@@ -391,10 +393,18 @@
public BatteryStatsHistory(int maxHistoryFiles, int maxHistoryBufferSize,
HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock,
MonotonicClock monotonicClock) {
+ this(maxHistoryFiles, maxHistoryBufferSize, stepDetailsCalculator, clock, monotonicClock,
+ new TraceDelegate());
+ }
+
+ @VisibleForTesting
+ public BatteryStatsHistory(int maxHistoryFiles, int maxHistoryBufferSize,
+ HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock,
+ MonotonicClock monotonicClock, TraceDelegate traceDelegate) {
mMaxHistoryFiles = maxHistoryFiles;
mMaxHistoryBufferSize = maxHistoryBufferSize;
mStepDetailsCalculator = stepDetailsCalculator;
- mTracer = new TraceDelegate();
+ mTracer = traceDelegate;
mClock = clock;
mMonotonicClock = monotonicClock;
@@ -2096,6 +2106,7 @@
* fewer bytes. It is a bit more expensive than just writing the long into the parcel,
* but at scale saves a lot of storage and allows recording of longer battery history.
*/
+ @android.ravenwood.annotation.RavenwoodKeepWholeClass
public static final class VarintParceler {
/**
* Writes an array of longs into Parcel using the varint format, see
diff --git a/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java b/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java
index 6bd5898..2dffe15 100644
--- a/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java
+++ b/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java
@@ -28,6 +28,7 @@
/**
* An iterator for {@link BatteryStats.HistoryItem}'s.
*/
[email protected]
public class BatteryStatsHistoryIterator implements Iterator<BatteryStats.HistoryItem>,
AutoCloseable {
private static final boolean DEBUG = false;
diff --git a/core/java/com/android/internal/os/PowerStats.java b/core/java/com/android/internal/os/PowerStats.java
index 1a7efac..56263fb 100644
--- a/core/java/com/android/internal/os/PowerStats.java
+++ b/core/java/com/android/internal/os/PowerStats.java
@@ -41,6 +41,7 @@
* Container for power stats, acquired by various PowerStatsCollector classes. See subclasses for
* details.
*/
[email protected]
public final class PowerStats {
private static final String TAG = "PowerStats";
@@ -67,6 +68,7 @@
* This descriptor is used for storing PowerStats and can also be used by power models
* to adjust the algorithm in accordance with the stats available on the device.
*/
+ @android.ravenwood.annotation.RavenwoodKeepWholeClass
public static class Descriptor {
public static final String XML_TAG_DESCRIPTOR = "descriptor";
private static final String XML_ATTR_ID = "id";
diff --git a/core/jni/android_os_HidlSupport.cpp b/core/jni/android_os_HidlSupport.cpp
index 3e51e93..e3602d8 100644
--- a/core/jni/android_os_HidlSupport.cpp
+++ b/core/jni/android_os_HidlSupport.cpp
@@ -15,7 +15,6 @@
*/
#include <hidl/HidlTransportSupport.h>
-#include <hidl/ServiceManagement.h>
#include <nativehelper/JNIHelp.h>
#include "core_jni_helpers.h"
@@ -25,13 +24,8 @@
return android::hardware::details::getPidIfSharable();
}
-static jboolean android_os_HidlSupport_isHidlSupported(JNIEnv*, jclass) {
- return android::hardware::isHidlSupported();
-}
-
static const JNINativeMethod gHidlSupportMethods[] = {
- {"getPidIfSharable", "()I", (void*)android_os_HidlSupport_getPidIfSharable},
- {"isHidlSupported", "()Z", (void*)android_os_HidlSupport_isHidlSupported},
+ {"getPidIfSharable", "()I", (void*)android_os_HidlSupport_getPidIfSharable},
};
const char* const kHidlSupportPathName = "android/os/HidlSupport";
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index dd93586..c6a241f 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4806,6 +4806,13 @@
<permission android:name="android.permission.CHANGE_ACCESSIBILITY_VOLUME"
android:protectionLevel="signature" />
+ <!-- @FlaggedApi("com.android.server.accessibility.motion_event_observing")
+ @hide
+ @TestApi
+ Allows an accessibility service to observe motion events without consuming them. -->
+ <permission android:name="android.permission.ACCESSIBILITY_MOTION_EVENT_OBSERVING"
+ android:protectionLevel="signature" />
+
<!-- @hide Allows an application to collect frame statistics -->
<permission android:name="android.permission.FRAME_STATS"
android:protectionLevel="signature" />
diff --git a/core/tests/coretests/src/android/os/BundleTest.java b/core/tests/coretests/src/android/os/BundleTest.java
index 8c231de..e7b5dff6 100644
--- a/core/tests/coretests/src/android/os/BundleTest.java
+++ b/core/tests/coretests/src/android/os/BundleTest.java
@@ -197,7 +197,6 @@
}
@Test
- @IgnoreUnderRavenwood(blockedBy = Parcel.class)
public void kindofEquals_bothParcelled_same() {
Bundle bundle1 = new Bundle();
bundle1.putString("StringKey", "S");
@@ -215,7 +214,6 @@
}
@Test
- @IgnoreUnderRavenwood(blockedBy = Parcel.class)
public void kindofEquals_bothParcelled_different() {
Bundle bundle1 = new Bundle();
bundle1.putString("StringKey", "S");
@@ -247,7 +245,6 @@
}
@Test
- @IgnoreUnderRavenwood(blockedBy = Parcel.class)
public void kindofEquals_lazyValues() {
Parcelable p1 = new CustomParcelable(13, "Tiramisu");
Parcelable p2 = new CustomParcelable(13, "Tiramisu");
@@ -281,7 +278,6 @@
}
@Test
- @IgnoreUnderRavenwood(blockedBy = Parcel.class)
public void kindofEquals_lazyValuesWithIdenticalParcels_returnsTrue() {
Parcelable p1 = new CustomParcelable(13, "Tiramisu");
Parcelable p2 = new CustomParcelable(13, "Tiramisu");
diff --git a/core/tests/coretests/src/android/os/MessageQueueTest.java b/core/tests/coretests/src/android/os/MessageQueueTest.java
index 8cd6773..851e612 100644
--- a/core/tests/coretests/src/android/os/MessageQueueTest.java
+++ b/core/tests/coretests/src/android/os/MessageQueueTest.java
@@ -16,6 +16,7 @@
package android.os;
+import android.platform.test.annotations.IgnoreUnderRavenwood;
import android.platform.test.ravenwood.RavenwoodRule;
import androidx.test.filters.MediumTest;
@@ -153,6 +154,7 @@
@Test
@MediumTest
+ @IgnoreUnderRavenwood(reason = "Flaky test, b/315872700")
public void testFieldIntegrity() throws Exception {
TestHandlerThread tester = new TestFieldIntegrityHandler() {
diff --git a/core/tests/coretests/src/android/os/ParcelTest.java b/core/tests/coretests/src/android/os/ParcelTest.java
index 5bbd221..26f6d69 100644
--- a/core/tests/coretests/src/android/os/ParcelTest.java
+++ b/core/tests/coretests/src/android/os/ParcelTest.java
@@ -132,7 +132,6 @@
}
@Test
- @IgnoreUnderRavenwood(blockedBy = Parcel.class)
public void testCompareDataInRange_whenSameData() {
Parcel pA = Parcel.obtain();
int iA = pA.dataPosition();
@@ -169,7 +168,6 @@
}
@Test
- @IgnoreUnderRavenwood(blockedBy = Parcel.class)
public void testCompareDataInRange_whenDifferentData() {
Parcel pA = Parcel.obtain();
int iA = pA.dataPosition();
@@ -186,7 +184,6 @@
}
@Test
- @IgnoreUnderRavenwood(blockedBy = Parcel.class)
public void testCompareDataInRange_whenLimitOutOfBounds_throws() {
Parcel pA = Parcel.obtain();
int iA = pA.dataPosition();
@@ -213,7 +210,6 @@
}
@Test
- @IgnoreUnderRavenwood(blockedBy = Parcel.class)
public void testCompareDataInRange_whenLengthZero() {
Parcel pA = Parcel.obtain();
int iA = pA.dataPosition();
@@ -232,7 +228,6 @@
}
@Test
- @IgnoreUnderRavenwood(blockedBy = Parcel.class)
public void testCompareDataInRange_whenNegativeLength_throws() {
Parcel pA = Parcel.obtain();
int iA = pA.dataPosition();
@@ -248,7 +243,6 @@
}
@Test
- @IgnoreUnderRavenwood(blockedBy = Parcel.class)
public void testCompareDataInRange_whenNegativeOffset_throws() {
Parcel pA = Parcel.obtain();
int iA = pA.dataPosition();
diff --git a/core/tests/coretests/src/android/util/SparseSetArrayTest.java b/core/tests/coretests/src/android/util/SparseSetArrayTest.java
index 1df1090..1c72185 100644
--- a/core/tests/coretests/src/android/util/SparseSetArrayTest.java
+++ b/core/tests/coretests/src/android/util/SparseSetArrayTest.java
@@ -37,6 +37,7 @@
public final RavenwoodRule mRavenwood = new RavenwoodRule();
@Test
+ @IgnoreUnderRavenwood(reason = "Flaky test, b/315872700")
public void testAddAll() {
final SparseSetArray<Integer> sparseSetArray = new SparseSetArray<>();
diff --git a/core/tests/coretests/src/android/widget/RemoteViewsTest.java b/core/tests/coretests/src/android/widget/RemoteViewsTest.java
index 15c9047..c8ea374 100644
--- a/core/tests/coretests/src/android/widget/RemoteViewsTest.java
+++ b/core/tests/coretests/src/android/widget/RemoteViewsTest.java
@@ -34,6 +34,9 @@
import android.appwidget.AppWidgetHostView;
import android.content.Context;
import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.AsyncTask;
@@ -834,33 +837,6 @@
}
@Test
- public void visitUris_intents() {
- RemoteViews views = new RemoteViews(mPackage, R.layout.remote_views_test);
-
- Uri fillIntentUri = Uri.parse("content://intent/fill");
- views.setOnCheckedChangeResponse(
- R.id.layout,
- RemoteViews.RemoteResponse.fromFillInIntent(new Intent("action", fillIntentUri)));
-
- Uri pendingIntentUri = Uri.parse("content://intent/pending");
- PendingIntent pendingIntent = getPendingIntentWithUri(pendingIntentUri);
- views.setOnClickResponse(
- R.id.layout,
- RemoteViews.RemoteResponse.fromPendingIntent(pendingIntent));
-
- Consumer<Uri> visitor = (Consumer<Uri>) spy(Consumer.class);
- views.visitUris(visitor);
- verify(visitor, times(1)).accept(eq(fillIntentUri));
- verify(visitor, times(1)).accept(eq(pendingIntentUri));
- }
-
- private PendingIntent getPendingIntentWithUri(Uri uri) {
- return PendingIntent.getActivity(mContext, 0,
- new Intent("action", uri),
- PendingIntent.FLAG_IMMUTABLE);
- }
-
- @Test
public void layoutInflaterFactory_nothingSet_returnsNull() {
final RemoteViews rv = new RemoteViews(mPackage, R.layout.remote_views_test);
assertNull(rv.getLayoutInflaterFactory());
diff --git a/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java b/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java
index f34b185..c9536b9 100644
--- a/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java
@@ -57,6 +57,16 @@
}
@Test
+ public void setValue() {
+ LongArrayMultiStateCounter counter = new LongArrayMultiStateCounter(2, 4);
+
+ counter.setValues(0, new long[]{1, 2, 3, 4});
+ counter.setValues(1, new long[]{5, 6, 7, 8});
+ assertCounts(counter, 0, new long[]{1, 2, 3, 4});
+ assertCounts(counter, 1, new long[]{5, 6, 7, 8});
+ }
+
+ @Test
public void setEnabled() {
LongArrayMultiStateCounter counter = new LongArrayMultiStateCounter(2, 4);
counter.setState(0, 1000);
diff --git a/core/tests/utiltests/src/android/util/TimeUtilsTest.java b/core/tests/utiltests/src/android/util/TimeUtilsTest.java
index e8246c8..ac659e1 100644
--- a/core/tests/utiltests/src/android/util/TimeUtilsTest.java
+++ b/core/tests/utiltests/src/android/util/TimeUtilsTest.java
@@ -18,8 +18,12 @@
import static org.junit.Assert.assertEquals;
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
+
import androidx.test.runner.AndroidJUnit4;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -29,6 +33,9 @@
@RunWith(AndroidJUnit4.class)
public class TimeUtilsTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
public static final long SECOND_IN_MILLIS = 1000;
public static final long MINUTE_IN_MILLIS = SECOND_IN_MILLIS * 60;
public static final long HOUR_IN_MILLIS = MINUTE_IN_MILLIS * 60;
@@ -78,6 +85,7 @@
}
@Test
+ @IgnoreUnderRavenwood(reason = "Flaky test, b/315872700")
public void testDumpTime() {
assertEquals("2023-01-01 00:00:00.000", runWithPrintWriter((pw) -> {
TimeUtils.dumpTime(pw, 1672556400000L);
@@ -91,6 +99,7 @@
}
@Test
+ @IgnoreUnderRavenwood(reason = "Flaky test, b/315872700")
public void testFormatForLogging() {
assertEquals("unknown", TimeUtils.formatForLogging(0));
assertEquals("unknown", TimeUtils.formatForLogging(-1));
@@ -99,6 +108,7 @@
}
@Test
+ @IgnoreUnderRavenwood(reason = "Flaky test, b/315872700")
public void testLogTimeOfDay() {
assertEquals("01-01 00:00:00.000", TimeUtils.logTimeOfDay(1672556400000L));
}
diff --git a/libs/WindowManager/Shell/OWNERS b/libs/WindowManager/Shell/OWNERS
index f0ed6ee..e346b51 100644
--- a/libs/WindowManager/Shell/OWNERS
+++ b/libs/WindowManager/Shell/OWNERS
@@ -1,4 +1,4 @@
[email protected]
# Give submodule owners in shell resource approval
-per-file res*/*/*.xml = [email protected], [email protected], [email protected], [email protected], [email protected]
+per-file res*/*/*.xml = [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected]
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index b1b196d..fe65fdd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -16,6 +16,7 @@
package com.android.wm.shell;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
@@ -168,6 +169,13 @@
private final Object mLock = new Object();
private StartingWindowController mStartingWindow;
+ /** Overlay surface for home root task */
+ private final SurfaceControl mHomeTaskOverlayContainer = new SurfaceControl.Builder()
+ .setName("home_task_overlay_container")
+ .setContainerLayer()
+ .setHidden(false)
+ .build();
+
/**
* In charge of showing compat UI. Can be {@code null} if the device doesn't support size
* compat or if this isn't the main {@link ShellTaskOrganizer}.
@@ -428,6 +436,14 @@
}
}
+ /**
+ * Returns a surface which can be used to attach overlays to the home root task
+ */
+ @NonNull
+ public SurfaceControl getHomeTaskOverlayContainer() {
+ return mHomeTaskOverlayContainer;
+ }
+
@Override
public void addStartingWindow(StartingWindowInfo info) {
if (mStartingWindow != null) {
@@ -485,6 +501,15 @@
if (mUnfoldAnimationController != null) {
mUnfoldAnimationController.onTaskAppeared(info.getTaskInfo(), info.getLeash());
}
+
+ if (info.getTaskInfo().getActivityType() == ACTIVITY_TYPE_HOME) {
+ ProtoLog.v(WM_SHELL_TASK_ORG, "Adding overlay to home task");
+ final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ t.setLayer(mHomeTaskOverlayContainer, Integer.MAX_VALUE);
+ t.reparent(mHomeTaskOverlayContainer, info.getLeash());
+ t.apply();
+ }
+
notifyLocusVisibilityIfNeeded(info.getTaskInfo());
notifyCompatUI(info.getTaskInfo(), listener);
mRecentTasks.ifPresent(recentTasks -> recentTasks.onTaskAdded(info.getTaskInfo()));
@@ -579,6 +604,12 @@
notifyCompatUI(taskInfo, null /* taskListener */);
// Notify the recent tasks that a task has been removed
mRecentTasks.ifPresent(recentTasks -> recentTasks.onTaskRemoved(taskInfo));
+ if (taskInfo.getActivityType() == ACTIVITY_TYPE_HOME) {
+ SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ t.reparent(mHomeTaskOverlayContainer, null);
+ t.apply();
+ ProtoLog.v(WM_SHELL_TASK_ORG, "Removing overlay surface");
+ }
if (!ENABLE_SHELL_TRANSITIONS && (appearedInfo.getLeash() != null)) {
// Preemptively clean up the leash only if shell transitions are not enabled
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java
index ef763ec..afd3b14 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java
@@ -22,6 +22,7 @@
import android.annotation.Nullable;
import android.app.TaskInfo;
import android.content.Context;
+import android.content.Intent;
import android.graphics.Rect;
import android.os.SystemClock;
import android.view.LayoutInflater;
@@ -227,9 +228,12 @@
}
private boolean getHasUserAspectRatioSettingsButton(@NonNull TaskInfo taskInfo) {
+ final Intent intent = taskInfo.baseIntent;
return taskInfo.appCompatTaskInfo.topActivityEligibleForUserAspectRatioButton
&& (taskInfo.appCompatTaskInfo.topActivityBoundsLetterboxed
|| taskInfo.appCompatTaskInfo.isUserFullscreenOverrideEnabled)
+ && Intent.ACTION_MAIN.equals(intent.getAction())
+ && intent.hasCategory(Intent.CATEGORY_LAUNCHER)
&& (!mUserAspectRatioButtonShownChecker.get() || isShowingButton());
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/OWNERS
index deb7c6d..1385f42 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/OWNERS
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/OWNERS
@@ -1,3 +1,7 @@
# WM shell sub-module desktop owners
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/OWNERS
index a3803ed..8a0eea0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/OWNERS
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/OWNERS
@@ -2,3 +2,6 @@
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/IShellTransitions.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/IShellTransitions.aidl
index 644a6a5..7f4a8f1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/IShellTransitions.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/IShellTransitions.aidl
@@ -16,6 +16,7 @@
package com.android.wm.shell.transition;
+import android.view.SurfaceControl;
import android.window.RemoteTransition;
import android.window.TransitionFilter;
@@ -42,6 +43,13 @@
*/
IBinder getShellApplyToken() = 3;
- /** Set listener that will receive callbacks about transitions involving home activity */
+ /**
+ * Set listener that will receive callbacks about transitions involving home activity.
+ */
oneway void setHomeTransitionListener(in IHomeTransitionListener listener) = 4;
+
+ /**
+ * Returns a container surface for the home root task.
+ */
+ SurfaceControl getHomeTaskOverlayContainer() = 5;
}
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 b98762d..af69b52 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
@@ -64,7 +64,6 @@
import android.window.TransitionMetrics;
import android.window.TransitionRequestInfo;
import android.window.WindowContainerTransaction;
-import android.window.WindowOrganizer;
import androidx.annotation.BinderThread;
@@ -72,6 +71,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
+import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.ExternalInterfaceBinder;
import com.android.wm.shell.common.RemoteCallable;
@@ -172,7 +172,7 @@
/** Transition to animate task to desktop. */
public static final int TRANSIT_MOVE_TO_DESKTOP = WindowManager.TRANSIT_FIRST_CUSTOM + 15;
- private final WindowOrganizer mOrganizer;
+ private final ShellTaskOrganizer mOrganizer;
private final Context mContext;
private final ShellExecutor mMainExecutor;
private final ShellExecutor mAnimExecutor;
@@ -264,7 +264,7 @@
public Transitions(@NonNull Context context,
@NonNull ShellInit shellInit,
@NonNull ShellController shellController,
- @NonNull WindowOrganizer organizer,
+ @NonNull ShellTaskOrganizer organizer,
@NonNull TransactionPool pool,
@NonNull DisplayController displayController,
@NonNull ShellExecutor mainExecutor,
@@ -280,7 +280,7 @@
@NonNull ShellInit shellInit,
@Nullable ShellCommandHandler shellCommandHandler,
@NonNull ShellController shellController,
- @NonNull WindowOrganizer organizer,
+ @NonNull ShellTaskOrganizer organizer,
@NonNull TransactionPool pool,
@NonNull DisplayController displayController,
@NonNull ShellExecutor mainExecutor,
@@ -1240,6 +1240,10 @@
}
}
+ private SurfaceControl getHomeTaskOverlayContainer() {
+ return mOrganizer.getHomeTaskOverlayContainer();
+ }
+
/**
* Interface for a callback that must be called after a TransitionHandler finishes playing an
* animation.
@@ -1470,6 +1474,17 @@
listener);
});
}
+
+ @Override
+ public SurfaceControl getHomeTaskOverlayContainer() {
+ SurfaceControl[] result = new SurfaceControl[1];
+ executeRemoteCallWithTaskPermission(mTransitions, "getHomeTaskOverlayContainer",
+ (controller) -> {
+ result[0] = controller.getHomeTaskOverlayContainer();
+ }, true /* blocking */);
+ // Return a copy as writing to parcel releases the original surface
+ return new SurfaceControl(result[0], "Transitions.HomeOverlay");
+ }
}
private class SettingsObserver extends ContentObserver {
diff --git a/libs/WindowManager/Shell/tests/OWNERS b/libs/WindowManager/Shell/tests/OWNERS
index deebad5..d718e15 100644
--- a/libs/WindowManager/Shell/tests/OWNERS
+++ b/libs/WindowManager/Shell/tests/OWNERS
@@ -9,3 +9,6 @@
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java
index 0652939..9fe2cb1 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java
@@ -16,6 +16,9 @@
package com.android.wm.shell.compatui;
+import static android.content.Intent.ACTION_MAIN;
+import static android.content.Intent.CATEGORY_LAUNCHER;
+import static android.hardware.usb.UsbManager.ACTION_USB_STATE;
import static android.view.WindowInsets.Type.navigationBars;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
@@ -33,6 +36,7 @@
import android.app.ActivityManager;
import android.app.TaskInfo;
import android.content.ComponentName;
+import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.testing.AndroidTestingRunner;
@@ -108,7 +112,7 @@
MockitoAnnotations.initMocks(this);
mExecutor = new TestShellExecutor();
mTaskInfo = createTaskInfo(/* eligibleForUserAspectRatioButton= */
- false, /* topActivityBoundsLetterboxed */ true);
+ false, /* topActivityBoundsLetterboxed */ true, ACTION_MAIN, CATEGORY_LAUNCHER);
mWindowManager = new UserAspectRatioSettingsWindowManager(mContext, mTaskInfo,
mSyncTransactionQueue, mTaskListener, new DisplayLayout(), new CompatUIHintsState(),
mOnUserAspectRatioSettingsButtonClicked, mExecutor, flags -> 0,
@@ -179,7 +183,7 @@
// No diff
clearInvocations(mWindowManager);
TaskInfo taskInfo = createTaskInfo(/* eligibleForUserAspectRatioButton= */
- true, /* topActivityBoundsLetterboxed */ true);
+ true, /* topActivityBoundsLetterboxed */ true, ACTION_MAIN, CATEGORY_LAUNCHER);
assertTrue(mWindowManager.updateCompatInfo(taskInfo, mTaskListener, /* canShow= */ true));
verify(mWindowManager, never()).updateSurfacePosition();
@@ -200,7 +204,24 @@
clearInvocations(mWindowManager);
clearInvocations(mLayout);
taskInfo = createTaskInfo(/* eligibleForUserAspectRatioButton= */
- false, /* topActivityBoundsLetterboxed */ true);
+ false, /* topActivityBoundsLetterboxed */ true, ACTION_MAIN, CATEGORY_LAUNCHER);
+ assertFalse(
+ mWindowManager.updateCompatInfo(taskInfo, newTaskListener, /* canShow= */ true));
+ verify(mWindowManager).release();
+
+ // Recreate button
+ clearInvocations(mWindowManager);
+ taskInfo = createTaskInfo(/* eligibleForUserAspectRatioButton= */
+ true, /* topActivityBoundsLetterboxed */ true, ACTION_MAIN, CATEGORY_LAUNCHER);
+ assertTrue(mWindowManager.updateCompatInfo(taskInfo, newTaskListener, /* canShow= */ true));
+
+ verify(mWindowManager).release();
+ verify(mWindowManager).createLayout(/* canShow= */ true);
+
+ // Change has no launcher category and is not main intent, dispose the component
+ clearInvocations(mWindowManager);
+ taskInfo = createTaskInfo(/* eligibleForUserAspectRatioButton= */
+ true, /* topActivityBoundsLetterboxed */ true, ACTION_USB_STATE, "");
assertFalse(
mWindowManager.updateCompatInfo(taskInfo, newTaskListener, /* canShow= */ true));
verify(mWindowManager).release();
@@ -217,7 +238,7 @@
// inflated
clearInvocations(mWindowManager);
TaskInfo taskInfo = createTaskInfo(/* eligibleForUserAspectRatioButton= */
- false, /* topActivityBoundsLetterboxed */ true);
+ false, /* topActivityBoundsLetterboxed */ true, ACTION_MAIN, CATEGORY_LAUNCHER);
mWindowManager.updateCompatInfo(taskInfo, mTaskListener, /* canShow= */ true);
verify(mWindowManager, never()).inflateLayout();
@@ -225,7 +246,7 @@
// Change topActivityInSizeCompat to true and pass canShow true, layout should be inflated.
clearInvocations(mWindowManager);
taskInfo = createTaskInfo(/* eligibleForUserAspectRatioButton= */
- true, /* topActivityBoundsLetterboxed */ true);
+ true, /* topActivityBoundsLetterboxed */ true, ACTION_MAIN, CATEGORY_LAUNCHER);
mWindowManager.updateCompatInfo(taskInfo, mTaskListener, /* canShow= */ true);
verify(mWindowManager).inflateLayout();
@@ -304,7 +325,7 @@
clearInvocations(mWindowManager);
spyOn(mWindowManager);
TaskInfo taskInfo = createTaskInfo(/* eligibleForUserAspectRatioButton= */
- true, /* topActivityBoundsLetterboxed */ true);
+ true, /* topActivityBoundsLetterboxed */ true, ACTION_MAIN, CATEGORY_LAUNCHER);
// User aspect ratio settings button has not yet been shown.
doReturn(false).when(mUserAspectRatioButtonShownChecker).get();
@@ -378,7 +399,7 @@
}
private static TaskInfo createTaskInfo(boolean eligibleForUserAspectRatioButton,
- boolean topActivityBoundsLetterboxed) {
+ boolean topActivityBoundsLetterboxed, String action, String category) {
ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
taskInfo.taskId = TASK_ID;
taskInfo.appCompatTaskInfo.topActivityEligibleForUserAspectRatioButton =
@@ -386,6 +407,7 @@
taskInfo.appCompatTaskInfo.topActivityBoundsLetterboxed = topActivityBoundsLetterboxed;
taskInfo.configuration.uiMode &= ~Configuration.UI_MODE_TYPE_DESK;
taskInfo.realActivity = new ComponentName("com.mypackage.test", "TestActivity");
+ taskInfo.baseIntent = new Intent(action).addCategory(category);
return taskInfo;
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
index 50802c3..66efa02 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
@@ -40,12 +40,12 @@
import android.view.SurfaceControl;
import android.window.TransitionInfo;
import android.window.TransitionInfo.TransitionMode;
-import android.window.WindowOrganizer;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.common.DisplayController;
@@ -68,7 +68,7 @@
@RunWith(AndroidJUnit4.class)
public class HomeTransitionObserverTest extends ShellTestCase {
- private final WindowOrganizer mOrganizer = mock(WindowOrganizer.class);
+ private final ShellTaskOrganizer mOrganizer = mock(ShellTaskOrganizer.class);
private final TransactionPool mTransactionPool = mock(TransactionPool.class);
private final Context mContext =
InstrumentationRegistry.getInstrumentation().getTargetContext();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
index 01c9bd0..e22bf3d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
@@ -87,7 +87,6 @@
import android.window.TransitionRequestInfo;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
-import android.window.WindowOrganizer;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -98,6 +97,7 @@
import com.android.internal.R;
import com.android.internal.policy.TransitionAnimation;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
+import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.common.DisplayController;
@@ -130,7 +130,7 @@
@RunWith(AndroidJUnit4.class)
public class ShellTransitionTests extends ShellTestCase {
- private final WindowOrganizer mOrganizer = mock(WindowOrganizer.class);
+ private final ShellTaskOrganizer mOrganizer = mock(ShellTaskOrganizer.class);
private final TransactionPool mTransactionPool = mock(TransactionPool.class);
private final Context mContext =
InstrumentationRegistry.getInstrumentation().getTargetContext();
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 79a7357..4741170 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -79,14 +79,6 @@
"external/skia/src/core",
],
- product_variables: {
- eng: {
- lto: {
- never: true,
- },
- },
- },
-
target: {
android: {
include_dirs: [
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index 8445032..69718a6 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -43,12 +43,15 @@
},
shared_libs: [
"libandroid_runtime",
+ "libbase",
+ "libinput",
"libinputservice",
"libhwui",
"libgui",
"libutils",
],
static_libs: [
+ "libflagtest",
"libgmock",
"libgtest",
],
diff --git a/libs/input/tests/PointerController_test.cpp b/libs/input/tests/PointerController_test.cpp
index d9efd3c..adfa91e 100644
--- a/libs/input/tests/PointerController_test.cpp
+++ b/libs/input/tests/PointerController_test.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include <com_android_input_flags.h>
+#include <flag_macros.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <input/PointerController.h>
@@ -28,6 +30,8 @@
namespace android {
+namespace input_flags = com::android::input::flags;
+
enum TestCursorType {
CURSOR_TYPE_DEFAULT = 0,
CURSOR_TYPE_HOVER,
@@ -261,7 +265,20 @@
mPointerController->reloadPointerResources();
}
-TEST_F(PointerControllerTest, updatePointerIcon) {
+TEST_F_WITH_FLAGS(PointerControllerTest, setPresentationBeforeDisplayViewportDoesNotLoadResources,
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(input_flags, enable_pointer_choreographer))) {
+ // Setting the presentation mode before a display viewport is set will not load any resources.
+ mPointerController->setPresentation(PointerController::Presentation::POINTER);
+ ASSERT_TRUE(mPolicy->noResourcesAreLoaded());
+
+ // When the display is set, then the resources are loaded.
+ ensureDisplayViewportIsSet();
+ ASSERT_TRUE(mPolicy->allResourcesAreLoaded());
+}
+
+TEST_F_WITH_FLAGS(PointerControllerTest, updatePointerIcon,
+ REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(input_flags,
+ enable_pointer_choreographer))) {
ensureDisplayViewportIsSet();
mPointerController->setPresentation(PointerController::Presentation::POINTER);
mPointerController->unfade(PointerController::Transition::IMMEDIATE);
@@ -277,6 +294,24 @@
mPointerController->updatePointerIcon(static_cast<PointerIconStyle>(type));
}
+TEST_F_WITH_FLAGS(PointerControllerTest, updatePointerIconWithChoreographer,
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(input_flags, enable_pointer_choreographer))) {
+ // When PointerChoreographer is enabled, the presentation mode is set before the viewport.
+ mPointerController->setPresentation(PointerController::Presentation::POINTER);
+ ensureDisplayViewportIsSet();
+ mPointerController->unfade(PointerController::Transition::IMMEDIATE);
+
+ int32_t type = CURSOR_TYPE_ADDITIONAL;
+ std::pair<float, float> hotspot = getHotSpotCoordinatesForType(type);
+ EXPECT_CALL(*mPointerSprite, setVisible(true));
+ EXPECT_CALL(*mPointerSprite, setAlpha(1.0f));
+ EXPECT_CALL(*mPointerSprite,
+ setIcon(AllOf(Field(&SpriteIcon::style, static_cast<PointerIconStyle>(type)),
+ Field(&SpriteIcon::hotSpotX, hotspot.first),
+ Field(&SpriteIcon::hotSpotY, hotspot.second))));
+ mPointerController->updatePointerIcon(static_cast<PointerIconStyle>(type));
+}
+
TEST_F(PointerControllerTest, setCustomPointerIcon) {
ensureDisplayViewportIsSet();
mPointerController->unfade(PointerController::Transition::IMMEDIATE);
diff --git a/media/OWNERS b/media/OWNERS
index 4a6648e..994a7b8 100644
--- a/media/OWNERS
+++ b/media/OWNERS
@@ -21,7 +21,6 @@
include platform/frameworks/av:/media/janitors/media_solutions_OWNERS
# SEO
[email protected]
# SEA/KIR/BVE
[email protected]
diff --git a/media/java/android/media/AudioHalVersionInfo.java b/media/java/android/media/AudioHalVersionInfo.java
index 0f48abeb..c881a03 100644
--- a/media/java/android/media/AudioHalVersionInfo.java
+++ b/media/java/android/media/AudioHalVersionInfo.java
@@ -80,9 +80,8 @@
* List of all valid Audio HAL versions. This list need to be in sync with sAudioHALVersions
* defined in frameworks/av/media/libaudiohal/FactoryHalHidl.cpp.
*/
- // TODO: add AIDL_1_0 with sAudioHALVersions.
public static final @NonNull List<AudioHalVersionInfo> VERSIONS =
- List.of(HIDL_7_1, HIDL_7_0, HIDL_6_0, HIDL_5_0, HIDL_4_0);
+ List.of(AIDL_1_0, HIDL_7_1, HIDL_7_0, HIDL_6_0, HIDL_5_0, HIDL_4_0);
private static final String TAG = "AudioHalVersionInfo";
private AudioHalVersion mHalVersion = new AudioHalVersion();
diff --git a/media/java/android/media/OWNERS b/media/java/android/media/OWNERS
index bbe5e06..058c5be 100644
--- a/media/java/android/media/OWNERS
+++ b/media/java/android/media/OWNERS
@@ -2,7 +2,6 @@
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
# go/android-fwk-media-solutions for info on areas of ownership.
diff --git a/media/java/android/media/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl
index a8ffd2b..901ea46 100644
--- a/media/java/android/media/tv/ITvInputManager.aidl
+++ b/media/java/android/media/tv/ITvInputManager.aidl
@@ -110,10 +110,6 @@
void pauseRecording(in IBinder sessionToken, in Bundle params, int userId);
void resumeRecording(in IBinder sessionToken, in Bundle params, int userId);
- // For playback control
- void startPlayback(in IBinder sessionToken, int userId);
- void stopPlayback(in IBinder sessionToken, int mode, int userId);
-
// For broadcast info
void requestBroadcastInfo(in IBinder sessionToken, in BroadcastInfoRequest request, int userId);
void removeBroadcastInfo(in IBinder sessionToken, int id, int userId);
diff --git a/media/java/android/media/tv/ITvInputSession.aidl b/media/java/android/media/tv/ITvInputSession.aidl
index e37ee6e..5246f5c4 100644
--- a/media/java/android/media/tv/ITvInputSession.aidl
+++ b/media/java/android/media/tv/ITvInputSession.aidl
@@ -63,9 +63,6 @@
void timeShiftSetMode(int mode);
void timeShiftEnablePositionTracking(boolean enable);
- void startPlayback();
- void stopPlayback(int mode);
-
// For the recording session
void startRecording(in Uri programUri, in Bundle params);
void stopRecording();
diff --git a/media/java/android/media/tv/ITvInputSessionWrapper.java b/media/java/android/media/tv/ITvInputSessionWrapper.java
index ae3ee65..d749b91 100644
--- a/media/java/android/media/tv/ITvInputSessionWrapper.java
+++ b/media/java/android/media/tv/ITvInputSessionWrapper.java
@@ -79,8 +79,6 @@
private static final int DO_TIME_SHIFT_SET_MODE = 30;
private static final int DO_SET_TV_MESSAGE_ENABLED = 31;
private static final int DO_NOTIFY_TV_MESSAGE = 32;
- private static final int DO_STOP_PLAYBACK = 33;
- private static final int DO_START_PLAYBACK = 34;
private final boolean mIsRecordingSession;
private final HandlerCaller mCaller;
@@ -288,14 +286,6 @@
mTvInputSessionImpl.onTvMessageReceived((Integer) args.arg1, (Bundle) args.arg2);
break;
}
- case DO_STOP_PLAYBACK: {
- mTvInputSessionImpl.stopPlayback(msg.arg1);
- break;
- }
- case DO_START_PLAYBACK: {
- mTvInputSessionImpl.startPlayback();
- break;
- }
default: {
Log.w(TAG, "Unhandled message code: " + msg.what);
break;
@@ -493,17 +483,6 @@
enabled));
}
- @Override
- public void stopPlayback(int mode) {
- mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_STOP_PLAYBACK, mode));
- }
-
- @Override
- public void startPlayback() {
- mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_START_PLAYBACK));
- }
-
-
private final class TvInputEventReceiver extends InputEventReceiver {
TvInputEventReceiver(InputChannel inputChannel, Looper looper) {
super(inputChannel, looper);
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index d4612ea..631ab9a 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -3302,30 +3302,6 @@
}
}
- void stopPlayback(int mode) {
- if (mToken == null) {
- Log.w(TAG, "The session has been already released");
- return;
- }
- try {
- mService.stopPlayback(mToken, mode, mUserId);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- void startPlayback() {
- if (mToken == null) {
- Log.w(TAG, "The session has been already released");
- return;
- }
- try {
- mService.startPlayback(mToken, mUserId);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
/**
* Sends TV messages to the service for testing purposes
*/
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 55fa517..720d9a6 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -34,7 +34,6 @@
import android.hardware.hdmi.HdmiDeviceInfo;
import android.media.AudioPresentation;
import android.media.PlaybackParams;
-import android.media.tv.interactive.TvInteractiveAppService;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
@@ -1532,32 +1531,6 @@
}
/**
- * Called when the application requests playback of the Audio, Video, and CC streams to be
- * stopped, but the metadata should continue to be filtered.
- *
- * <p>The metadata that will continue to be filtered includes the PSI
- * (Program specific information) and SI (Service Information), part of ISO/IEC 13818-1.
- *
- * <p> Note that this is different form {@link #timeShiftPause()} as should release the
- * stream, making it impossible to resume from this position again.
- * @param mode
- * @hide
- */
- public void onStopPlayback(@TvInteractiveAppService.PlaybackCommandStopMode int mode) {
- }
-
- /**
- * Starts playback of the Audio, Video, and CC streams.
- *
- * <p> Note that this is different form {@link #timeShiftResume()} as this is intended to be
- * used after stopping playback. This is used to restart playback from the current position
- * in the live broadcast.
- * @hide
- */
- public void onStartPlayback() {
- }
-
- /**
* Called when the application requests to play a given recorded TV program.
*
* @param recordedProgramUri The URI of a recorded TV program.
@@ -2020,20 +1993,6 @@
}
/**
- * Calls {@link #onStopPlayback(int)}.
- */
- void stopPlayback(int mode) {
- onStopPlayback(mode);
- }
-
- /**
- * Calls {@link #onStartPlayback()}.
- */
- void startPlayback() {
- onStartPlayback();
- }
-
- /**
* Calls {@link #onTimeShiftPlay(Uri)}.
*/
void timeShiftPlay(Uri recordedProgramUri) {
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
index 233f966..196b5c3 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -37,7 +37,6 @@
import android.media.tv.TvInputManager.Session;
import android.media.tv.TvInputManager.Session.FinishedInputEventCallback;
import android.media.tv.TvInputManager.SessionCallback;
-import android.media.tv.interactive.TvInteractiveAppService;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
@@ -644,35 +643,6 @@
}
}
- /**
- * Stops playback of the Audio, Video, and CC streams, but continue filtering the metadata.
- *
- * <p>The metadata that will continue to be filtered includes the PSI
- * (Program specific information) and SI (Service Information), part of ISO/IEC 13818-1.
- *
- * <p> Note that this is different form {@link #timeShiftPause()} as this completely drops
- * the stream, making it impossible to resume from this position again.
- * @hide
- */
- public void stopPlayback(@TvInteractiveAppService.PlaybackCommandStopMode int mode) {
- if (mSession != null) {
- mSession.stopPlayback(mode);
- }
- }
-
- /**
- * Starts playback of the Audio, Video, and CC streams.
- *
- * <p> Note that this is different form {@link #timeShiftResume()} as this is intended to be
- * used after stopping playback. This is used to restart playback from the current position
- * in the live broadcast.
- * @hide
- */
- public void startPlayback() {
- if (mSession != null) {
- mSession.startPlayback();
- }
- }
/**
* Sends TV messages to the session for testing purposes
diff --git a/packages/CompanionDeviceManager/res/values/strings.xml b/packages/CompanionDeviceManager/res/values/strings.xml
index 281eba6..6019aa8 100644
--- a/packages/CompanionDeviceManager/res/values/strings.xml
+++ b/packages/CompanionDeviceManager/res/values/strings.xml
@@ -156,7 +156,7 @@
<string name="permission_storage">Photos and media</string>
<!-- Notification permission will be granted of corresponding profile [CHAR LIMIT=30] -->
- <string name="permission_notification">Notifications</string>
+ <string name="permission_notifications">Notifications</string>
<!-- Apps permission will be granted of corresponding profile [CHAR LIMIT=30] -->
<string name="permission_app_streaming">Apps</string>
@@ -165,28 +165,31 @@
<string name="permission_nearby_device_streaming">Streaming</string>
<!-- Description of phone permission of corresponding profile [CHAR LIMIT=NONE] -->
- <string name="permission_phone_summary">Can make and manage phone calls</string>
+ <string name="permission_phone_summary">Make and manage phone calls</string>
<!-- Description of Call logs permission of corresponding profile [CHAR LIMIT=NONE] -->
- <string name="permission_call_logs_summary">Can read and write phone call log</string>
+ <string name="permission_call_logs_summary">Read and write phone call log</string>
<!-- Description of SMS permission of corresponding profile [CHAR LIMIT=NONE] -->
- <string name="permission_sms_summary">Can send and view SMS messages</string>
+ <string name="permission_sms_summary">Send and view SMS messages</string>
<!-- Description of contacts permission of corresponding profile [CHAR LIMIT=NONE] -->
- <string name="permission_contacts_summary">Can access your contacts</string>
+ <string name="permission_contacts_summary">Access your contacts</string>
<!-- Description of calendar permission of corresponding profile [CHAR LIMIT=NONE] -->
- <string name="permission_calendar_summary">Can access your calendar</string>
+ <string name="permission_calendar_summary">Access your calendar</string>
<!-- Description of microphone permission of corresponding profile [CHAR LIMIT=NONE] -->
- <string name="permission_microphone_summary">Can record audio</string>
+ <string name="permission_microphone_summary">Record audio</string>
<!-- Description of nearby devices' permission of corresponding profile [CHAR LIMIT=NONE] -->
- <string name="permission_nearby_devices_summary">Can find, connect to, and determine the relative position of nearby devices</string>
+ <string name="permission_nearby_devices_summary">Find, connect to, and determine the relative position of nearby devices</string>
- <!-- Description of notification permission of corresponding profile [CHAR LIMIT=NONE] -->
- <string name="permission_notification_summary">Can read all notifications, including information like contacts, messages, and photos</string>
+ <!-- Description of NLA (notification listener access) of corresponding profile [CHAR LIMIT=NONE] -->
+ <string name="permission_notification_listener_access_summary">Read all notifications, including information like contacts, messages, and photos</string>
+
+ <!-- Description of NLA & POST_NOTIFICATION of corresponding profile [CHAR LIMIT=NONE] -->
+ <string name="permission_notifications_summary">\u2022 Read all notifications, including info like contacts, messages, and photos<br/>\u2022 Send notifications<br/><br/>You can manage this app\'s ability to read and send notifications anytime in Settings > Notifications.</string>
<!-- Description of app streaming permission of corresponding profile [CHAR LIMIT=NONE] -->
<string name="permission_app_streaming_summary">Stream your phone\u2019s apps</string>
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
index 97016f5..0abf285 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
@@ -27,13 +27,13 @@
import static com.android.companiondevicemanager.CompanionDeviceDiscoveryService.DiscoveryState;
import static com.android.companiondevicemanager.CompanionDeviceDiscoveryService.DiscoveryState.FINISHED_TIMEOUT;
-import static com.android.companiondevicemanager.CompanionDeviceResources.PERMISSION_TYPES;
-import static com.android.companiondevicemanager.CompanionDeviceResources.PROFILES_NAME;
-import static com.android.companiondevicemanager.CompanionDeviceResources.PROFILE_ICON;
-import static com.android.companiondevicemanager.CompanionDeviceResources.SUMMARIES;
+import static com.android.companiondevicemanager.CompanionDeviceResources.PROFILE_PERMISSIONS;
+import static com.android.companiondevicemanager.CompanionDeviceResources.PROFILE_NAMES;
+import static com.android.companiondevicemanager.CompanionDeviceResources.PROFILE_ICONS;
+import static com.android.companiondevicemanager.CompanionDeviceResources.PROFILE_SUMMARIES;
import static com.android.companiondevicemanager.CompanionDeviceResources.SUPPORTED_PROFILES;
import static com.android.companiondevicemanager.CompanionDeviceResources.SUPPORTED_SELF_MANAGED_PROFILES;
-import static com.android.companiondevicemanager.CompanionDeviceResources.TITLES;
+import static com.android.companiondevicemanager.CompanionDeviceResources.PROFILE_TITLES;
import static com.android.companiondevicemanager.Utils.getApplicationLabel;
import static com.android.companiondevicemanager.Utils.getHtmlFromResources;
import static com.android.companiondevicemanager.Utils.getIcon;
@@ -482,7 +482,7 @@
return;
}
- title = getHtmlFromResources(this, TITLES.get(deviceProfile), deviceName);
+ title = getHtmlFromResources(this, PROFILE_TITLES.get(deviceProfile), deviceName);
setupPermissionList(deviceProfile);
// Summary is not needed for selfManaged dialog.
@@ -525,7 +525,7 @@
mSelectedDevice = requireNonNull(deviceFilterPairs.get(0));
- final Drawable profileIcon = getIcon(this, PROFILE_ICON.get(deviceProfile));
+ final Drawable profileIcon = getIcon(this, PROFILE_ICONS.get(deviceProfile));
updatePermissionUi();
@@ -545,14 +545,14 @@
throw new RuntimeException("Unsupported profile " + deviceProfile);
}
- profileIcon = getIcon(this, PROFILE_ICON.get(deviceProfile));
+ profileIcon = getIcon(this, PROFILE_ICONS.get(deviceProfile));
if (deviceProfile == null) {
title = getHtmlFromResources(this, R.string.chooser_title_non_profile, appLabel);
mButtonNotAllowMultipleDevices.setText(R.string.consent_no);
} else {
title = getHtmlFromResources(this,
- R.string.chooser_title, getString(PROFILES_NAME.get(deviceProfile)));
+ R.string.chooser_title, getString(PROFILE_NAMES.get(deviceProfile)));
}
mDeviceAdapter = new DeviceListAdapter(this, this::onDeviceClicked);
@@ -609,10 +609,10 @@
private void updatePermissionUi() {
final String deviceProfile = mRequest.getDeviceProfile();
- final int summaryResourceId = SUMMARIES.get(deviceProfile);
+ final int summaryResourceId = PROFILE_SUMMARIES.get(deviceProfile);
final String remoteDeviceName = mSelectedDevice.getDisplayName();
final Spanned title = getHtmlFromResources(
- this, TITLES.get(deviceProfile), mAppLabel, remoteDeviceName);
+ this, PROFILE_TITLES.get(deviceProfile), mAppLabel, remoteDeviceName);
final Spanned summary;
// No need to show permission consent dialog if it is a isSkipPrompt(true)
@@ -680,7 +680,8 @@
// and when mPermissionListRecyclerView is fully populated.
// Lastly, disable the Allow and Don't allow buttons.
private void setupPermissionList(String deviceProfile) {
- final List<Integer> permissionTypes = new ArrayList<>(PERMISSION_TYPES.get(deviceProfile));
+ final List<Integer> permissionTypes = new ArrayList<>(
+ PROFILE_PERMISSIONS.get(deviceProfile));
mPermissionListAdapter.setPermissionType(permissionTypes);
mPermissionListRecyclerView.setAdapter(mPermissionListAdapter);
mPermissionListRecyclerView.setLayoutManager(mPermissionsLayoutManager);
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceResources.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceResources.java
index 551e975..23a11d6 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceResources.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceResources.java
@@ -22,28 +22,15 @@
import static android.companion.AssociationRequest.DEVICE_PROFILE_GLASSES;
import static android.companion.AssociationRequest.DEVICE_PROFILE_NEARBY_DEVICE_STREAMING;
import static android.companion.AssociationRequest.DEVICE_PROFILE_WATCH;
-
-import static com.android.companiondevicemanager.PermissionListAdapter.PERMISSION_APP_STREAMING;
-import static com.android.companiondevicemanager.PermissionListAdapter.PERMISSION_CALENDAR;
-import static com.android.companiondevicemanager.PermissionListAdapter.PERMISSION_CALL_LOGS;
-import static com.android.companiondevicemanager.PermissionListAdapter.PERMISSION_CHANGE_MEDIA_OUTPUT;
-import static com.android.companiondevicemanager.PermissionListAdapter.PERMISSION_CONTACTS;
-import static com.android.companiondevicemanager.PermissionListAdapter.PERMISSION_MICROPHONE;
-import static com.android.companiondevicemanager.PermissionListAdapter.PERMISSION_NEARBY_DEVICES;
-import static com.android.companiondevicemanager.PermissionListAdapter.PERMISSION_NEARBY_DEVICE_STREAMING;
-import static com.android.companiondevicemanager.PermissionListAdapter.PERMISSION_NOTIFICATION;
-import static com.android.companiondevicemanager.PermissionListAdapter.PERMISSION_PHONE;
-import static com.android.companiondevicemanager.PermissionListAdapter.PERMISSION_SMS;
-import static com.android.companiondevicemanager.PermissionListAdapter.PERMISSION_STORAGE;
+import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
import static java.util.Collections.unmodifiableMap;
import static java.util.Collections.unmodifiableSet;
+import android.os.Build;
import android.util.ArrayMap;
import android.util.ArraySet;
-import com.android.media.flags.Flags;
-
import java.util.Arrays;
import java.util.List;
import java.util.Map;
@@ -54,7 +41,85 @@
* for the corresponding profile.
*/
final class CompanionDeviceResources {
- static final Map<String, Integer> TITLES;
+
+ // Permission resources
+ private static final int PERMISSION_NOTIFICATION_LISTENER_ACCESS = 0;
+ private static final int PERMISSION_STORAGE = 1;
+ private static final int PERMISSION_APP_STREAMING = 2;
+ private static final int PERMISSION_PHONE = 3;
+ private static final int PERMISSION_SMS = 4;
+ private static final int PERMISSION_CONTACTS = 5;
+ private static final int PERMISSION_CALENDAR = 6;
+ private static final int PERMISSION_NEARBY_DEVICES = 7;
+ private static final int PERMISSION_NEARBY_DEVICE_STREAMING = 8;
+ private static final int PERMISSION_MICROPHONE = 9;
+ private static final int PERMISSION_CALL_LOGS = 10;
+ // Notification Listener Access & POST_NOTIFICATION permission
+ private static final int PERMISSION_NOTIFICATIONS = 11;
+ private static final int PERMISSION_CHANGE_MEDIA_OUTPUT = 12;
+
+ static final Map<Integer, Integer> PERMISSION_TITLES;
+ static {
+ final Map<Integer, Integer> map = new ArrayMap<>();
+ map.put(PERMISSION_NOTIFICATION_LISTENER_ACCESS, R.string.permission_notifications);
+ map.put(PERMISSION_STORAGE, R.string.permission_storage);
+ map.put(PERMISSION_APP_STREAMING, R.string.permission_app_streaming);
+ map.put(PERMISSION_PHONE, R.string.permission_phone);
+ map.put(PERMISSION_SMS, R.string.permission_sms);
+ map.put(PERMISSION_CONTACTS, R.string.permission_contacts);
+ map.put(PERMISSION_CALENDAR, R.string.permission_calendar);
+ map.put(PERMISSION_NEARBY_DEVICES, R.string.permission_nearby_devices);
+ map.put(PERMISSION_NEARBY_DEVICE_STREAMING, R.string.permission_nearby_device_streaming);
+ map.put(PERMISSION_MICROPHONE, R.string.permission_microphone);
+ map.put(PERMISSION_CALL_LOGS, R.string.permission_call_logs);
+ map.put(PERMISSION_NOTIFICATIONS, R.string.permission_notifications);
+ map.put(PERMISSION_CHANGE_MEDIA_OUTPUT, R.string.permission_media_routing_control);
+ PERMISSION_TITLES = unmodifiableMap(map);
+ }
+
+ static final Map<Integer, Integer> PERMISSION_SUMMARIES;
+ static {
+ final Map<Integer, Integer> map = new ArrayMap<>();
+ map.put(PERMISSION_NOTIFICATION_LISTENER_ACCESS,
+ R.string.permission_notification_listener_access_summary);
+ map.put(PERMISSION_STORAGE, R.string.permission_storage_summary);
+ map.put(PERMISSION_APP_STREAMING, R.string.permission_app_streaming_summary);
+ map.put(PERMISSION_PHONE, R.string.permission_phone_summary);
+ map.put(PERMISSION_SMS, R.string.permission_sms_summary);
+ map.put(PERMISSION_CONTACTS, R.string.permission_contacts_summary);
+ map.put(PERMISSION_CALENDAR, R.string.permission_calendar_summary);
+ map.put(PERMISSION_NEARBY_DEVICES, R.string.permission_nearby_devices_summary);
+ map.put(PERMISSION_NEARBY_DEVICE_STREAMING,
+ R.string.permission_nearby_device_streaming_summary);
+ map.put(PERMISSION_MICROPHONE, R.string.permission_microphone_summary);
+ map.put(PERMISSION_CALL_LOGS, R.string.permission_call_logs_summary);
+ map.put(PERMISSION_NOTIFICATIONS, R.string.permission_notifications_summary);
+ map.put(PERMISSION_CHANGE_MEDIA_OUTPUT, R.string.permission_media_routing_control_summary);
+ PERMISSION_SUMMARIES = unmodifiableMap(map);
+ }
+
+ static final Map<Integer, Integer> PERMISSION_ICONS;
+ static {
+ final Map<Integer, Integer> map = new ArrayMap<>();
+ map.put(PERMISSION_NOTIFICATION_LISTENER_ACCESS, R.drawable.ic_permission_notifications);
+ map.put(PERMISSION_STORAGE, R.drawable.ic_permission_storage);
+ map.put(PERMISSION_APP_STREAMING, R.drawable.ic_permission_app_streaming);
+ map.put(PERMISSION_PHONE, R.drawable.ic_permission_phone);
+ map.put(PERMISSION_SMS, R.drawable.ic_permission_sms);
+ map.put(PERMISSION_CONTACTS, R.drawable.ic_permission_contacts);
+ map.put(PERMISSION_CALENDAR, R.drawable.ic_permission_calendar);
+ map.put(PERMISSION_NEARBY_DEVICES, R.drawable.ic_permission_nearby_devices);
+ map.put(PERMISSION_NEARBY_DEVICE_STREAMING,
+ R.drawable.ic_permission_nearby_device_streaming);
+ map.put(PERMISSION_MICROPHONE, R.drawable.ic_permission_microphone);
+ map.put(PERMISSION_CALL_LOGS, R.drawable.ic_permission_call_logs);
+ map.put(PERMISSION_NOTIFICATIONS, R.drawable.ic_permission_notifications);
+ map.put(PERMISSION_CHANGE_MEDIA_OUTPUT, R.drawable.ic_permission_media_routing_control);
+ PERMISSION_ICONS = unmodifiableMap(map);
+ }
+
+ // Profile resources
+ static final Map<String, Integer> PROFILE_TITLES;
static {
final Map<String, Integer> map = new ArrayMap<>();
map.put(DEVICE_PROFILE_APP_STREAMING, R.string.title_app_streaming);
@@ -65,71 +130,61 @@
map.put(DEVICE_PROFILE_GLASSES, R.string.confirmation_title_glasses);
map.put(null, R.string.confirmation_title);
- TITLES = unmodifiableMap(map);
+ PROFILE_TITLES = unmodifiableMap(map);
}
- static final Map<String, List<Integer>> PERMISSION_TYPES;
- static {
- final Map<String, List<Integer>> map = new ArrayMap<>();
- map.put(DEVICE_PROFILE_APP_STREAMING, Arrays.asList(PERMISSION_APP_STREAMING));
- map.put(DEVICE_PROFILE_COMPUTER, Arrays.asList(
- PERMISSION_NOTIFICATION, PERMISSION_STORAGE));
- map.put(DEVICE_PROFILE_NEARBY_DEVICE_STREAMING,
- Arrays.asList(PERMISSION_NEARBY_DEVICE_STREAMING));
- if (!Flags.enablePrivilegedRoutingForMediaRoutingControl()) {
- map.put(DEVICE_PROFILE_WATCH, Arrays.asList(PERMISSION_NOTIFICATION, PERMISSION_PHONE,
- PERMISSION_CALL_LOGS, PERMISSION_SMS, PERMISSION_CONTACTS, PERMISSION_CALENDAR,
- PERMISSION_NEARBY_DEVICES));
- } else {
- map.put(DEVICE_PROFILE_WATCH, Arrays.asList(PERMISSION_NOTIFICATION, PERMISSION_PHONE,
- PERMISSION_CALL_LOGS, PERMISSION_SMS, PERMISSION_CONTACTS, PERMISSION_CALENDAR,
- PERMISSION_NEARBY_DEVICES, PERMISSION_CHANGE_MEDIA_OUTPUT));
- }
- map.put(DEVICE_PROFILE_GLASSES, Arrays.asList(PERMISSION_NOTIFICATION, PERMISSION_PHONE,
- PERMISSION_SMS, PERMISSION_CONTACTS, PERMISSION_MICROPHONE,
- PERMISSION_NEARBY_DEVICES));
-
- PERMISSION_TYPES = unmodifiableMap(map);
- }
-
- static final Map<String, Integer> SUMMARIES;
+ static final Map<String, Integer> PROFILE_SUMMARIES;
static {
final Map<String, Integer> map = new ArrayMap<>();
map.put(DEVICE_PROFILE_WATCH, R.string.summary_watch);
map.put(DEVICE_PROFILE_GLASSES, R.string.summary_glasses);
map.put(null, R.string.summary_generic);
- SUMMARIES = unmodifiableMap(map);
+ PROFILE_SUMMARIES = unmodifiableMap(map);
}
- static final Map<String, Integer> PROFILES_NAME;
+ static final Map<String, List<Integer>> PROFILE_PERMISSIONS;
+ static {
+ final Map<String, List<Integer>> map = new ArrayMap<>();
+ map.put(DEVICE_PROFILE_APP_STREAMING, Arrays.asList(PERMISSION_APP_STREAMING));
+ map.put(DEVICE_PROFILE_COMPUTER, Arrays.asList(
+ PERMISSION_NOTIFICATION_LISTENER_ACCESS, PERMISSION_STORAGE));
+ map.put(DEVICE_PROFILE_NEARBY_DEVICE_STREAMING,
+ Arrays.asList(PERMISSION_NEARBY_DEVICE_STREAMING));
+ if (Build.VERSION.SDK_INT > UPSIDE_DOWN_CAKE) {
+ map.put(DEVICE_PROFILE_WATCH, Arrays.asList(PERMISSION_NOTIFICATIONS, PERMISSION_PHONE,
+ PERMISSION_CALL_LOGS, PERMISSION_SMS, PERMISSION_CONTACTS, PERMISSION_CALENDAR,
+ PERMISSION_NEARBY_DEVICES, PERMISSION_CHANGE_MEDIA_OUTPUT));
+ } else {
+ map.put(DEVICE_PROFILE_WATCH, Arrays.asList(PERMISSION_NOTIFICATION_LISTENER_ACCESS,
+ PERMISSION_PHONE, PERMISSION_CALL_LOGS, PERMISSION_SMS, PERMISSION_CONTACTS,
+ PERMISSION_CALENDAR, PERMISSION_NEARBY_DEVICES));
+ }
+ map.put(DEVICE_PROFILE_GLASSES, Arrays.asList(PERMISSION_NOTIFICATION_LISTENER_ACCESS,
+ PERMISSION_PHONE, PERMISSION_SMS, PERMISSION_CONTACTS, PERMISSION_MICROPHONE,
+ PERMISSION_NEARBY_DEVICES));
+
+ PROFILE_PERMISSIONS = unmodifiableMap(map);
+ }
+
+ static final Map<String, Integer> PROFILE_NAMES;
static {
final Map<String, Integer> map = new ArrayMap<>();
map.put(DEVICE_PROFILE_WATCH, R.string.profile_name_watch);
map.put(DEVICE_PROFILE_GLASSES, R.string.profile_name_glasses);
map.put(null, R.string.profile_name_generic);
- PROFILES_NAME = unmodifiableMap(map);
+ PROFILE_NAMES = unmodifiableMap(map);
}
- static final Map<String, Integer> PROFILES_NAME_MULTI;
- static {
- final Map<String, Integer> map = new ArrayMap<>();
- map.put(DEVICE_PROFILE_GLASSES, R.string.profile_name_generic);
- map.put(DEVICE_PROFILE_WATCH, R.string.profile_name_watch);
- map.put(null, R.string.profile_name_generic);
-
- PROFILES_NAME_MULTI = unmodifiableMap(map);
- }
-
- static final Map<String, Integer> PROFILE_ICON;
+ static final Map<String, Integer> PROFILE_ICONS;
static {
final Map<String, Integer> map = new ArrayMap<>();
map.put(DEVICE_PROFILE_WATCH, R.drawable.ic_watch);
map.put(DEVICE_PROFILE_GLASSES, R.drawable.ic_glasses);
map.put(null, R.drawable.ic_device_other);
- PROFILE_ICON = unmodifiableMap(map);
+ PROFILE_ICONS = unmodifiableMap(map);
}
static final Set<String> SUPPORTED_PROFILES;
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/PermissionListAdapter.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/PermissionListAdapter.java
index e21aee3..4a1f801 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/PermissionListAdapter.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/PermissionListAdapter.java
@@ -16,14 +16,14 @@
package com.android.companiondevicemanager;
+import static com.android.companiondevicemanager.CompanionDeviceResources.PERMISSION_ICONS;
+import static com.android.companiondevicemanager.CompanionDeviceResources.PERMISSION_SUMMARIES;
+import static com.android.companiondevicemanager.CompanionDeviceResources.PERMISSION_TITLES;
import static com.android.companiondevicemanager.Utils.getHtmlFromResources;
import static com.android.companiondevicemanager.Utils.getIcon;
-import static java.util.Collections.unmodifiableMap;
-
import android.content.Context;
import android.text.Spanned;
-import android.util.ArrayMap;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -35,7 +35,6 @@
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
-import java.util.Map;
class PermissionListAdapter extends RecyclerView.Adapter<PermissionListAdapter.ViewHolder> {
private final Context mContext;
@@ -43,75 +42,6 @@
// Add the expand buttons if permissions are more than PERMISSION_SIZE in the permission list.
private static final int PERMISSION_SIZE = 2;
- static final int PERMISSION_NOTIFICATION = 0;
- static final int PERMISSION_STORAGE = 1;
- static final int PERMISSION_APP_STREAMING = 2;
- static final int PERMISSION_PHONE = 3;
- static final int PERMISSION_SMS = 4;
- static final int PERMISSION_CONTACTS = 5;
- static final int PERMISSION_CALENDAR = 6;
- static final int PERMISSION_NEARBY_DEVICES = 7;
- static final int PERMISSION_NEARBY_DEVICE_STREAMING = 8;
- static final int PERMISSION_MICROPHONE = 9;
- static final int PERMISSION_CALL_LOGS = 10;
- static final int PERMISSION_CHANGE_MEDIA_OUTPUT = 11;
-
- private static final Map<Integer, Integer> sTitleMap;
- static {
- final Map<Integer, Integer> map = new ArrayMap<>();
- map.put(PERMISSION_NOTIFICATION, R.string.permission_notification);
- map.put(PERMISSION_STORAGE, R.string.permission_storage);
- map.put(PERMISSION_APP_STREAMING, R.string.permission_app_streaming);
- map.put(PERMISSION_PHONE, R.string.permission_phone);
- map.put(PERMISSION_SMS, R.string.permission_sms);
- map.put(PERMISSION_CONTACTS, R.string.permission_contacts);
- map.put(PERMISSION_CALENDAR, R.string.permission_calendar);
- map.put(PERMISSION_NEARBY_DEVICES, R.string.permission_nearby_devices);
- map.put(PERMISSION_NEARBY_DEVICE_STREAMING, R.string.permission_nearby_device_streaming);
- map.put(PERMISSION_MICROPHONE, R.string.permission_microphone);
- map.put(PERMISSION_CALL_LOGS, R.string.permission_call_logs);
- map.put(PERMISSION_CHANGE_MEDIA_OUTPUT, R.string.permission_media_routing_control);
- sTitleMap = unmodifiableMap(map);
- }
-
- private static final Map<Integer, Integer> sSummaryMap;
- static {
- final Map<Integer, Integer> map = new ArrayMap<>();
- map.put(PERMISSION_NOTIFICATION, R.string.permission_notification_summary);
- map.put(PERMISSION_STORAGE, R.string.permission_storage_summary);
- map.put(PERMISSION_APP_STREAMING, R.string.permission_app_streaming_summary);
- map.put(PERMISSION_PHONE, R.string.permission_phone_summary);
- map.put(PERMISSION_SMS, R.string.permission_sms_summary);
- map.put(PERMISSION_CONTACTS, R.string.permission_contacts_summary);
- map.put(PERMISSION_CALENDAR, R.string.permission_calendar_summary);
- map.put(PERMISSION_NEARBY_DEVICES, R.string.permission_nearby_devices_summary);
- map.put(PERMISSION_NEARBY_DEVICE_STREAMING,
- R.string.permission_nearby_device_streaming_summary);
- map.put(PERMISSION_MICROPHONE, R.string.permission_microphone_summary);
- map.put(PERMISSION_CALL_LOGS, R.string.permission_call_logs_summary);
- map.put(PERMISSION_CHANGE_MEDIA_OUTPUT, R.string.permission_media_routing_control_summary);
- sSummaryMap = unmodifiableMap(map);
- }
-
- private static final Map<Integer, Integer> sIconMap;
- static {
- final Map<Integer, Integer> map = new ArrayMap<>();
- map.put(PERMISSION_NOTIFICATION, R.drawable.ic_permission_notifications);
- map.put(PERMISSION_STORAGE, R.drawable.ic_permission_storage);
- map.put(PERMISSION_APP_STREAMING, R.drawable.ic_permission_app_streaming);
- map.put(PERMISSION_PHONE, R.drawable.ic_permission_phone);
- map.put(PERMISSION_SMS, R.drawable.ic_permission_sms);
- map.put(PERMISSION_CONTACTS, R.drawable.ic_permission_contacts);
- map.put(PERMISSION_CALENDAR, R.drawable.ic_permission_calendar);
- map.put(PERMISSION_NEARBY_DEVICES, R.drawable.ic_permission_nearby_devices);
- map.put(PERMISSION_NEARBY_DEVICE_STREAMING,
- R.drawable.ic_permission_nearby_device_streaming);
- map.put(PERMISSION_MICROPHONE, R.drawable.ic_permission_microphone);
- map.put(PERMISSION_CALL_LOGS, R.drawable.ic_permission_call_logs);
- map.put(PERMISSION_CHANGE_MEDIA_OUTPUT, R.drawable.ic_permission_media_routing_control);
- sIconMap = unmodifiableMap(map);
- }
-
PermissionListAdapter(Context context) {
mContext = context;
}
@@ -121,7 +51,8 @@
View view = LayoutInflater.from(parent.getContext()).inflate(
R.layout.list_item_permission, parent, false);
ViewHolder viewHolder = new ViewHolder(view);
- viewHolder.mPermissionIcon.setImageDrawable(getIcon(mContext, sIconMap.get(viewType)));
+ viewHolder.mPermissionIcon.setImageDrawable(
+ getIcon(mContext, PERMISSION_ICONS.get(viewType)));
if (viewHolder.mExpandButton.getTag() == null) {
viewHolder.mExpandButton.setTag(R.drawable.btn_expand_more);
@@ -165,8 +96,8 @@
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
int type = getItemViewType(position);
- final Spanned title = getHtmlFromResources(mContext, sTitleMap.get(type));
- final Spanned summary = getHtmlFromResources(mContext, sSummaryMap.get(type));
+ final Spanned title = getHtmlFromResources(mContext, PERMISSION_TITLES.get(type));
+ final Spanned summary = getHtmlFromResources(mContext, PERMISSION_SUMMARIES.get(type));
holder.mPermissionSummary.setText(summary);
holder.mPermissionName.setText(title);
@@ -192,6 +123,7 @@
private final TextView mPermissionSummary;
private final ImageView mPermissionIcon;
private final ImageButton mExpandButton;
+
ViewHolder(View itemView) {
super(itemView);
mPermissionName = itemView.findViewById(R.id.permission_name);
@@ -203,7 +135,7 @@
private void setAccessibility(View view, int viewType, int action, int statusResourceId,
int actionResourceId) {
- final String permission = mContext.getString(sTitleMap.get(viewType));
+ final String permission = mContext.getString(PERMISSION_TITLES.get(viewType));
if (actionResourceId != 0) {
view.announceForAccessibility(
diff --git a/packages/PackageInstaller/res/values/strings.xml b/packages/PackageInstaller/res/values/strings.xml
index e3b93ba..f4641b9 100644
--- a/packages/PackageInstaller/res/values/strings.xml
+++ b/packages/PackageInstaller/res/values/strings.xml
@@ -202,11 +202,6 @@
<!-- Dialog attributes to indicate parse errors -->
<string name="Parse_error_dlg_text">There was a problem parsing the package.</string>
- <!-- Title of dialog telling users that Install/Uninstall action is not supported on Android Wear. [CHAR LIMIT=30] -->
- <string name="wear_not_allowed_dlg_title">Android Wear</string>
- <!-- Title of dialog telling users that Install/Uninstall action is not supported on Android Wear. [CHAR LIMIT=none] -->
- <string name="wear_not_allowed_dlg_text">Install/Uninstall actions not supported on Wear.</string>
-
<!-- Message that the app to be installed is being staged [CHAR LIMIT=50] -->
<string name="message_staging">Staging app…</string>
diff --git a/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java b/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java
index 96a11ee..5b39f4e 100644
--- a/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java
+++ b/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java
@@ -112,26 +112,6 @@
}
/**
- * Shows restricted setting dialog.
- */
- @RequiresApi(Build.VERSION_CODES.TIRAMISU)
- public static void sendShowRestrictedSettingDialogIntent(Context context,
- String packageName, int uid) {
- final Intent intent = getShowRestrictedSettingsIntent(packageName, uid);
- context.startActivity(intent);
- }
-
- /**
- * Gets restricted settings dialog intent.
- */
- private static Intent getShowRestrictedSettingsIntent(String packageName, int uid) {
- final Intent intent = new Intent(Settings.ACTION_SHOW_RESTRICTED_SETTING_DIALOG);
- intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
- intent.putExtra(Intent.EXTRA_UID, uid);
- return intent;
- }
-
- /**
* Checks if current user is profile or not
*/
@RequiresApi(Build.VERSION_CODES.M)
@@ -238,4 +218,35 @@
+ '}';
}
}
+
+
+ /**
+ * Shows restricted setting dialog.
+ *
+ * @deprecated TODO(b/308921175): This will be deleted with the
+ * {@link android.security.Flags#extendEcmToAllSettings} feature flag. Do not use for any new
+ * code.
+ */
+ @Deprecated
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
+ public static void sendShowRestrictedSettingDialogIntent(Context context,
+ String packageName, int uid) {
+ final Intent intent = getShowRestrictedSettingsIntent(packageName, uid);
+ context.startActivity(intent);
+ }
+
+ /**
+ * Gets restricted settings dialog intent.
+ *
+ * @deprecated TODO(b/308921175): This will be deleted with the
+ * {@link android.security.Flags#extendEcmToAllSettings} feature flag. Do not use for any new
+ * code.
+ */
+ @Deprecated
+ private static Intent getShowRestrictedSettingsIntent(String packageName, int uid) {
+ final Intent intent = new Intent(Settings.ACTION_SHOW_RESTRICTED_SETTING_DIALOG);
+ intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
+ intent.putExtra(Intent.EXTRA_UID, uid);
+ return intent;
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
index 4454b71..0237446 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
@@ -77,6 +77,9 @@
ECM_KEYS.add(AppOpsManager.OPSTR_LOADER_USAGE_STATS);
ECM_KEYS.add(Manifest.permission.BIND_DEVICE_ADMIN);
}
+
+ ECM_KEYS.add(AppOpsManager.OPSTR_ACCESS_NOTIFICATIONS);
+ ECM_KEYS.add(AppOpsManager.OPSTR_BIND_ACCESSIBILITY_SERVICE);
}
/**
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
index db2a6ec..50e3bd0 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
@@ -96,12 +96,29 @@
mHelper.checkRestrictionAndSetDisabled(userRestriction, userId);
}
+ /**
+ * Checks if the given setting is subject to Enhanced Confirmation Mode restrictions for this
+ * package. Marks the preference as disabled if so.
+ * @param restriction The key identifying the setting
+ * @param packageName the package to check the restriction for
+ * @param uid the uid of the package
+ */
+ public void checkEcmRestrictionAndSetDisabled(String restriction, String packageName, int uid) {
+ mHelper.checkEcmRestrictionAndSetDisabled(restriction, packageName, uid);
+ }
+
@Override
public void setEnabled(boolean enabled) {
if (enabled && isDisabledByAdmin()) {
mHelper.setDisabledByAdmin(null);
return;
}
+
+ if (enabled && isDisabledByEcm()) {
+ mHelper.setDisabledByEcm(null);
+ return;
+ }
+
super.setEnabled(enabled);
}
@@ -111,16 +128,14 @@
}
}
- public void setDisabledByAppOps(boolean disabled) {
- if (mHelper.setDisabledByAppOps(disabled)) {
- notifyChanged();
- }
- }
-
public boolean isDisabledByAdmin() {
return mHelper.isDisabledByAdmin();
}
+ public boolean isDisabledByEcm() {
+ return mHelper.isDisabledByEcm();
+ }
+
public int getUid() {
return mHelper != null ? mHelper.uid : Process.INVALID_UID;
}
@@ -128,4 +143,16 @@
public String getPackageName() {
return mHelper != null ? mHelper.packageName : null;
}
+
+ /**
+ * @deprecated TODO(b/308921175): This will be deleted with the
+ * {@link android.security.Flags#extendEcmToAllSettings} feature flag. Do not use for any new
+ * code.
+ */
+ @Deprecated
+ public void setDisabledByAppOps(boolean disabled) {
+ if (mHelper.setDisabledByAppOps(disabled)) {
+ notifyChanged();
+ }
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
index 29ea25e..a479269 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
@@ -17,10 +17,12 @@
package com.android.settingslib;
import static android.app.admin.DevicePolicyResources.Strings.Settings.CONTROLLED_BY_ADMIN_SUMMARY;
+
import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
+import android.content.Intent;
import android.content.res.TypedArray;
import android.os.Build;
import android.os.UserHandle;
@@ -52,7 +54,8 @@
private String mAttrUserRestriction = null;
private boolean mDisabledSummary = false;
- private boolean mDisabledByAppOps;
+ private boolean mDisabledByEcm;
+ private Intent mDisabledByEcmIntent = null;
public RestrictedPreferenceHelper(Context context, Preference preference,
AttributeSet attrs, String packageName, int uid) {
@@ -101,7 +104,7 @@
* Modify PreferenceViewHolder to add padlock if restriction is disabled.
*/
public void onBindViewHolder(PreferenceViewHolder holder) {
- if (mDisabledByAdmin || mDisabledByAppOps) {
+ if (mDisabledByAdmin || mDisabledByEcm) {
holder.itemView.setEnabled(true);
}
if (mDisabledSummary) {
@@ -112,7 +115,7 @@
: mContext.getString(R.string.disabled_by_admin_summary_text);
if (mDisabledByAdmin) {
summaryView.setText(disabledText);
- } else if (mDisabledByAppOps) {
+ } else if (mDisabledByEcm) {
summaryView.setText(R.string.disabled_by_app_ops_text);
} else if (TextUtils.equals(disabledText, summaryView.getText())) {
// It's previously set to disabled text, clear it.
@@ -144,7 +147,12 @@
RestrictedLockUtils.sendShowAdminSupportDetailsIntent(mContext, mEnforcedAdmin);
return true;
}
- if (mDisabledByAppOps) {
+ if (mDisabledByEcm) {
+ if (android.security.Flags.extendEcmToAllSettings()) {
+ mContext.startActivity(mDisabledByEcmIntent);
+ return true;
+ }
+
RestrictedLockUtilsInternal.sendShowRestrictedSettingDialogIntent(mContext, packageName,
uid);
return true;
@@ -174,6 +182,20 @@
}
/**
+ * Checks if the given setting is subject to Enhanced Confirmation Mode restrictions for this
+ * package. Marks the preference as disabled if so.
+ * @param restriction The key identifying the setting
+ * @param packageName the package to check the restriction for
+ * @param uid the uid of the package
+ */
+ public void checkEcmRestrictionAndSetDisabled(String restriction, String packageName, int uid) {
+ updatePackageDetails(packageName, uid);
+ Intent intent = RestrictedLockUtilsInternal.checkIfRequiresEnhancedConfirmation(
+ mContext, restriction, uid, packageName);
+ setDisabledByEcm(intent);
+ }
+
+ /**
* @return EnforcedAdmin if we have been passed the restriction in the xml.
*/
public EnforcedAdmin checkRestrictionEnforced() {
@@ -211,10 +233,19 @@
return changed;
}
- public boolean setDisabledByAppOps(boolean disabled) {
+ /**
+ * Disable the preference based on the passed in Intent
+ * @param disabledIntent The intent which is started when the user clicks the disabled
+ * preference. If it is {@code null}, then this preference will be enabled. Otherwise, it will
+ * be disabled.
+ * @return true if the disabled state was changed.
+ */
+ public boolean setDisabledByEcm(Intent disabledIntent) {
+ boolean disabled = disabledIntent != null;
boolean changed = false;
- if (mDisabledByAppOps != disabled) {
- mDisabledByAppOps = disabled;
+ if (mDisabledByEcm != disabled) {
+ mDisabledByEcmIntent = disabledIntent;
+ mDisabledByEcm = disabled;
changed = true;
updateDisabledState();
}
@@ -226,8 +257,8 @@
return mDisabledByAdmin;
}
- public boolean isDisabledByAppOps() {
- return mDisabledByAppOps;
+ public boolean isDisabledByEcm() {
+ return mDisabledByEcm;
}
public void updatePackageDetails(String packageName, int uid) {
@@ -236,13 +267,31 @@
}
private void updateDisabledState() {
+ boolean isEnabled = !(mDisabledByAdmin || mDisabledByEcm);
if (!(mPreference instanceof RestrictedTopLevelPreference)) {
- mPreference.setEnabled(!(mDisabledByAdmin || mDisabledByAppOps));
+ mPreference.setEnabled(isEnabled);
}
if (mPreference instanceof PrimarySwitchPreference) {
- ((PrimarySwitchPreference) mPreference)
- .setSwitchEnabled(!(mDisabledByAdmin || mDisabledByAppOps));
+ ((PrimarySwitchPreference) mPreference).setSwitchEnabled(isEnabled);
}
}
+
+
+ /**
+ * @deprecated TODO(b/308921175): This will be deleted with the
+ * {@link android.security.Flags#extendEcmToAllSettings} feature flag. Do not use for any new
+ * code.
+ */
+ @Deprecated
+ public boolean setDisabledByAppOps(boolean disabled) {
+ boolean changed = false;
+ if (mDisabledByEcm != disabled) {
+ mDisabledByEcm = disabled;
+ changed = true;
+ updateDisabledState();
+ }
+
+ return changed;
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
index 60321eb1..3b8f665 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
@@ -197,6 +197,17 @@
mHelper.checkRestrictionAndSetDisabled(userRestriction, userId);
}
+ /**
+ * Checks if the given setting is subject to Enhanced Confirmation Mode restrictions for this
+ * package. Marks the preference as disabled if so.
+ * @param restriction The key identifying the setting
+ * @param packageName the package to check the restriction for
+ * @param uid the uid of the package
+ */
+ public void checkEcmRestrictionAndSetDisabled(String restriction, String packageName, int uid) {
+ mHelper.checkEcmRestrictionAndSetDisabled(restriction, packageName, uid);
+ }
+
@Override
public void setEnabled(boolean enabled) {
boolean changed = false;
@@ -204,8 +215,8 @@
mHelper.setDisabledByAdmin(null);
changed = true;
}
- if (enabled && isDisabledByAppOps()) {
- mHelper.setDisabledByAppOps(false);
+ if (enabled && isDisabledByEcm()) {
+ mHelper.setDisabledByEcm(null);
changed = true;
}
if (!changed) {
@@ -223,25 +234,50 @@
return mHelper.isDisabledByAdmin();
}
+ public boolean isDisabledByEcm() {
+ return mHelper.isDisabledByEcm();
+ }
+
+ /**
+ * @deprecated TODO(b/308921175): This will be deleted with the
+ * {@link android.security.Flags#extendEcmToAllSettings} feature flag. Do not use for any new
+ * code.
+ */
+ @Deprecated
private void setDisabledByAppOps(boolean disabled) {
if (mHelper.setDisabledByAppOps(disabled)) {
notifyChanged();
}
}
- public boolean isDisabledByAppOps() {
- return mHelper.isDisabledByAppOps();
- }
-
+ /**
+ * @deprecated TODO(b/308921175): This will be deleted with the
+ * {@link android.security.Flags#extendEcmToAllSettings} feature flag. Do not use for any new
+ * code.
+ */
+ @Deprecated
public int getUid() {
return mHelper != null ? mHelper.uid : Process.INVALID_UID;
}
+ /**
+ * @deprecated TODO(b/308921175): This will be deleted with the
+ * {@link android.security.Flags#extendEcmToAllSettings} feature flag. Do not use for any new
+ * code.
+ */
+ @Deprecated
public String getPackageName() {
return mHelper != null ? mHelper.packageName : null;
}
- /** Updates enabled state based on associated package. */
+ /**
+ * Updates enabled state based on associated package
+ *
+ * @deprecated TODO(b/308921175): This will be deleted with the
+ * {@link android.security.Flags#extendEcmToAllSettings} feature flag. Do not use for any new
+ * code.
+ */
+ @Deprecated
public void updateState(
@NonNull String packageName, int uid, boolean isEnableAllowed, boolean isEnabled) {
mHelper.updatePackageDetails(packageName, uid);
@@ -258,7 +294,7 @@
setEnabled(false);
} else if (isEnabled) {
setEnabled(true);
- } else if (appOpsAllowed && isDisabledByAppOps()) {
+ } else if (appOpsAllowed && isDisabledByEcm()) {
setEnabled(true);
} else if (!appOpsAllowed){
setDisabledByAppOps(true);
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index d12d9d6..bacab0f 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -879,6 +879,9 @@
<uses-permission android:name="android.permission.GET_BINDING_UID_IMPORTANCE" />
+ <!-- Permissions required for CTS test - CtsAccessibilityServiceTestCases-->
+ <uses-permission android:name="android.permission.ACCESSIBILITY_MOTION_EVENT_OBSERVING" />
+
<application
android:label="@string/app_label"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 7061e2c..f10ac1b 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -204,6 +204,7 @@
"lottie",
"LowLightDreamLib",
"motion_tool_lib",
+ "notification_flags_lib",
],
libs: [
"keepanno-annotations",
@@ -328,6 +329,7 @@
"androidx.compose.ui_ui",
"flag-junit",
"platform-test-annotations",
+ "notification_flags_lib",
],
}
diff --git a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/compose/ComposeFacade.kt b/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/compose/ComposeFacade.kt
index 914e5f2..fd04b5ee0 100644
--- a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/compose/ComposeFacade.kt
+++ b/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/compose/ComposeFacade.kt
@@ -51,6 +51,7 @@
activity: ComponentActivity,
viewModel: BaseCommunalViewModel,
onOpenWidgetPicker: () -> Unit,
+ onEditDone: () -> Unit,
) {
throwComposeUnavailableError()
}
diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt
index 59bd95b..5055ee1 100644
--- a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt
+++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt
@@ -66,12 +66,14 @@
activity: ComponentActivity,
viewModel: BaseCommunalViewModel,
onOpenWidgetPicker: () -> Unit,
+ onEditDone: () -> Unit,
) {
activity.setContent {
PlatformTheme {
CommunalHub(
viewModel = viewModel,
onOpenWidgetPicker = onOpenWidgetPicker,
+ onEditDone = onEditDone,
)
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
index e8ecd3a..2a9cf0f 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
@@ -25,10 +25,12 @@
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.GridItemSpan
@@ -36,23 +38,41 @@
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
-import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.Edit
+import androidx.compose.material.icons.outlined.Delete
+import androidx.compose.material3.Button
+import androidx.compose.material3.ButtonColors
+import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
+import androidx.compose.material3.OutlinedButton
+import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.layout.LayoutCoordinates
+import androidx.compose.ui.layout.onGloballyPositioned
+import androidx.compose.ui.layout.onSizeChanged
+import androidx.compose.ui.layout.positionInWindow
+import androidx.compose.ui.platform.LocalConfiguration
+import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
+import com.android.compose.theme.LocalAndroidColorScheme
import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.communal.shared.model.CommunalContentSize
import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel
@@ -66,21 +86,38 @@
modifier: Modifier = Modifier,
viewModel: BaseCommunalViewModel,
onOpenWidgetPicker: (() -> Unit)? = null,
+ onEditDone: (() -> Unit)? = null,
) {
val communalContent by viewModel.communalContent.collectAsState(initial = emptyList())
+ var removeButtonCoordinates: LayoutCoordinates? by remember { mutableStateOf(null) }
+ var toolbarSize: IntSize? by remember { mutableStateOf(null) }
+ var gridCoordinates: LayoutCoordinates? by remember { mutableStateOf(null) }
+ var isDraggingToRemove by remember { mutableStateOf(false) }
+
Box(
modifier = modifier.fillMaxSize().background(Color.White),
) {
CommunalHubLazyGrid(
- modifier = Modifier.height(Dimensions.GridHeight).align(Alignment.CenterStart),
+ modifier = Modifier.align(Alignment.CenterStart),
communalContent = communalContent,
- isEditMode = viewModel.isEditMode,
viewModel = viewModel,
- )
- if (viewModel.isEditMode && onOpenWidgetPicker != null) {
- IconButton(onClick = onOpenWidgetPicker) {
- Icon(Icons.Default.Add, stringResource(R.string.hub_mode_add_widget_button_text))
+ contentPadding = gridContentPadding(viewModel.isEditMode, toolbarSize),
+ setGridCoordinates = { gridCoordinates = it },
+ updateDragPositionForRemove = {
+ isDraggingToRemove =
+ checkForDraggingToRemove(it, removeButtonCoordinates, gridCoordinates)
+ isDraggingToRemove
}
+ )
+
+ if (viewModel.isEditMode && onOpenWidgetPicker != null && onEditDone != null) {
+ Toolbar(
+ isDraggingToRemove = isDraggingToRemove,
+ setToolbarSize = { toolbarSize = it },
+ setRemoveButtonCoordinates = { removeButtonCoordinates = it },
+ onEditDone = onEditDone,
+ onOpenWidgetPicker = onOpenWidgetPicker,
+ )
} else {
IconButton(onClick = viewModel::onOpenWidgetEditor) {
Icon(Icons.Default.Edit, stringResource(R.string.button_to_open_widget_editor))
@@ -103,25 +140,38 @@
@Composable
private fun CommunalHubLazyGrid(
communalContent: List<CommunalContentModel>,
- isEditMode: Boolean,
viewModel: BaseCommunalViewModel,
modifier: Modifier = Modifier,
+ contentPadding: PaddingValues,
+ setGridCoordinates: (coordinates: LayoutCoordinates) -> Unit,
+ updateDragPositionForRemove: (offset: Offset) -> Boolean,
) {
var gridModifier = modifier
val gridState = rememberLazyGridState()
var list = communalContent
var dragDropState: GridDragDropState? = null
- if (isEditMode && viewModel is CommunalEditModeViewModel) {
+ if (viewModel.isEditMode && viewModel is CommunalEditModeViewModel) {
val contentListState = rememberContentListState(communalContent, viewModel)
list = contentListState.list
- dragDropState = rememberGridDragDropState(gridState, contentListState)
- gridModifier = gridModifier.dragContainer(dragDropState)
+ dragDropState =
+ rememberGridDragDropState(
+ gridState = gridState,
+ contentListState = contentListState,
+ updateDragPositionForRemove = updateDragPositionForRemove
+ )
+ gridModifier =
+ gridModifier
+ .fillMaxSize()
+ .dragContainer(dragDropState, beforeContentPadding(contentPadding))
+ .onGloballyPositioned { setGridCoordinates(it) }
+ } else {
+ gridModifier = gridModifier.height(Dimensions.GridHeight)
}
LazyHorizontalGrid(
modifier = gridModifier,
state = gridState,
rows = GridCells.Fixed(CommunalContentSize.FULL.span),
- contentPadding = PaddingValues(horizontal = Dimensions.Spacing),
+ contentPadding = contentPadding,
horizontalArrangement = Arrangement.spacedBy(Dimensions.Spacing),
verticalArrangement = Arrangement.spacedBy(Dimensions.Spacing),
) {
@@ -130,19 +180,18 @@
key = { index -> list[index].key },
span = { index -> GridItemSpan(list[index].size.span) },
) { index ->
- val cardModifier = Modifier.fillMaxHeight().width(Dimensions.CardWidth)
+ val cardModifier = Modifier.width(Dimensions.CardWidth)
val size =
SizeF(
Dimensions.CardWidth.value,
list[index].size.dp().value,
)
- if (isEditMode && dragDropState != null) {
+ if (viewModel.isEditMode && dragDropState != null) {
DraggableItem(dragDropState = dragDropState, enabled = true, index = index) {
isDragging ->
val elevation by animateDpAsState(if (isDragging) 4.dp else 1.dp)
CommunalContent(
modifier = cardModifier,
- deleteOnClick = viewModel::onDeleteWidget,
elevation = elevation,
model = list[index],
viewModel = viewModel,
@@ -161,6 +210,95 @@
}
}
+/**
+ * Toolbar that contains action buttons to
+ * 1) open the widget picker
+ * 2) remove a widget from the grid and
+ * 3) exit the edit mode.
+ */
+@Composable
+private fun Toolbar(
+ isDraggingToRemove: Boolean,
+ setToolbarSize: (toolbarSize: IntSize) -> Unit,
+ setRemoveButtonCoordinates: (coordinates: LayoutCoordinates) -> Unit,
+ onOpenWidgetPicker: () -> Unit,
+ onEditDone: () -> Unit,
+) {
+ Row(
+ modifier =
+ Modifier.fillMaxWidth()
+ .padding(
+ top = Dimensions.ToolbarPaddingTop,
+ start = Dimensions.ToolbarPaddingHorizontal,
+ end = Dimensions.ToolbarPaddingHorizontal,
+ )
+ .onSizeChanged { setToolbarSize(it) },
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ val buttonContentPadding =
+ PaddingValues(
+ vertical = Dimensions.ToolbarButtonPaddingVertical,
+ horizontal = Dimensions.ToolbarButtonPaddingHorizontal,
+ )
+ val spacerModifier = Modifier.width(Dimensions.ToolbarButtonSpaceBetween)
+ Button(
+ onClick = onOpenWidgetPicker,
+ colors = filledSecondaryButtonColors(),
+ contentPadding = buttonContentPadding
+ ) {
+ Icon(Icons.Default.Add, stringResource(R.string.button_to_open_widget_editor))
+ Spacer(spacerModifier)
+ Text(
+ text = stringResource(R.string.hub_mode_add_widget_button_text),
+ )
+ }
+
+ val buttonColors =
+ if (isDraggingToRemove) filledButtonColors() else ButtonDefaults.outlinedButtonColors()
+ OutlinedButton(
+ onClick = {},
+ colors = buttonColors,
+ contentPadding = buttonContentPadding,
+ modifier = Modifier.onGloballyPositioned { setRemoveButtonCoordinates(it) },
+ ) {
+ Icon(Icons.Outlined.Delete, stringResource(R.string.button_to_open_widget_editor))
+ Spacer(spacerModifier)
+ Text(
+ text = stringResource(R.string.button_to_remove_widget),
+ )
+ }
+
+ Button(
+ onClick = onEditDone,
+ colors = filledButtonColors(),
+ contentPadding = buttonContentPadding
+ ) {
+ Text(
+ text = stringResource(R.string.hub_mode_editing_exit_button_text),
+ )
+ }
+ }
+}
+
+@Composable
+private fun filledButtonColors(): ButtonColors {
+ val colors = LocalAndroidColorScheme.current
+ return ButtonDefaults.buttonColors(
+ containerColor = colors.primary,
+ contentColor = colors.onPrimary,
+ )
+}
+
+@Composable
+private fun filledSecondaryButtonColors(): ButtonColors {
+ val colors = LocalAndroidColorScheme.current
+ return ButtonDefaults.buttonColors(
+ containerColor = colors.secondary,
+ contentColor = colors.onSecondary,
+ )
+}
+
@Composable
private fun CommunalContent(
model: CommunalContentModel,
@@ -168,11 +306,9 @@
size: SizeF,
modifier: Modifier = Modifier,
elevation: Dp = 0.dp,
- deleteOnClick: ((id: Int) -> Unit)? = null,
) {
when (model) {
- is CommunalContentModel.Widget ->
- WidgetContent(model, size, elevation, deleteOnClick, modifier)
+ is CommunalContentModel.Widget -> WidgetContent(model, size, elevation, modifier)
is CommunalContentModel.Smartspace -> SmartspaceContent(model, modifier)
is CommunalContentModel.Tutorial -> TutorialContent(modifier)
is CommunalContentModel.Umo -> Umo(viewModel, modifier)
@@ -184,19 +320,12 @@
model: CommunalContentModel.Widget,
size: SizeF,
elevation: Dp,
- deleteOnClick: ((id: Int) -> Unit)?,
modifier: Modifier = Modifier,
) {
- // TODO(b/309009246): update background color
Card(
- modifier = modifier.fillMaxSize().background(Color.White),
+ modifier = modifier.height(size.height.dp),
elevation = CardDefaults.cardElevation(draggedElevation = elevation),
) {
- if (deleteOnClick != null) {
- IconButton(onClick = { deleteOnClick(model.appWidgetId) }) {
- Icon(Icons.Default.Close, stringResource(R.string.button_to_remove_widget))
- }
- }
AndroidView(
modifier = modifier,
factory = { context ->
@@ -249,6 +378,60 @@
)
}
+/**
+ * Returns the `contentPadding` of the grid. Use the vertical padding to push the grid content area
+ * below the toolbar and let the grid take the max size. This ensures the item can be dragged
+ * outside the grid over the toolbar, without part of it getting clipped by the container.
+ */
+@Composable
+private fun gridContentPadding(isEditMode: Boolean, toolbarSize: IntSize?): PaddingValues {
+ if (!isEditMode || toolbarSize == null) {
+ return PaddingValues(horizontal = Dimensions.Spacing)
+ }
+ val configuration = LocalConfiguration.current
+ val density = LocalDensity.current
+ val screenHeight = configuration.screenHeightDp.dp
+ val toolbarHeight = with(density) { Dimensions.ToolbarPaddingTop + toolbarSize.height.toDp() }
+ val verticalPadding =
+ ((screenHeight - toolbarHeight - Dimensions.GridHeight) / 2).coerceAtLeast(
+ Dimensions.Spacing
+ )
+ return PaddingValues(
+ start = Dimensions.ToolbarPaddingHorizontal,
+ end = Dimensions.ToolbarPaddingHorizontal,
+ top = verticalPadding + toolbarHeight,
+ bottom = verticalPadding
+ )
+}
+
+@Composable
+private fun beforeContentPadding(paddingValues: PaddingValues): ContentPaddingInPx {
+ return with(LocalDensity.current) {
+ ContentPaddingInPx(
+ startPadding = paddingValues.calculateLeftPadding(LayoutDirection.Ltr).toPx(),
+ topPadding = paddingValues.calculateTopPadding().toPx()
+ )
+ }
+}
+
+/**
+ * Check whether the pointer position that the item is being dragged at is within the coordinates of
+ * the remove button in the toolbar. Returns true if the item is removable.
+ */
+private fun checkForDraggingToRemove(
+ offset: Offset,
+ removeButtonCoordinates: LayoutCoordinates?,
+ gridCoordinates: LayoutCoordinates?,
+): Boolean {
+ if (removeButtonCoordinates == null || gridCoordinates == null) {
+ return false
+ }
+ val pointer = gridCoordinates.positionInWindow() + offset
+ val removeButton = removeButtonCoordinates.positionInWindow()
+ return pointer.x in removeButton.x..removeButton.x + removeButtonCoordinates.size.width &&
+ pointer.y in removeButton.y..removeButton.y + removeButtonCoordinates.size.height
+}
+
private fun CommunalContentSize.dp(): Dp {
return when (this) {
CommunalContentSize.FULL -> Dimensions.CardHeightFull
@@ -257,6 +440,8 @@
}
}
+data class ContentPaddingInPx(val startPadding: Float, val topPadding: Float)
+
object Dimensions {
val CardWidth = 464.dp
val CardHeightFull = 630.dp
@@ -264,4 +449,11 @@
val CardHeightThird = 199.dp
val GridHeight = CardHeightFull
val Spacing = 16.dp
+
+ // The sizing/padding of the toolbar in glanceable hub edit mode
+ val ToolbarPaddingTop = 27.dp
+ val ToolbarPaddingHorizontal = 16.dp
+ val ToolbarButtonPaddingHorizontal = 24.dp
+ val ToolbarButtonPaddingVertical = 16.dp
+ val ToolbarButtonSpaceBetween = 8.dp
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt
index 6cfa2f2..5451d05 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt
@@ -48,12 +48,18 @@
@Composable
fun rememberGridDragDropState(
gridState: LazyGridState,
- contentListState: ContentListState
+ contentListState: ContentListState,
+ updateDragPositionForRemove: (offset: Offset) -> Boolean,
): GridDragDropState {
val scope = rememberCoroutineScope()
val state =
remember(gridState, contentListState) {
- GridDragDropState(state = gridState, contentListState = contentListState, scope = scope)
+ GridDragDropState(
+ state = gridState,
+ contentListState = contentListState,
+ scope = scope,
+ updateDragPositionForRemove = updateDragPositionForRemove
+ )
}
LaunchedEffect(state) {
while (true) {
@@ -67,23 +73,30 @@
/**
* Handles drag and drop cards in the glanceable hub. While dragging to move, other items that are
* affected will dynamically get positioned and the state is tracked by [ContentListState]. When
- * dragging to remove, affected cards will be moved and [ContentListState.onRemove] is called to
- * remove the dragged item. On dragging ends, call [ContentListState.onSaveList] to persist the
- * change.
+ * dragging to remove, affected cards will be moved and [updateDragPositionForRemove] is called to
+ * check whether the dragged item can be removed. On dragging ends, call [ContentListState.onRemove]
+ * to remove the dragged item if condition met and call [ContentListState.onSaveList] to persist any
+ * change in ordering.
*/
class GridDragDropState
internal constructor(
private val state: LazyGridState,
private val contentListState: ContentListState,
private val scope: CoroutineScope,
+ private val updateDragPositionForRemove: (offset: Offset) -> Boolean
) {
var draggingItemIndex by mutableStateOf<Int?>(null)
private set
+ var isDraggingToRemove by mutableStateOf(false)
+ private set
+
internal val scrollChannel = Channel<Float>()
private var draggingItemDraggedDelta by mutableStateOf(Offset.Zero)
private var draggingItemInitialOffset by mutableStateOf(Offset.Zero)
+ private var dragStartPointerOffset by mutableStateOf(Offset.Zero)
+
internal val draggingItemOffset: Offset
get() =
draggingItemLayoutInfo?.let { item ->
@@ -94,27 +107,36 @@
private val draggingItemLayoutInfo: LazyGridItemInfo?
get() = state.layoutInfo.visibleItemsInfo.firstOrNull { it.index == draggingItemIndex }
- internal fun onDragStart(offset: Offset) {
+ internal fun onDragStart(offset: Offset, contentOffset: Offset) {
state.layoutInfo.visibleItemsInfo
.firstOrNull { item ->
+ // grid item offset is based off grid content container so we need to deduct
+ // before content padding from the initial pointer position
item.isEditable &&
- offset.x.toInt() in item.offset.x..item.offsetEnd.x &&
- offset.y.toInt() in item.offset.y..item.offsetEnd.y
+ (offset.x - contentOffset.x).toInt() in item.offset.x..item.offsetEnd.x &&
+ (offset.y - contentOffset.y).toInt() in item.offset.y..item.offsetEnd.y
}
?.apply {
+ dragStartPointerOffset = offset - this.offset.toOffset()
draggingItemIndex = index
draggingItemInitialOffset = this.offset.toOffset()
}
}
internal fun onDragInterrupted() {
- if (draggingItemIndex != null) {
+ draggingItemIndex?.let {
+ if (isDraggingToRemove) {
+ contentListState.onRemove(it)
+ isDraggingToRemove = false
+ updateDragPositionForRemove(Offset.Zero)
+ }
// persist list editing changes on dragging ends
contentListState.onSaveList()
draggingItemIndex = null
}
draggingItemDraggedDelta = Offset.Zero
draggingItemInitialOffset = Offset.Zero
+ dragStartPointerOffset = Offset.Zero
}
internal fun onDrag(offset: Offset) {
@@ -152,18 +174,13 @@
contentListState.onMove(draggingItem.index, targetItem.index)
}
draggingItemIndex = targetItem.index
+ isDraggingToRemove = false
} else {
val overscroll = checkForOverscroll(startOffset, endOffset)
if (overscroll != 0f) {
scrollChannel.trySend(overscroll)
}
- val removeOffset = checkForRemove(startOffset)
- if (removeOffset != 0f) {
- draggingItemIndex?.let {
- contentListState.onRemove(it)
- draggingItemIndex = null
- }
- }
+ isDraggingToRemove = checkForRemove(startOffset)
}
}
@@ -185,14 +202,11 @@
}
}
- // TODO(b/309968801): a temporary solution to decide whether to remove card when it's dragged up
- // and out of grid. Once we have a taskbar, calculate the intersection of the dragged item with
- // the Remove button.
- private fun checkForRemove(startOffset: Offset): Float {
+ /** Calls the callback with the updated drag position and returns whether to remove the item. */
+ private fun checkForRemove(startOffset: Offset): Boolean {
return if (draggingItemDraggedDelta.y < 0)
- (startOffset.y + Dimensions.CardHeightHalf.value - state.layoutInfo.viewportStartOffset)
- .coerceAtMost(0f)
- else 0f
+ updateDragPositionForRemove(startOffset + dragStartPointerOffset)
+ else false
}
}
@@ -204,14 +218,22 @@
return Offset(x + size.width, y + size.height)
}
-fun Modifier.dragContainer(dragDropState: GridDragDropState): Modifier {
- return pointerInput(dragDropState) {
+fun Modifier.dragContainer(
+ dragDropState: GridDragDropState,
+ beforeContentPadding: ContentPaddingInPx
+): Modifier {
+ return pointerInput(dragDropState, beforeContentPadding) {
detectDragGesturesAfterLongPress(
onDrag = { change, offset ->
change.consume()
dragDropState.onDrag(offset = offset)
},
- onDragStart = { offset -> dragDropState.onDragStart(offset) },
+ onDragStart = { offset ->
+ dragDropState.onDragStart(
+ offset,
+ Offset(beforeContentPadding.startPadding, beforeContentPadding.topPadding)
+ )
+ },
onDragEnd = { dragDropState.onDragInterrupted() },
onDragCancel = { dragDropState.onDragInterrupted() }
)
@@ -237,6 +259,7 @@
Modifier.zIndex(1f).graphicsLayer {
translationX = dragDropState.draggingItemOffset.x
translationY = dragDropState.draggingItemOffset.y
+ alpha = if (dragDropState.isDraggingToRemove) 0.5f else 1f
}
} else {
Modifier.animateItemPlacement()
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt
index ded6cc1..32025b4 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt
@@ -73,37 +73,37 @@
internal fun Modifier.nestedScrollToScene(
layoutImpl: SceneTransitionLayoutImpl,
orientation: Orientation,
- startBehavior: NestedScrollBehavior,
- endBehavior: NestedScrollBehavior,
+ topOrLeftBehavior: NestedScrollBehavior,
+ bottomOrRightBehavior: NestedScrollBehavior,
) =
this then
NestedScrollToSceneElement(
layoutImpl = layoutImpl,
orientation = orientation,
- startBehavior = startBehavior,
- endBehavior = endBehavior,
+ topOrLeftBehavior = topOrLeftBehavior,
+ bottomOrRightBehavior = bottomOrRightBehavior,
)
private data class NestedScrollToSceneElement(
private val layoutImpl: SceneTransitionLayoutImpl,
private val orientation: Orientation,
- private val startBehavior: NestedScrollBehavior,
- private val endBehavior: NestedScrollBehavior,
+ private val topOrLeftBehavior: NestedScrollBehavior,
+ private val bottomOrRightBehavior: NestedScrollBehavior,
) : ModifierNodeElement<NestedScrollToSceneNode>() {
override fun create() =
NestedScrollToSceneNode(
layoutImpl = layoutImpl,
orientation = orientation,
- startBehavior = startBehavior,
- endBehavior = endBehavior,
+ topOrLeftBehavior = topOrLeftBehavior,
+ bottomOrRightBehavior = bottomOrRightBehavior,
)
override fun update(node: NestedScrollToSceneNode) {
node.update(
layoutImpl = layoutImpl,
orientation = orientation,
- startBehavior = startBehavior,
- endBehavior = endBehavior,
+ topOrLeftBehavior = topOrLeftBehavior,
+ bottomOrRightBehavior = bottomOrRightBehavior,
)
}
@@ -111,23 +111,23 @@
name = "nestedScrollToScene"
properties["layoutImpl"] = layoutImpl
properties["orientation"] = orientation
- properties["startBehavior"] = startBehavior
- properties["endBehavior"] = endBehavior
+ properties["topOrLeftBehavior"] = topOrLeftBehavior
+ properties["bottomOrRightBehavior"] = bottomOrRightBehavior
}
}
private class NestedScrollToSceneNode(
layoutImpl: SceneTransitionLayoutImpl,
orientation: Orientation,
- startBehavior: NestedScrollBehavior,
- endBehavior: NestedScrollBehavior,
+ topOrLeftBehavior: NestedScrollBehavior,
+ bottomOrRightBehavior: NestedScrollBehavior,
) : DelegatingNode() {
private var priorityNestedScrollConnection: PriorityNestedScrollConnection =
scenePriorityNestedScrollConnection(
layoutImpl = layoutImpl,
orientation = orientation,
- startBehavior = startBehavior,
- endBehavior = endBehavior,
+ topOrLeftBehavior = topOrLeftBehavior,
+ bottomOrRightBehavior = bottomOrRightBehavior,
)
private var nestedScrollNode: DelegatableNode =
@@ -148,8 +148,8 @@
fun update(
layoutImpl: SceneTransitionLayoutImpl,
orientation: Orientation,
- startBehavior: NestedScrollBehavior,
- endBehavior: NestedScrollBehavior,
+ topOrLeftBehavior: NestedScrollBehavior,
+ bottomOrRightBehavior: NestedScrollBehavior,
) {
// Clean up the old nested scroll connection
priorityNestedScrollConnection.reset()
@@ -160,8 +160,8 @@
scenePriorityNestedScrollConnection(
layoutImpl = layoutImpl,
orientation = orientation,
- startBehavior = startBehavior,
- endBehavior = endBehavior,
+ topOrLeftBehavior = topOrLeftBehavior,
+ bottomOrRightBehavior = bottomOrRightBehavior,
)
nestedScrollNode =
nestedScrollModifierNode(
@@ -175,12 +175,12 @@
private fun scenePriorityNestedScrollConnection(
layoutImpl: SceneTransitionLayoutImpl,
orientation: Orientation,
- startBehavior: NestedScrollBehavior,
- endBehavior: NestedScrollBehavior,
+ topOrLeftBehavior: NestedScrollBehavior,
+ bottomOrRightBehavior: NestedScrollBehavior,
) =
SceneNestedScrollHandler(
gestureHandler = layoutImpl.gestureHandler(orientation = orientation),
- startBehavior = startBehavior,
- endBehavior = endBehavior,
+ topOrLeftBehavior = topOrLeftBehavior,
+ bottomOrRightBehavior = bottomOrRightBehavior,
)
.connection
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt
index f5561cb..6a7a3a0 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt
@@ -86,16 +86,26 @@
return element(layoutImpl, scene, key)
}
- override fun Modifier.nestedScrollToScene(
- orientation: Orientation,
- startBehavior: NestedScrollBehavior,
- endBehavior: NestedScrollBehavior,
+ override fun Modifier.horizontalNestedScrollToScene(
+ leftBehavior: NestedScrollBehavior,
+ rightBehavior: NestedScrollBehavior,
): Modifier =
nestedScrollToScene(
layoutImpl = layoutImpl,
- orientation = orientation,
- startBehavior = startBehavior,
- endBehavior = endBehavior,
+ orientation = Orientation.Horizontal,
+ topOrLeftBehavior = leftBehavior,
+ bottomOrRightBehavior = rightBehavior,
+ )
+
+ override fun Modifier.verticalNestedScrollToScene(
+ topBehavior: NestedScrollBehavior,
+ bottomBehavior: NestedScrollBehavior
+ ): Modifier =
+ nestedScrollToScene(
+ layoutImpl = layoutImpl,
+ orientation = Orientation.Vertical,
+ topOrLeftBehavior = topBehavior,
+ bottomOrRightBehavior = bottomBehavior,
)
@Composable
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt
index 01179d3..03f37d0 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt
@@ -494,9 +494,9 @@
}
internal class SceneNestedScrollHandler(
- private val gestureHandler: SceneGestureHandler,
- private val startBehavior: NestedScrollBehavior,
- private val endBehavior: NestedScrollBehavior,
+ private val gestureHandler: SceneGestureHandler,
+ private val topOrLeftBehavior: NestedScrollBehavior,
+ private val bottomOrRightBehavior: NestedScrollBehavior,
) : NestedScrollHandler {
override val connection: PriorityNestedScrollConnection = nestedScrollConnection()
@@ -565,8 +565,8 @@
canStartPostScroll = { offsetAvailable, offsetBeforeStart ->
val behavior: NestedScrollBehavior =
when {
- offsetAvailable > 0f -> startBehavior
- offsetAvailable < 0f -> endBehavior
+ offsetAvailable > 0f -> topOrLeftBehavior
+ offsetAvailable < 0f -> bottomOrRightBehavior
else -> return@PriorityNestedScrollConnection false
}
@@ -594,8 +594,8 @@
canStartPostFling = { velocityAvailable ->
val behavior: NestedScrollBehavior =
when {
- velocityAvailable > 0f -> startBehavior
- velocityAvailable < 0f -> endBehavior
+ velocityAvailable > 0f -> topOrLeftBehavior
+ velocityAvailable < 0f -> bottomOrRightBehavior
else -> return@PriorityNestedScrollConnection false
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
index afa184b..239971f 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
@@ -126,14 +126,24 @@
* Adds a [NestedScrollConnection] to intercept scroll events not handled by the scrollable
* component.
*
- * @param orientation is used to determine if we handle top/bottom or left/right events.
- * @param startBehavior when we should perform the overscroll animation at the top/left.
- * @param endBehavior when we should perform the overscroll animation at the bottom/right.
+ * @param leftBehavior when we should perform the overscroll animation at the left.
+ * @param rightBehavior when we should perform the overscroll animation at the right.
*/
- fun Modifier.nestedScrollToScene(
- orientation: Orientation,
- startBehavior: NestedScrollBehavior = NestedScrollBehavior.EdgeNoPreview,
- endBehavior: NestedScrollBehavior = NestedScrollBehavior.EdgeNoPreview,
+ fun Modifier.horizontalNestedScrollToScene(
+ leftBehavior: NestedScrollBehavior = NestedScrollBehavior.EdgeNoPreview,
+ rightBehavior: NestedScrollBehavior = NestedScrollBehavior.EdgeNoPreview,
+ ): Modifier
+
+ /**
+ * Adds a [NestedScrollConnection] to intercept scroll events not handled by the scrollable
+ * component.
+ *
+ * @param topBehavior when we should perform the overscroll animation at the top.
+ * @param bottomBehavior when we should perform the overscroll animation at the bottom.
+ */
+ fun Modifier.verticalNestedScrollToScene(
+ topBehavior: NestedScrollBehavior = NestedScrollBehavior.EdgeNoPreview,
+ bottomBehavior: NestedScrollBehavior = NestedScrollBehavior.EdgeNoPreview,
): Modifier
/**
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt
index aa942e0..34afc4c 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt
@@ -117,8 +117,8 @@
fun nestedScrollConnection(nestedScrollBehavior: NestedScrollBehavior) =
SceneNestedScrollHandler(
gestureHandler = sceneGestureHandler,
- startBehavior = nestedScrollBehavior,
- endBehavior = nestedScrollBehavior,
+ topOrLeftBehavior = nestedScrollBehavior,
+ bottomOrRightBehavior = nestedScrollBehavior,
)
.connection
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 16cfa23..1f8e29a 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
@@ -161,7 +161,7 @@
whenever(target1.remoteViews).thenReturn(mock(RemoteViews::class.java))
val targets = listOf(target1, target2, target3)
- smartspaceRepository.setLockscreenSmartspaceTargets(targets)
+ smartspaceRepository.setCommunalSmartspaceTargets(targets)
val smartspaceContent by collectLastValue(underTest.smartspaceContent)
assertThat(smartspaceContent?.size).isEqualTo(1)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
index 8896e6e..314dfdf 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
@@ -116,7 +116,7 @@
whenever(target.smartspaceTargetId).thenReturn("target")
whenever(target.featureType).thenReturn(SmartspaceTarget.FEATURE_TIMER)
whenever(target.remoteViews).thenReturn(Mockito.mock(RemoteViews::class.java))
- smartspaceRepository.setLockscreenSmartspaceTargets(listOf(target))
+ smartspaceRepository.setCommunalSmartspaceTargets(listOf(target))
// Media playing.
mediaRepository.mediaPlaying.value = true
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 7fbcae0..8a71168 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
@@ -135,7 +135,7 @@
whenever(target.smartspaceTargetId).thenReturn("target")
whenever(target.featureType).thenReturn(SmartspaceTarget.FEATURE_TIMER)
whenever(target.remoteViews).thenReturn(Mockito.mock(RemoteViews::class.java))
- smartspaceRepository.setLockscreenSmartspaceTargets(listOf(target))
+ smartspaceRepository.setCommunalSmartspaceTargets(listOf(target))
// Media playing.
mediaRepository.mediaPlaying.value = true
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/restoreprocessors/WorkTileRestoreProcessorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/restoreprocessors/WorkTileRestoreProcessorTest.kt
new file mode 100644
index 0000000..30d1822
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/restoreprocessors/WorkTileRestoreProcessorTest.kt
@@ -0,0 +1,95 @@
+/*
+ * 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.qs.pipeline.data.restoreprocessors
+
+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.qs.pipeline.data.model.RestoreData
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@OptIn(ExperimentalCoroutinesApi::class)
+class WorkTileRestoreProcessorTest : SysuiTestCase() {
+
+ private val underTest = WorkTileRestoreProcessor()
+ @Test
+ fun restoreWithWorkTile_removeTracking() = runTest {
+ val removeTracking by collectLastValue(underTest.removeTrackingForUser(UserHandle.of(USER)))
+ runCurrent()
+
+ val restoreData =
+ RestoreData(
+ restoredTiles = listOf(TILE_SPEC),
+ restoredAutoAddedTiles = setOf(TILE_SPEC),
+ USER,
+ )
+
+ underTest.postProcessRestore(restoreData)
+
+ assertThat(removeTracking).isEqualTo(Unit)
+ }
+
+ @Test
+ fun restoreWithWorkTile_otherUser_noRemoveTracking() = runTest {
+ val removeTracking by
+ collectLastValue(underTest.removeTrackingForUser(UserHandle.of(USER + 1)))
+ runCurrent()
+
+ val restoreData =
+ RestoreData(
+ restoredTiles = listOf(TILE_SPEC),
+ restoredAutoAddedTiles = setOf(TILE_SPEC),
+ USER,
+ )
+
+ underTest.postProcessRestore(restoreData)
+
+ assertThat(removeTracking).isNull()
+ }
+
+ @Test
+ fun restoreWithoutWorkTile_noSignal() = runTest {
+ val removeTracking by collectLastValue(underTest.removeTrackingForUser(UserHandle.of(USER)))
+ runCurrent()
+
+ val restoreData =
+ RestoreData(
+ restoredTiles = emptyList(),
+ restoredAutoAddedTiles = emptySet(),
+ USER,
+ )
+
+ underTest.postProcessRestore(restoreData)
+
+ assertThat(removeTracking).isNull()
+ }
+
+ companion object {
+ private const val USER = 10
+ private val TILE_SPEC = TileSpec.Companion.create("work")
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt
index adccc84..c7e7845 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt
@@ -25,6 +25,11 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.pipeline.data.model.RestoreData
+import com.android.systemui.qs.pipeline.data.model.RestoreProcessor
+import com.android.systemui.qs.pipeline.data.model.workTileRestoreProcessor
import com.android.systemui.qs.pipeline.domain.model.AutoAddSignal
import com.android.systemui.qs.pipeline.domain.model.AutoAddTracking
import com.android.systemui.qs.pipeline.shared.TileSpec
@@ -32,25 +37,28 @@
import com.android.systemui.settings.FakeUserTracker
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.MockitoAnnotations
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class WorkTileAutoAddableTest : SysuiTestCase() {
+ private val kosmos = Kosmos()
+
+ private val restoreProcessor: RestoreProcessor
+ get() = kosmos.workTileRestoreProcessor
+
private lateinit var userTracker: FakeUserTracker
private lateinit var underTest: WorkTileAutoAddable
@Before
fun setup() {
- MockitoAnnotations.initMocks(this)
-
userTracker =
FakeUserTracker(
_userId = USER_INFO_0.id,
@@ -58,7 +66,7 @@
_userProfiles = listOf(USER_INFO_0)
)
- underTest = WorkTileAutoAddable(userTracker)
+ underTest = WorkTileAutoAddable(userTracker, kosmos.workTileRestoreProcessor)
}
@Test
@@ -114,10 +122,80 @@
assertThat(underTest.autoAddTracking).isEqualTo(AutoAddTracking.Always)
}
+ @Test
+ fun restoreDataWithWorkTile_noCurrentManagedProfile_triggersRemove() = runTest {
+ val userId = 0
+ val signal by collectLastValue(underTest.autoAddSignal(userId))
+ runCurrent()
+
+ val restoreData = createRestoreWithWorkTile(userId)
+
+ restoreProcessor.postProcessRestore(restoreData)
+
+ assertThat(signal!!).isEqualTo(AutoAddSignal.RemoveTracking(SPEC))
+ }
+
+ @Test
+ fun restoreDataWithWorkTile_currentlyManagedProfile_doesntTriggerRemove() = runTest {
+ userTracker.set(listOf(USER_INFO_0, USER_INFO_WORK), selectedUserIndex = 0)
+ val userId = 0
+ val signals by collectValues(underTest.autoAddSignal(userId))
+ runCurrent()
+
+ val restoreData = createRestoreWithWorkTile(userId)
+
+ restoreProcessor.postProcessRestore(restoreData)
+
+ assertThat(signals).doesNotContain(AutoAddSignal.RemoveTracking(SPEC))
+ }
+
+ @Test
+ fun restoreDataWithoutWorkTile_noManagedProfile_doesntTriggerRemove() = runTest {
+ val userId = 0
+ val signals by collectValues(underTest.autoAddSignal(userId))
+ runCurrent()
+
+ val restoreData = createRestoreWithoutWorkTile(userId)
+
+ restoreProcessor.postProcessRestore(restoreData)
+
+ assertThat(signals).doesNotContain(AutoAddSignal.RemoveTracking(SPEC))
+ }
+
+ @Test
+ fun restoreDataWithoutWorkTile_managedProfile_doesntTriggerRemove() = runTest {
+ userTracker.set(listOf(USER_INFO_0, USER_INFO_WORK), selectedUserIndex = 0)
+ val userId = 0
+ val signals by collectValues(underTest.autoAddSignal(userId))
+ runCurrent()
+
+ val restoreData = createRestoreWithoutWorkTile(userId)
+
+ restoreProcessor.postProcessRestore(restoreData)
+
+ assertThat(signals).doesNotContain(AutoAddSignal.RemoveTracking(SPEC))
+ }
+
companion object {
private val SPEC = TileSpec.create(WorkModeTile.TILE_SPEC)
private val USER_INFO_0 = UserInfo(0, "", FLAG_PRIMARY or FLAG_FULL)
private val USER_INFO_1 = UserInfo(1, "", FLAG_FULL)
private val USER_INFO_WORK = UserInfo(10, "", FLAG_PROFILE or FLAG_MANAGED_PROFILE)
+
+ private fun createRestoreWithWorkTile(userId: Int): RestoreData {
+ return RestoreData(
+ listOf(TileSpec.create("a"), SPEC, TileSpec.create("b")),
+ setOf(SPEC),
+ userId,
+ )
+ }
+
+ private fun createRestoreWithoutWorkTile(userId: Int): RestoreData {
+ return RestoreData(
+ listOf(TileSpec.create("a"), TileSpec.create("b")),
+ emptySet(),
+ userId,
+ )
+ }
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt
index 41a7ec0..54b03a9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt
@@ -183,6 +183,22 @@
assertThat(autoAddedTiles).contains(SPEC)
}
+ @Test
+ fun autoAddable_removeTrackingSignal_notRemovedButUnmarked() =
+ testScope.runTest {
+ autoAddRepository.markTileAdded(USER, SPEC)
+ val autoAddedTiles by collectLastValue(autoAddRepository.autoAddedTiles(USER))
+ val fakeAutoAddable = FakeAutoAddable(SPEC, AutoAddTracking.Always)
+
+ underTest = createInteractor(setOf(fakeAutoAddable))
+
+ fakeAutoAddable.sendRemoveTrackingSignal(USER)
+ runCurrent()
+
+ verify(currentTilesInteractor, never()).removeTiles(any())
+ assertThat(autoAddedTiles).doesNotContain(SPEC)
+ }
+
private fun createInteractor(autoAddables: Set<AutoAddable>): AutoAddInteractor {
return AutoAddInteractor(
autoAddables,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt
index f73cab8..b2a9783 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt
@@ -5,10 +5,15 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.qs.pipeline.data.model.RestoreData
+import com.android.systemui.qs.pipeline.data.model.RestoreProcessor
import com.android.systemui.qs.pipeline.data.repository.FakeAutoAddRepository
import com.android.systemui.qs.pipeline.data.repository.FakeQSSettingsRestoredRepository
import com.android.systemui.qs.pipeline.data.repository.FakeTileSpecRepository
import com.android.systemui.qs.pipeline.data.repository.TilesSettingConverter
+import com.android.systemui.qs.pipeline.domain.interactor.RestoreReconciliationInteractorTest.TestableRestoreProcessor.Companion.POSTPROCESS
+import com.android.systemui.qs.pipeline.domain.interactor.RestoreReconciliationInteractorTest.TestableRestoreProcessor.Companion.PREPROCESS
+import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
+import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
@@ -17,7 +22,7 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.inOrder
@RunWith(AndroidJUnit4::class)
@SmallTest
@@ -28,6 +33,9 @@
private val qsSettingsRestoredRepository = FakeQSSettingsRestoredRepository()
+ private val restoreProcessor: TestableRestoreProcessor = TestableRestoreProcessor()
+ private val qsLogger: QSPipelineLogger = mock()
+
private lateinit var underTest: RestoreReconciliationInteractor
private val testDispatcher = StandardTestDispatcher()
@@ -35,13 +43,13 @@
@Before
fun setUp() {
- MockitoAnnotations.initMocks(this)
-
underTest =
RestoreReconciliationInteractor(
tileSpecRepository,
autoAddRepository,
qsSettingsRestoredRepository,
+ setOf(restoreProcessor),
+ qsLogger,
testScope.backgroundScope,
testDispatcher
)
@@ -85,6 +93,44 @@
assertThat(autoAdd).isEqualTo(expectedAutoAdd.toTilesSet())
}
+ @Test
+ fun restoreProcessorsCalled() =
+ testScope.runTest {
+ val user = 10
+
+ val restoredSpecs = "a,c,d,f"
+ val restoredAutoAdded = "d,e"
+
+ val restoreData =
+ RestoreData(
+ restoredSpecs.toTilesList(),
+ restoredAutoAdded.toTilesSet(),
+ user,
+ )
+
+ qsSettingsRestoredRepository.onDataRestored(restoreData)
+ runCurrent()
+
+ assertThat(restoreProcessor.calls).containsExactly(PREPROCESS, POSTPROCESS).inOrder()
+ }
+
+ private class TestableRestoreProcessor : RestoreProcessor {
+ val calls = mutableListOf<Any>()
+
+ override suspend fun preProcessRestore(restoreData: RestoreData) {
+ calls.add(PREPROCESS)
+ }
+
+ override suspend fun postProcessRestore(restoreData: RestoreData) {
+ calls.add(POSTPROCESS)
+ }
+
+ companion object {
+ val PREPROCESS = Any()
+ val POSTPROCESS = Any()
+ }
+ }
+
companion object {
private fun String.toTilesList() = TilesSettingConverter.toTilesList(this)
private fun String.toTilesSet() = TilesSettingConverter.toTilesSet(this)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/WorkProfileAutoAddedAfterRestoreTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/WorkProfileAutoAddedAfterRestoreTest.kt
new file mode 100644
index 0000000..96d5774
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/WorkProfileAutoAddedAfterRestoreTest.kt
@@ -0,0 +1,176 @@
+/*
+ * 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.qs.pipeline.domain.interactor
+
+import android.content.pm.UserInfo
+import android.os.UserManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import com.android.systemui.Flags.FLAG_QS_NEW_PIPELINE
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.qs.FakeQSFactory
+import com.android.systemui.qs.pipeline.data.model.RestoreData
+import com.android.systemui.qs.pipeline.data.repository.fakeRestoreRepository
+import com.android.systemui.qs.pipeline.data.repository.fakeTileSpecRepository
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.qsTileFactory
+import com.android.systemui.settings.fakeUserTracker
+import com.android.systemui.settings.userTracker
+import com.google.common.truth.Truth.assertThat
+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.Test
+import org.junit.runner.RunWith
+
+/**
+ * This integration test is for testing the solution to b/314781280. In particular, there are two
+ * issues we want to verify after a restore of a device with a work profile and a work mode tile:
+ * * When the work profile is re-enabled in the target device, it is auto-added.
+ * * The tile is auto-added in the same position that it was in the restored device.
+ */
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+@OptIn(ExperimentalCoroutinesApi::class)
+class WorkProfileAutoAddedAfterRestoreTest : SysuiTestCase() {
+
+ private val kosmos = Kosmos().apply { fakeUserTracker.set(listOf(USER_0_INFO), 0) }
+ // Getter here so it can change when there is a managed profile.
+ private val workTileAvailable: Boolean
+ get() = hasManagedProfile()
+ private val currentUser: Int
+ get() = kosmos.userTracker.userId
+
+ private val testScope: TestScope
+ get() = kosmos.testScope
+
+ @Before
+ fun setUp() {
+ mSetFlagsRule.enableFlags(FLAG_QS_NEW_PIPELINE)
+
+ kosmos.qsTileFactory = FakeQSFactory(::tileCreator)
+ kosmos.restoreReconciliationInteractor.start()
+ kosmos.autoAddInteractor.init(kosmos.currentTilesInteractor)
+ }
+
+ @Test
+ fun workTileRestoredAndPreviouslyAutoAdded_notAvailable_willBeAutoaddedInCorrectPosition() =
+ testScope.runTest {
+ val tiles by collectLastValue(kosmos.currentTilesInteractor.currentTiles)
+
+ // Set up
+ val currentTiles = listOf("a".toTileSpec())
+ kosmos.fakeTileSpecRepository.setTiles(currentUser, currentTiles)
+
+ val restoredTiles =
+ listOf(WORK_TILE_SPEC) + listOf("b", "c", "d").map { it.toTileSpec() }
+ val restoredAutoAdded = setOf(WORK_TILE_SPEC)
+
+ val restoreData = RestoreData(restoredTiles, restoredAutoAdded, currentUser)
+
+ // WHEN we restore tiles that auto-added the WORK tile and it's not available (there
+ // are no managed profiles)
+ kosmos.fakeRestoreRepository.onDataRestored(restoreData)
+
+ // THEN the work tile is not part of the current tiles
+ assertThat(tiles!!).hasSize(3)
+ assertThat(tiles!!.map { it.spec }).doesNotContain(WORK_TILE_SPEC)
+
+ // WHEN we add a work profile
+ createManagedProfileAndAdd()
+
+ // THEN the work profile is added in the correct place
+ assertThat(tiles!!.first().spec).isEqualTo(WORK_TILE_SPEC)
+ }
+
+ @Test
+ fun workTileNotRestoredAndPreviouslyAutoAdded_wontBeAutoAddedWhenWorkProfileIsAdded() =
+ testScope.runTest {
+ val tiles by collectLastValue(kosmos.currentTilesInteractor.currentTiles)
+
+ // Set up
+ val currentTiles = listOf("a".toTileSpec())
+ kosmos.fakeTileSpecRepository.setTiles(currentUser, currentTiles)
+ runCurrent()
+
+ val restoredTiles = listOf("b", "c", "d").map { it.toTileSpec() }
+ val restoredAutoAdded = setOf(WORK_TILE_SPEC)
+
+ val restoreData = RestoreData(restoredTiles, restoredAutoAdded, currentUser)
+
+ // WHEN we restore tiles that auto-added the WORK tile
+ kosmos.fakeRestoreRepository.onDataRestored(restoreData)
+
+ // THEN the work tile is not part of the current tiles
+ assertThat(tiles!!).hasSize(3)
+ assertThat(tiles!!.map { it.spec }).doesNotContain(WORK_TILE_SPEC)
+
+ // WHEN we add a work profile
+ createManagedProfileAndAdd()
+
+ // THEN the work profile is not added because the user had manually removed it in the
+ // past
+ assertThat(tiles!!.map { it.spec }).doesNotContain(WORK_TILE_SPEC)
+ }
+
+ private fun tileCreator(spec: String): QSTile {
+ return if (spec == WORK_TILE_SPEC.spec) {
+ FakeQSTile(currentUser, workTileAvailable)
+ } else {
+ FakeQSTile(currentUser)
+ }
+ }
+
+ private fun hasManagedProfile(): Boolean {
+ return kosmos.userTracker.userProfiles.any { it.isManagedProfile }
+ }
+
+ private fun TestScope.createManagedProfileAndAdd() {
+ kosmos.fakeUserTracker.set(
+ listOf(USER_0_INFO, MANAGED_USER_INFO),
+ 0,
+ )
+ runCurrent()
+ }
+
+ private companion object {
+ val WORK_TILE_SPEC = "work".toTileSpec()
+ val USER_0_INFO =
+ UserInfo(
+ 0,
+ "zero",
+ "",
+ UserInfo.FLAG_ADMIN or UserInfo.FLAG_FULL,
+ )
+ val MANAGED_USER_INFO =
+ UserInfo(
+ 10,
+ "ten-managed",
+ "",
+ 0,
+ UserManager.USER_TYPE_PROFILE_MANAGED,
+ )
+
+ fun String.toTileSpec() = TileSpec.create(this)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapperTest.kt
index 2b744ac..00405d0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapperTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.qs.tiles.impl.alarm.domain
import android.app.AlarmManager
+import android.graphics.drawable.TestStubDrawable
import android.widget.Switch
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -40,7 +41,14 @@
private val kosmos = Kosmos()
private val alarmTileConfig = kosmos.qsAlarmTileConfig
// Using lazy (versus =) to make sure we override the right context -- see b/311612168
- private val mapper by lazy { AlarmTileMapper(context.orCreateTestableResources.resources) }
+ private val mapper by lazy {
+ AlarmTileMapper(
+ context.orCreateTestableResources
+ .apply { addOverride(R.drawable.ic_alarm, TestStubDrawable()) }
+ .resources,
+ context.theme
+ )
+ }
@Test
fun notAlarmSet() {
@@ -100,7 +108,7 @@
): QSTileState {
val label = context.getString(R.string.status_bar_alarm)
return QSTileState(
- { Icon.Resource(R.drawable.ic_alarm, null) },
+ { Icon.Loaded(context.getDrawable(R.drawable.ic_alarm)!!, null) },
label,
activationState,
secondaryLabel,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt
index 7b2ac90..b60f483 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.qs.tiles.impl.flashlight.domain
+import android.graphics.drawable.TestStubDrawable
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -35,7 +36,17 @@
class FlashlightMapperTest : SysuiTestCase() {
private val kosmos = Kosmos()
private val qsTileConfig = kosmos.qsFlashlightTileConfig
- private val mapper by lazy { FlashlightMapper(context.orCreateTestableResources.resources) }
+ private val mapper by lazy {
+ FlashlightMapper(
+ context.orCreateTestableResources
+ .apply {
+ addOverride(R.drawable.qs_flashlight_icon_off, TestStubDrawable())
+ addOverride(R.drawable.qs_flashlight_icon_on, TestStubDrawable())
+ }
+ .resources,
+ context.theme
+ )
+ }
@Test
fun mapsDisabledDataToInactiveState() {
@@ -56,20 +67,20 @@
@Test
fun mapsEnabledDataToOnIconState() {
- val expectedIcon = Icon.Resource(R.drawable.qs_flashlight_icon_on, null)
-
val tileState: QSTileState = mapper.map(qsTileConfig, FlashlightTileModel(true))
+ val expectedIcon =
+ Icon.Loaded(context.getDrawable(R.drawable.qs_flashlight_icon_on)!!, null)
val actualIcon = tileState.icon()
assertThat(actualIcon).isEqualTo(expectedIcon)
}
@Test
fun mapsDisabledDataToOffIconState() {
- val expectedIcon = Icon.Resource(R.drawable.qs_flashlight_icon_off, null)
-
val tileState: QSTileState = mapper.map(qsTileConfig, FlashlightTileModel(false))
+ val expectedIcon =
+ Icon.Loaded(context.getDrawable(R.drawable.qs_flashlight_icon_off)!!, null)
val actualIcon = tileState.icon()
assertThat(actualIcon).isEqualTo(expectedIcon)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt
index 8791877..ea74a4c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.qs.tiles.impl.location.domain
+import android.graphics.drawable.TestStubDrawable
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -36,7 +37,17 @@
private val kosmos = Kosmos()
private val qsTileConfig = kosmos.qsLocationTileConfig
- private val mapper by lazy { LocationTileMapper(context.orCreateTestableResources.resources) }
+ private val mapper by lazy {
+ LocationTileMapper(
+ context.orCreateTestableResources
+ .apply {
+ addOverride(R.drawable.qs_location_icon_off, TestStubDrawable())
+ addOverride(R.drawable.qs_location_icon_on, TestStubDrawable())
+ }
+ .resources,
+ context.theme
+ )
+ }
@Test
fun mapsDisabledDataToInactiveState() {
@@ -56,20 +67,18 @@
@Test
fun mapsEnabledDataToOnIconState() {
- val expectedIcon = Icon.Resource(R.drawable.qs_location_icon_on, null)
-
val tileState: QSTileState = mapper.map(qsTileConfig, LocationTileModel(true))
+ val expectedIcon = Icon.Loaded(context.getDrawable(R.drawable.qs_location_icon_on)!!, null)
val actualIcon = tileState.icon()
Truth.assertThat(actualIcon).isEqualTo(expectedIcon)
}
@Test
fun mapsDisabledDataToOffIconState() {
- val expectedIcon = Icon.Resource(R.drawable.qs_location_icon_off, null)
-
val tileState: QSTileState = mapper.map(qsTileConfig, LocationTileModel(false))
+ val expectedIcon = Icon.Loaded(context.getDrawable(R.drawable.qs_location_icon_off)!!, null)
val actualIcon = tileState.icon()
Truth.assertThat(actualIcon).isEqualTo(expectedIcon)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapperTest.kt
index d182412..d162c77 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapperTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.qs.tiles.impl.saver.domain
+import android.graphics.drawable.TestStubDrawable
import android.widget.Switch
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -37,7 +38,17 @@
private val dataSaverTileConfig = kosmos.qsDataSaverTileConfig
// Using lazy (versus =) to make sure we override the right context -- see b/311612168
- private val mapper by lazy { DataSaverTileMapper(context.orCreateTestableResources.resources) }
+ private val mapper by lazy {
+ DataSaverTileMapper(
+ context.orCreateTestableResources
+ .apply {
+ addOverride(R.drawable.qs_data_saver_icon_off, TestStubDrawable())
+ addOverride(R.drawable.qs_data_saver_icon_on, TestStubDrawable())
+ }
+ .resources,
+ context.theme
+ )
+ }
@Test
fun activeStateMatchesEnabledModel() {
@@ -80,7 +91,7 @@
else context.resources.getStringArray(R.array.tile_states_saver)[0]
return QSTileState(
- { Icon.Resource(iconRes, null) },
+ { Icon.Loaded(context.getDrawable(iconRes)!!, null) },
label,
activationState,
secondaryLabel,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapperTest.kt
index 87f5009..a977606 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapperTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.qs.tiles.impl.uimodenight.domain
import android.app.UiModeManager
+import android.graphics.drawable.TestStubDrawable
import android.text.TextUtils
import android.view.View
import android.widget.Switch
@@ -41,7 +42,15 @@
private val qsTileConfig = kosmos.qsUiModeNightTileConfig
private val mapper by lazy {
- UiModeNightTileMapper(context.orCreateTestableResources.resources)
+ UiModeNightTileMapper(
+ context.orCreateTestableResources
+ .apply {
+ addOverride(R.drawable.qs_light_dark_theme_icon_off, TestStubDrawable())
+ addOverride(R.drawable.qs_light_dark_theme_icon_on, TestStubDrawable())
+ }
+ .resources,
+ context.theme
+ )
}
private fun createUiNightModeTileState(
@@ -60,7 +69,7 @@
expandedAccessibilityClass: KClass<out View>? = Switch::class,
): QSTileState {
return QSTileState(
- { Icon.Resource(iconRes, null) },
+ { Icon.Loaded(context.getDrawable(iconRes)!!, null) },
label,
activationState,
secondaryLabel,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/CommunalSmartspaceControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/CommunalSmartspaceControllerTest.kt
new file mode 100644
index 0000000..ef2046d
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/CommunalSmartspaceControllerTest.kt
@@ -0,0 +1,172 @@
+/*
+ * 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.smartspace
+
+import android.app.smartspace.SmartspaceManager
+import android.app.smartspace.SmartspaceSession
+import android.app.smartspace.SmartspaceTarget
+import android.content.Context
+import android.graphics.drawable.Drawable
+import android.testing.TestableLooper
+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.communal.smartspace.CommunalSmartspaceController
+import com.android.systemui.plugins.BcSmartspaceConfigPlugin
+import com.android.systemui.plugins.BcSmartspaceDataPlugin
+import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceView
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.util.concurrency.Execution
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.withArgCaptor
+import com.google.common.truth.Truth.assertThat
+import java.util.Optional
+import java.util.concurrent.Executor
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
[email protected]
+class CommunalSmartspaceControllerTest : SysuiTestCase() {
+ @Mock private lateinit var smartspaceManager: SmartspaceManager
+
+ @Mock private lateinit var execution: Execution
+
+ @Mock private lateinit var uiExecutor: Executor
+
+ @Mock private lateinit var targetFilter: SmartspaceTargetFilter
+
+ @Mock private lateinit var plugin: BcSmartspaceDataPlugin
+
+ @Mock private lateinit var precondition: SmartspacePrecondition
+
+ @Mock private lateinit var listener: BcSmartspaceDataPlugin.SmartspaceTargetListener
+
+ @Mock private lateinit var session: SmartspaceSession
+
+ private lateinit var controller: CommunalSmartspaceController
+
+ // TODO(b/272811280): Remove usage of real view
+ private val fakeParent = FrameLayout(context)
+
+ /**
+ * A class which implements SmartspaceView and extends View. This is mocked to provide the right
+ * object inheritance and interface implementation used in CommunalSmartspaceController
+ */
+ private class TestView(context: Context?) : View(context), SmartspaceView {
+ override fun registerDataProvider(plugin: BcSmartspaceDataPlugin?) {}
+
+ override fun registerConfigProvider(plugin: BcSmartspaceConfigPlugin?) {}
+
+ override fun setPrimaryTextColor(color: Int) {}
+
+ override fun setUiSurface(uiSurface: String) {}
+
+ override fun setDozeAmount(amount: Float) {}
+
+ override fun setIntentStarter(intentStarter: BcSmartspaceDataPlugin.IntentStarter?) {}
+
+ override fun setFalsingManager(falsingManager: FalsingManager?) {}
+
+ override fun setDnd(image: Drawable?, description: String?) {}
+
+ override fun setNextAlarm(image: Drawable?, description: String?) {}
+
+ override fun setMediaTarget(target: SmartspaceTarget?) {}
+
+ override fun getSelectedPage(): Int {
+ return 0
+ }
+
+ override fun getCurrentCardTopPadding(): Int {
+ return 0
+ }
+ }
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ `when`(smartspaceManager.createSmartspaceSession(any())).thenReturn(session)
+
+ controller =
+ CommunalSmartspaceController(
+ context,
+ smartspaceManager,
+ execution,
+ uiExecutor,
+ precondition,
+ Optional.of(targetFilter),
+ Optional.of(plugin)
+ )
+ }
+
+ /** Ensures smartspace session begins on a listener only flow. */
+ @Test
+ fun testConnectOnListen() {
+ `when`(precondition.conditionsMet()).thenReturn(true)
+ controller.addListener(listener)
+
+ verify(smartspaceManager).createSmartspaceSession(any())
+
+ var targetListener =
+ withArgCaptor<SmartspaceSession.OnTargetsAvailableListener> {
+ verify(session).addOnTargetsAvailableListener(any(), capture())
+ }
+
+ `when`(targetFilter.filterSmartspaceTarget(any())).thenReturn(true)
+
+ var target = Mockito.mock(SmartspaceTarget::class.java)
+ targetListener.onTargetsAvailable(listOf(target))
+
+ var targets =
+ withArgCaptor<List<SmartspaceTarget>> { verify(plugin).onTargetsAvailable(capture()) }
+
+ assertThat(targets.contains(target)).isTrue()
+
+ controller.removeListener(listener)
+
+ verify(session).close()
+ }
+
+ /**
+ * Ensures session is closed and weather plugin unregisters the notifier when weather smartspace
+ * view is detached.
+ */
+ @Test
+ fun testDisconnect_emitsEmptyListAndRemovesNotifier() {
+ `when`(precondition.conditionsMet()).thenReturn(true)
+ controller.addListener(listener)
+
+ verify(smartspaceManager).createSmartspaceSession(any())
+
+ controller.removeListener(listener)
+
+ verify(session).close()
+
+ // And the listener receives an empty list of targets and unregisters the notifier
+ verify(plugin).onTargetsAvailable(emptyList())
+ verify(plugin).registerSmartspaceEventNotifier(null)
+ }
+}
diff --git a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
index 64c0f99..c99cb39 100644
--- a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
+++ b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
@@ -44,6 +44,7 @@
String UI_SURFACE_HOME_SCREEN = "home";
String UI_SURFACE_MEDIA = "media_data_manager";
String UI_SURFACE_DREAM = "dream";
+ String UI_SURFACE_GLANCEABLE_HUB = "glanceable_hub";
String ACTION = "com.android.systemui.action.PLUGIN_BC_SMARTSPACE_DATA";
int VERSION = 1;
diff --git a/packages/SystemUI/res-keyguard/drawable/bouncer_password_text_view_focused_background.xml b/packages/SystemUI/res-keyguard/drawable/bouncer_password_text_view_focused_background.xml
deleted file mode 100644
index 02e10cd..0000000
--- a/packages/SystemUI/res-keyguard/drawable/bouncer_password_text_view_focused_background.xml
+++ /dev/null
@@ -1,24 +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.
--->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_focused="true">
- <shape android:shape="rectangle">
- <corners android:radius="16dp" />
- <stroke android:width="3dp"
- android:color="@color/bouncer_password_focus_color" />
- </shape>
- </item>
-</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_message_area.xml b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_message_area.xml
index 0b35559..66c54f2 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_message_area.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_message_area.xml
@@ -23,7 +23,7 @@
android:layout_marginTop="@dimen/keyguard_lock_padding"
android:importantForAccessibility="no"
android:ellipsize="marquee"
- android:focusable="false"
+ android:focusable="true"
android:gravity="center"
android:singleLine="true" />
</merge>
diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml
index 6e6709f..88f7bcd 100644
--- a/packages/SystemUI/res-keyguard/values/styles.xml
+++ b/packages/SystemUI/res-keyguard/values/styles.xml
@@ -76,7 +76,6 @@
</style>
<style name="Widget.TextView.Password" parent="@android:style/Widget.TextView">
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
- <item name="android:background">@drawable/bouncer_password_text_view_focused_background</item>
<item name="android:gravity">center</item>
<item name="android:textColor">?android:attr/textColorPrimary</item>
</style>
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index a22fd18..bcc3c83 100644
--- a/packages/SystemUI/res/values-night/colors.xml
+++ b/packages/SystemUI/res/values-night/colors.xml
@@ -93,9 +93,6 @@
<color name="qs_user_switcher_selected_avatar_icon_color">#202124</color>
<!-- Color of background circle of user avatars in quick settings user switcher -->
<color name="qs_user_switcher_avatar_background">#3C4043</color>
- <!-- Color of border for keyguard password input when focused -->
- <color name="bouncer_password_focus_color">@*android:color/system_secondary_dark</color>
-
<!-- Accessibility floating menu -->
<color name="accessibility_floating_menu_background">#B3000000</color> <!-- 70% -->
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 462fc95..5f6a39a 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -56,8 +56,6 @@
<color name="kg_user_switcher_restricted_avatar_icon_color">@color/GM2_grey_600</color>
<!-- Color of background circle of user avatars in keyguard user switcher -->
<color name="user_avatar_color_bg">?android:attr/colorBackgroundFloating</color>
- <!-- Color of border for keyguard password input when focused -->
- <color name="bouncer_password_focus_color">@*android:color/system_secondary_light</color>
<!-- Icon color for user avatars in user switcher quick settings -->
<color name="qs_user_switcher_avatar_icon_color">#3C4043</color>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 78b701c..13d2fea 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1066,9 +1066,11 @@
<!-- Description for the button that opens the widget editor on click. [CHAR LIMIT=50] -->
<string name="button_to_open_widget_editor">Open the widget editor</string>
<!-- Description for the button that removes a widget on click. [CHAR LIMIT=50] -->
- <string name="button_to_remove_widget">Remove a widget</string>
+ <string name="button_to_remove_widget">Remove</string>
<!-- Text for the button that launches the hub mode widget picker. [CHAR LIMIT=50] -->
- <string name="hub_mode_add_widget_button_text">Add Widget</string>
+ <string name="hub_mode_add_widget_button_text">Add widget</string>
+ <!-- Text for the button that exits the hub mode editing mode. [CHAR LIMIT=50] -->
+ <string name="hub_mode_editing_exit_button_text">Done</string>
<!-- Related to user switcher --><skip/>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
index 9764de1..36fe75f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
@@ -168,6 +168,7 @@
// Set selected property on so the view can send accessibility events.
mPasswordEntry.setSelected(true);
+ mPasswordEntry.setDefaultFocusHighlightEnabled(false);
mOkButton = findViewById(R.id.key_enter);
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 1a2a425..e342c6b 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
@@ -122,7 +122,7 @@
if (!smartspaceRepository.isSmartspaceRemoteViewsEnabled) {
flowOf(emptyList())
} else {
- smartspaceRepository.lockscreenSmartspaceTargets.map { targets ->
+ smartspaceRepository.communalSmartspaceTargets.map { targets ->
targets
.filter { target ->
target.featureType == SmartspaceTarget.FEATURE_TIMER &&
diff --git a/packages/SystemUI/src/com/android/systemui/communal/smartspace/CommunalSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/communal/smartspace/CommunalSmartspaceController.kt
new file mode 100644
index 0000000..c5610c8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/smartspace/CommunalSmartspaceController.kt
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.smartspace
+
+import android.app.smartspace.SmartspaceConfig
+import android.app.smartspace.SmartspaceManager
+import android.app.smartspace.SmartspaceSession
+import android.app.smartspace.SmartspaceTarget
+import android.content.Context
+import android.util.Log
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.plugins.BcSmartspaceDataPlugin
+import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceTargetListener
+import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceView
+import com.android.systemui.plugins.BcSmartspaceDataPlugin.UI_SURFACE_GLANCEABLE_HUB
+import com.android.systemui.smartspace.SmartspacePrecondition
+import com.android.systemui.smartspace.SmartspaceTargetFilter
+import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.DREAM_SMARTSPACE_PRECONDITION
+import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.DREAM_SMARTSPACE_TARGET_FILTER
+import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.GLANCEABLE_HUB_SMARTSPACE_DATA_PLUGIN
+import com.android.systemui.util.concurrency.Execution
+import java.util.Optional
+import java.util.concurrent.Executor
+import javax.inject.Inject
+import javax.inject.Named
+
+/** Controller for managing the smartspace view on the dream */
+@SysUISingleton
+class CommunalSmartspaceController
+@Inject
+constructor(
+ private val context: Context,
+ private val smartspaceManager: SmartspaceManager?,
+ private val execution: Execution,
+ @Main private val uiExecutor: Executor,
+ @Named(DREAM_SMARTSPACE_PRECONDITION) private val precondition: SmartspacePrecondition,
+ @Named(DREAM_SMARTSPACE_TARGET_FILTER)
+ private val optionalTargetFilter: Optional<SmartspaceTargetFilter>,
+ @Named(GLANCEABLE_HUB_SMARTSPACE_DATA_PLUGIN) optionalPlugin: Optional<BcSmartspaceDataPlugin>,
+) {
+ companion object {
+ private const val TAG = "CommunalSmartspaceCtrlr"
+ }
+
+ private var session: SmartspaceSession? = null
+ private val plugin: BcSmartspaceDataPlugin? = optionalPlugin.orElse(null)
+ private var targetFilter: SmartspaceTargetFilter? = optionalTargetFilter.orElse(null)
+
+ // A shadow copy of listeners is maintained to track whether the session should remain open.
+ private var listeners = mutableSetOf<SmartspaceTargetListener>()
+
+ private var unfilteredListeners = mutableSetOf<SmartspaceTargetListener>()
+
+ // Smartspace can be used on multiple displays, such as when the user casts their screen
+ private var smartspaceViews = mutableSetOf<SmartspaceView>()
+
+ var preconditionListener =
+ object : SmartspacePrecondition.Listener {
+ override fun onCriteriaChanged() {
+ reloadSmartspace()
+ }
+ }
+
+ init {
+ precondition.addListener(preconditionListener)
+ }
+
+ var filterListener =
+ object : SmartspaceTargetFilter.Listener {
+ override fun onCriteriaChanged() {
+ reloadSmartspace()
+ }
+ }
+
+ init {
+ targetFilter?.addListener(filterListener)
+ }
+
+ private val sessionListener =
+ SmartspaceSession.OnTargetsAvailableListener { targets ->
+ execution.assertIsMainThread()
+
+ val filteredTargets =
+ targets.filter { targetFilter?.filterSmartspaceTarget(it) ?: true }
+ plugin?.onTargetsAvailable(filteredTargets)
+ }
+
+ private fun hasActiveSessionListeners(): Boolean {
+ return smartspaceViews.isNotEmpty() ||
+ listeners.isNotEmpty() ||
+ unfilteredListeners.isNotEmpty()
+ }
+
+ private fun connectSession() {
+ if (smartspaceManager == null) {
+ return
+ }
+ if (plugin == null) {
+ return
+ }
+ if (session != null || !hasActiveSessionListeners()) {
+ return
+ }
+
+ if (!precondition.conditionsMet()) {
+ return
+ }
+
+ val newSession =
+ smartspaceManager.createSmartspaceSession(
+ SmartspaceConfig.Builder(context, UI_SURFACE_GLANCEABLE_HUB).build()
+ )
+ Log.d(TAG, "Starting smartspace session for dream")
+ newSession.addOnTargetsAvailableListener(uiExecutor, sessionListener)
+ this.session = newSession
+
+ plugin?.registerSmartspaceEventNotifier { e -> session?.notifySmartspaceEvent(e) }
+
+ reloadSmartspace()
+ }
+
+ /** Disconnects the smartspace view from the smartspace service and cleans up any resources. */
+ private fun disconnect() {
+ if (hasActiveSessionListeners()) return
+
+ execution.assertIsMainThread()
+
+ if (session == null) {
+ return
+ }
+
+ session?.let {
+ it.removeOnTargetsAvailableListener(sessionListener)
+ it.close()
+ }
+
+ session = null
+
+ plugin?.registerSmartspaceEventNotifier(null)
+ plugin?.onTargetsAvailable(emptyList())
+ Log.d(TAG, "Ending smartspace session for dream")
+ }
+
+ fun addListener(listener: SmartspaceTargetListener) {
+ addAndRegisterListener(listener, plugin)
+ }
+
+ fun removeListener(listener: SmartspaceTargetListener) {
+ removeAndUnregisterListener(listener, plugin)
+ }
+
+ private fun addAndRegisterListener(
+ listener: SmartspaceTargetListener,
+ smartspaceDataPlugin: BcSmartspaceDataPlugin?
+ ) {
+ execution.assertIsMainThread()
+ smartspaceDataPlugin?.registerListener(listener)
+ listeners.add(listener)
+
+ connectSession()
+ }
+
+ private fun removeAndUnregisterListener(
+ listener: SmartspaceTargetListener,
+ smartspaceDataPlugin: BcSmartspaceDataPlugin?
+ ) {
+ execution.assertIsMainThread()
+ smartspaceDataPlugin?.unregisterListener(listener)
+ listeners.remove(listener)
+ disconnect()
+ }
+
+ private fun reloadSmartspace() {
+ session?.requestSmartspaceUpdate()
+ }
+
+ private fun onTargetsAvailableUnfiltered(targets: List<SmartspaceTarget>) {
+ unfilteredListeners.forEach { it.onSmartspaceTargetsUpdated(targets) }
+ }
+}
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 7b94fc1..573a748 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
@@ -76,6 +76,10 @@
Intent(applicationContext, WidgetPickerActivity::class.java)
)
},
+ onEditDone = {
+ // TODO(b/315154364): in a separate change, lock the device and transition to GH
+ finish()
+ }
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt b/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt
index 65d4495..3a92739 100644
--- a/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt
+++ b/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt
@@ -63,6 +63,7 @@
activity: ComponentActivity,
viewModel: BaseCommunalViewModel,
onOpenWidgetPicker: () -> Unit,
+ onEditDone: () -> Unit,
)
/** Create a [View] to represent [viewModel] on screen. */
diff --git a/packages/SystemUI/src/com/android/systemui/contrast/ContrastDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/contrast/ContrastDialogDelegate.kt
index 63b01ed..0daa058 100644
--- a/packages/SystemUI/src/com/android/systemui/contrast/ContrastDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/contrast/ContrastDialogDelegate.kt
@@ -35,23 +35,22 @@
import javax.inject.Inject
/** Dialog to select contrast options */
-class ContrastDialogDelegate @Inject constructor(
- private val sysuiDialogFactory : SystemUIDialog.Factory,
+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 {
- override fun createDialog(): SystemUIDialog {
- return sysuiDialogFactory.create(this)
- }
-
@VisibleForTesting lateinit var contrastButtons: Map<Int, FrameLayout>
lateinit var dialogView: View
@VisibleForTesting var initialContrast: Float = fromContrastLevel(CONTRAST_LEVEL_STANDARD)
- override fun onCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) {
+ override fun createDialog(): SystemUIDialog {
+ val dialog = sysuiDialogFactory.create(this)
dialogView = dialog.layoutInflater.inflate(R.layout.contrast_dialog, null)
with(dialog) {
setView(dialogView)
@@ -67,12 +66,16 @@
}
setPositiveButton(com.android.settingslib.R.string.done) { _, _ -> dialog.dismiss() }
}
+
+ return dialog
+ }
+
+ override fun onCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) {
contrastButtons =
mapOf(
- CONTRAST_LEVEL_STANDARD to dialogView.requireViewById(
- R.id.contrast_button_standard),
- CONTRAST_LEVEL_MEDIUM to dialogView.requireViewById(R.id.contrast_button_medium),
- CONTRAST_LEVEL_HIGH to dialogView.requireViewById(R.id.contrast_button_high)
+ 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) ->
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt
index d5b95d67..5ec51f4 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt
@@ -16,6 +16,12 @@
package com.android.systemui.flags
+import com.android.server.notification.Flags.FLAG_CROSS_APP_POLITE_NOTIFICATIONS
+import com.android.server.notification.Flags.FLAG_POLITE_NOTIFICATIONS
+import com.android.server.notification.Flags.FLAG_VIBRATE_WHILE_UNLOCKED
+import com.android.server.notification.Flags.crossAppPoliteNotifications
+import com.android.server.notification.Flags.politeNotifications
+import com.android.server.notification.Flags.vibrateWhileUnlocked
import com.android.systemui.Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR
import com.android.systemui.Flags.keyguardBottomAreaRefactor
import com.android.systemui.dagger.SysUISingleton
@@ -36,5 +42,14 @@
val keyguardBottomAreaRefactor = FlagToken(
FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR, keyguardBottomAreaRefactor())
KeyguardShadeMigrationNssl.token dependsOn keyguardBottomAreaRefactor
+
+ val crossAppPoliteNotifToken =
+ FlagToken(FLAG_CROSS_APP_POLITE_NOTIFICATIONS, crossAppPoliteNotifications())
+ val politeNotifToken = FlagToken(FLAG_POLITE_NOTIFICATIONS, politeNotifications())
+ crossAppPoliteNotifToken dependsOn politeNotifToken
+
+ val vibrateWhileUnlockedToken =
+ FlagToken(FLAG_VIBRATE_WHILE_UNLOCKED, vibrateWhileUnlocked())
+ vibrateWhileUnlockedToken dependsOn politeNotifToken
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index b753ff7..b1d4587 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -111,7 +111,7 @@
// TODO(b/301955929)
@JvmField
val NOTIF_LS_BACKGROUND_THREAD =
- unreleasedFlag("notification_lockscreen_mgr_bg_thread", teamfood = true)
+ releasedFlag("notification_lockscreen_mgr_bg_thread")
// 200 - keyguard/lockscreen
// ** Flag retired **
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
index b51edab6..0df7f9b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
@@ -160,6 +160,9 @@
/** Last point that [KeyguardRootView] was tapped */
val lastRootViewTapPosition: MutableStateFlow<Point?>
+ /** Is the ambient indication area visible? */
+ val ambientIndicationVisible: MutableStateFlow<Boolean>
+
/** Observable for the [StatusBarState] */
val statusBarState: StateFlow<StatusBarState>
@@ -423,6 +426,8 @@
override val lastRootViewTapPosition: MutableStateFlow<Point?> = MutableStateFlow(null)
+ override val ambientIndicationVisible: MutableStateFlow<Boolean> = MutableStateFlow(false)
+
override val isDreamingWithOverlay: Flow<Boolean> =
conflatedCallbackFlow {
val callback =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index c12efe8..defca18 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -174,6 +174,9 @@
/** Last point that [KeyguardRootView] view was tapped */
val lastRootViewTapPosition: Flow<Point?> = repository.lastRootViewTapPosition.asStateFlow()
+ /** Is the ambient indication area visible? */
+ val ambientIndicationVisible: Flow<Boolean> = repository.ambientIndicationVisible.asStateFlow()
+
/** Whether the primary bouncer is showing or not. */
val primaryBouncerShowing: Flow<Boolean> = bouncerRepository.primaryBouncerShow
@@ -311,6 +314,10 @@
repository.lastRootViewTapPosition.value = point
}
+ fun setAmbientIndicationVisible(isVisible: Boolean) {
+ repository.ambientIndicationVisible.value = isVisible
+ }
+
companion object {
private const val TAG = "KeyguardInteractor"
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
index 1d4520f..26dace0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
@@ -180,7 +180,9 @@
goneToAodTransitionViewModel
.enterFromTopTranslationY(enterFromTopAmount)
.onStart { emit(0f) },
- occludedToLockscreenTransitionViewModel.lockscreenTranslationY,
+ occludedToLockscreenTransitionViewModel.lockscreenTranslationY.onStart {
+ emit(0f)
+ },
) {
keyguardTransitionY,
burnInTranslationY,
@@ -193,6 +195,7 @@
occludedToLockscreenTransitionTranslationY
}
}
+ .distinctUntilChanged()
val translationX: Flow<Float> = burnIn().map { it.translationX.toFloat() }
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogControllerV2.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogControllerV2.kt
index fdc70a8..76ef8a2 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogControllerV2.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogControllerV2.kt
@@ -278,7 +278,12 @@
d.setShowForAllUsers(true)
d.addOnDismissListener(onDialogDismissed)
if (view != null) {
- dialogLaunchAnimator.showFromView(d, view)
+ val controller = getPrivacyDialogController(view)
+ if (controller == null) {
+ d.show()
+ } else {
+ dialogLaunchAnimator.show(d, controller)
+ }
} else {
d.show()
}
@@ -291,6 +296,13 @@
}
}
+ private fun getPrivacyDialogController(source: View): DialogLaunchAnimator.Controller? {
+ val delegate = DialogLaunchAnimator.Controller.fromView(source) ?: return null
+ return object : DialogLaunchAnimator.Controller by delegate {
+ override fun shouldAnimateExit() = false
+ }
+ }
+
/** Dismisses the dialog */
fun dismissDialog() {
dialog?.dismiss()
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt
index b50798e..4bad45f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt
@@ -20,6 +20,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogBufferFactory
+import com.android.systemui.qs.pipeline.data.model.RestoreProcessor
import com.android.systemui.qs.pipeline.data.repository.DefaultTilesQSHostRepository
import com.android.systemui.qs.pipeline.data.repository.DefaultTilesRepository
import com.android.systemui.qs.pipeline.data.repository.InstalledTilesComponentRepository
@@ -39,14 +40,17 @@
import dagger.Provides
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
+import dagger.multibindings.Multibinds
-@Module(includes = [QSAutoAddModule::class])
+@Module(includes = [QSAutoAddModule::class, RestoreProcessorsModule::class])
abstract class QSPipelineModule {
/** Implementation for [TileSpecRepository] */
@Binds
abstract fun provideTileSpecRepository(impl: TileSpecSettingsRepository): TileSpecRepository
+ @Multibinds abstract fun provideRestoreProcessors(): Set<RestoreProcessor>
+
@Binds
abstract fun provideDefaultTilesRepository(
impl: DefaultTilesQSHostRepository
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/RestoreProcessorsModule.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/RestoreProcessorsModule.kt
new file mode 100644
index 0000000..e970c84
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/RestoreProcessorsModule.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.pipeline.dagger
+
+import com.android.systemui.qs.pipeline.data.model.RestoreProcessor
+import com.android.systemui.qs.pipeline.data.restoreprocessors.WorkTileRestoreProcessor
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoSet
+
+@Module
+interface RestoreProcessorsModule {
+
+ @Binds
+ @IntoSet
+ fun bindWorkTileRestoreProcessor(impl: WorkTileRestoreProcessor): RestoreProcessor
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/model/RestoreProcessor.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/model/RestoreProcessor.kt
new file mode 100644
index 0000000..8f7de19
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/model/RestoreProcessor.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.pipeline.data.model
+
+/**
+ * Perform processing of the [RestoreData] before or after it's applied to repositories.
+ *
+ * The order in which the restore processors are applied in not deterministic.
+ *
+ * In order to declare a restore processor, add it in [RestoreProcessingModule] using
+ *
+ * ```
+ * @Binds
+ * @IntoSet
+ * ``
+ */
+interface RestoreProcessor {
+
+ /** Should be called before applying the restore to the necessary repositories */
+ suspend fun preProcessRestore(restoreData: RestoreData) {}
+
+ /** Should be called after requesting the repositories to update. */
+ suspend fun postProcessRestore(restoreData: RestoreData) {}
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/AutoAddRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/AutoAddRepository.kt
index 7998dfb..d40f3f4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/AutoAddRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/AutoAddRepository.kt
@@ -20,9 +20,8 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.qs.pipeline.data.model.RestoreData
import com.android.systemui.qs.pipeline.shared.TileSpec
-import kotlinx.coroutines.flow.Flow
import javax.inject.Inject
-import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.Flow
/** Repository to track what QS tiles have been auto-added */
interface AutoAddRepository {
@@ -49,8 +48,9 @@
@SysUISingleton
class AutoAddSettingRepository
@Inject
-constructor(private val userAutoAddRepositoryFactory: UserAutoAddRepository.Factory) :
- AutoAddRepository {
+constructor(
+ private val userAutoAddRepositoryFactory: UserAutoAddRepository.Factory,
+) : AutoAddRepository {
private val userAutoAddRepositories = SparseArray<UserAutoAddRepository>()
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredRepository.kt
index 6cee116..e718eea 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredRepository.kt
@@ -10,6 +10,7 @@
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.qs.pipeline.data.model.RestoreData
+import com.android.systemui.qs.pipeline.data.repository.QSSettingsRestoredRepository.Companion.BUFFER_CAPACITY
import com.android.systemui.qs.pipeline.data.repository.TilesSettingConverter.toTilesList
import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
import javax.inject.Inject
@@ -28,6 +29,14 @@
/** Provides restored data (from Backup and Restore) for Quick Settings pipeline */
interface QSSettingsRestoredRepository {
val restoreData: Flow<RestoreData>
+
+ companion object {
+ // This capacity is the number of restore data that we will keep buffered in the shared
+ // flow. It is unlikely that at any given time there would be this many restores being
+ // processed by consumers, but just in case that a couple of users are restored at the
+ // same time and they need to be replayed for the consumers of the flow.
+ const val BUFFER_CAPACITY = 10
+ }
}
@SysUISingleton
@@ -86,11 +95,6 @@
private companion object {
private const val TAG = "QSSettingsRestoredBroadcastRepository"
- // This capacity is the number of restore data that we will keep buffered in the shared
- // flow. It is unlikely that at any given time there would be this many restores being
- // processed by consumers, but just in case that a couple of users are restored at the
- // same time and they need to be replayed for the consumers of the flow.
- private const val BUFFER_CAPACITY = 10
private val INTENT_FILTER = IntentFilter(Intent.ACTION_SETTING_RESTORED)
private const val TILES_SETTING = Settings.Secure.QS_TILES
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/restoreprocessors/WorkTileRestoreProcessor.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/restoreprocessors/WorkTileRestoreProcessor.kt
new file mode 100644
index 0000000..7376aa9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/restoreprocessors/WorkTileRestoreProcessor.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.pipeline.data.restoreprocessors
+
+import android.os.UserHandle
+import android.util.SparseIntArray
+import androidx.annotation.GuardedBy
+import androidx.core.util.getOrDefault
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.qs.pipeline.data.model.RestoreData
+import com.android.systemui.qs.pipeline.data.model.RestoreProcessor
+import com.android.systemui.qs.pipeline.data.repository.QSSettingsRestoredRepository
+import com.android.systemui.qs.pipeline.data.repository.TileSpecRepository
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.tiles.WorkModeTile
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.map
+
+/**
+ * Processor for restore data for work tile.
+ *
+ * It will indicate when auto-add tracking may be removed for a user. This may be necessary if the
+ * tile will be destroyed due to being not available, but needs to be added once work profile is
+ * enabled (after restore), in the same position as it was in the restored data.
+ */
+@SysUISingleton
+class WorkTileRestoreProcessor @Inject constructor() : RestoreProcessor {
+
+ @GuardedBy("lastRestorePosition") private val lastRestorePosition = SparseIntArray()
+
+ private val _removeTrackingForUser =
+ MutableSharedFlow<Int>(extraBufferCapacity = QSSettingsRestoredRepository.BUFFER_CAPACITY)
+
+ /**
+ * Flow indicating that we may need to remove auto-add tracking for the work tile for a given
+ * user.
+ */
+ fun removeTrackingForUser(userHandle: UserHandle): Flow<Unit> {
+ return _removeTrackingForUser.filter { it == userHandle.identifier }.map {}
+ }
+
+ override suspend fun postProcessRestore(restoreData: RestoreData) {
+ if (TILE_SPEC in restoreData.restoredTiles) {
+ synchronized(lastRestorePosition) {
+ lastRestorePosition.put(
+ restoreData.userId,
+ restoreData.restoredTiles.indexOf(TILE_SPEC)
+ )
+ }
+ _removeTrackingForUser.emit(restoreData.userId)
+ }
+ }
+
+ fun pollLastPosition(userId: Int): Int {
+ return synchronized(lastRestorePosition) {
+ lastRestorePosition.getOrDefault(userId, TileSpecRepository.POSITION_AT_END).also {
+ lastRestorePosition.delete(userId)
+ }
+ }
+ }
+
+ companion object {
+ private val TILE_SPEC = TileSpec.create(WorkModeTile.TILE_SPEC)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddable.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddable.kt
index 5e3c348..b221199 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddable.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddable.kt
@@ -17,8 +17,10 @@
package com.android.systemui.qs.pipeline.domain.autoaddable
import android.content.pm.UserInfo
+import android.os.UserHandle
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.qs.pipeline.data.restoreprocessors.WorkTileRestoreProcessor
import com.android.systemui.qs.pipeline.domain.model.AutoAddSignal
import com.android.systemui.qs.pipeline.domain.model.AutoAddTracking
import com.android.systemui.qs.pipeline.domain.model.AutoAddable
@@ -28,6 +30,8 @@
import javax.inject.Inject
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.mapNotNull
+import kotlinx.coroutines.flow.merge
/**
* [AutoAddable] for [WorkModeTile.TILE_SPEC].
@@ -36,17 +40,37 @@
* signal to remove it if there is not.
*/
@SysUISingleton
-class WorkTileAutoAddable @Inject constructor(private val userTracker: UserTracker) : AutoAddable {
+class WorkTileAutoAddable
+@Inject
+constructor(
+ private val userTracker: UserTracker,
+ private val workTileRestoreProcessor: WorkTileRestoreProcessor,
+) : AutoAddable {
private val spec = TileSpec.create(WorkModeTile.TILE_SPEC)
override fun autoAddSignal(userId: Int): Flow<AutoAddSignal> {
- return conflatedCallbackFlow {
+ val removeTrackingDueToRestore: Flow<AutoAddSignal> =
+ workTileRestoreProcessor.removeTrackingForUser(UserHandle.of(userId)).mapNotNull {
+ val profiles = userTracker.userProfiles
+ if (profiles.any { it.id == userId } && !profiles.any { it.isManagedProfile }) {
+ // Only remove auto-added if there are no managed profiles for this user
+ AutoAddSignal.RemoveTracking(spec)
+ } else {
+ null
+ }
+ }
+ val signalsFromCallback = conflatedCallbackFlow {
fun maybeSend(profiles: List<UserInfo>) {
if (profiles.any { it.id == userId }) {
// We are looking at the profiles of the correct user.
if (profiles.any { it.isManagedProfile }) {
- trySend(AutoAddSignal.Add(spec))
+ trySend(
+ AutoAddSignal.Add(
+ spec,
+ workTileRestoreProcessor.pollLastPosition(userId),
+ )
+ )
} else {
trySend(AutoAddSignal.Remove(spec))
}
@@ -65,6 +89,7 @@
awaitClose { userTracker.removeCallback(callback) }
}
+ return merge(removeTrackingDueToRestore, signalsFromCallback)
}
override val autoAddTracking = AutoAddTracking.Always
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractor.kt
index b747393..cde3835 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractor.kt
@@ -103,6 +103,10 @@
qsPipelineLogger.logTileAutoRemoved(userId, signal.spec)
repository.unmarkTileAdded(userId, signal.spec)
}
+ is AutoAddSignal.RemoveTracking -> {
+ qsPipelineLogger.logTileUnmarked(userId, signal.spec)
+ repository.unmarkTileAdded(userId, signal.spec)
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractor.kt
index 9844903..a5be14e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractor.kt
@@ -3,14 +3,15 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.qs.pipeline.data.model.RestoreProcessor
import com.android.systemui.qs.pipeline.data.repository.AutoAddRepository
import com.android.systemui.qs.pipeline.data.repository.QSSettingsRestoredRepository
import com.android.systemui.qs.pipeline.data.repository.TileSpecRepository
+import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.flatMapConcat
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.take
@@ -33,6 +34,8 @@
private val tileSpecRepository: TileSpecRepository,
private val autoAddRepository: AutoAddRepository,
private val qsSettingsRestoredRepository: QSSettingsRestoredRepository,
+ private val restoreProcessors: Set<@JvmSuppressWildcards RestoreProcessor>,
+ private val qsPipelineLogger: QSPipelineLogger,
@Application private val applicationScope: CoroutineScope,
@Background private val backgroundDispatcher: CoroutineDispatcher,
) {
@@ -40,14 +43,30 @@
@OptIn(ExperimentalCoroutinesApi::class)
fun start() {
applicationScope.launch(backgroundDispatcher) {
- qsSettingsRestoredRepository.restoreData.flatMapConcat { data ->
- autoAddRepository.autoAddedTiles(data.userId)
- .take(1)
- .map { tiles -> data to tiles }
- }.collect { (restoreData, autoAdded) ->
- tileSpecRepository.reconcileRestore(restoreData, autoAdded)
- autoAddRepository.reconcileRestore(restoreData)
- }
+ qsSettingsRestoredRepository.restoreData
+ .flatMapConcat { data ->
+ autoAddRepository.autoAddedTiles(data.userId).take(1).map { tiles ->
+ data to tiles
+ }
+ }
+ .collect { (restoreData, autoAdded) ->
+ restoreProcessors.forEach {
+ it.preProcessRestore(restoreData)
+ qsPipelineLogger.logRestoreProcessorApplied(
+ it::class.simpleName,
+ QSPipelineLogger.RestorePreprocessorStep.PREPROCESSING,
+ )
+ }
+ tileSpecRepository.reconcileRestore(restoreData, autoAdded)
+ autoAddRepository.reconcileRestore(restoreData)
+ restoreProcessors.forEach {
+ it.postProcessRestore(restoreData)
+ qsPipelineLogger.logRestoreProcessorApplied(
+ it::class.simpleName,
+ QSPipelineLogger.RestorePreprocessorStep.POSTPROCESSING,
+ )
+ }
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/model/AutoAddSignal.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/model/AutoAddSignal.kt
index ed7b8bd..8263680 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/model/AutoAddSignal.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/model/AutoAddSignal.kt
@@ -34,4 +34,9 @@
data class Remove(
override val spec: TileSpec,
) : AutoAddSignal
+
+ /** Signal for remove the auto-add marker from the tile, but not remove the tile */
+ data class RemoveTracking(
+ override val spec: TileSpec,
+ ) : AutoAddSignal
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt
index bca86c9..7d2c6c8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt
@@ -209,6 +209,18 @@
)
}
+ fun logTileUnmarked(userId: Int, spec: TileSpec) {
+ tileAutoAddLogBuffer.log(
+ AUTO_ADD_TAG,
+ LogLevel.DEBUG,
+ {
+ int1 = userId
+ str1 = spec.toString()
+ },
+ { "Tile $str1 unmarked as auto-added for user $int1" }
+ )
+ }
+
fun logSettingsRestored(restoreData: RestoreData) {
restoreLogBuffer.log(
RESTORE_TAG,
@@ -226,6 +238,21 @@
)
}
+ fun logRestoreProcessorApplied(
+ restoreProcessorClassName: String?,
+ step: RestorePreprocessorStep,
+ ) {
+ restoreLogBuffer.log(
+ RESTORE_TAG,
+ LogLevel.DEBUG,
+ {
+ str1 = restoreProcessorClassName
+ str2 = step.name
+ },
+ { "Restore $str2 processed by $str1" }
+ )
+ }
+
/** Reasons for destroying an existing tile. */
enum class TileDestroyedReason(val readable: String) {
TILE_REMOVED("Tile removed from current set"),
@@ -234,4 +261,9 @@
EXISTING_TILE_NOT_AVAILABLE("Existing tile not available"),
TILE_NOT_PRESENT_IN_NEW_USER("Tile not present in new user"),
}
+
+ enum class RestorePreprocessorStep {
+ PREPROCESSING,
+ POSTPROCESSING
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileUserActionInteractor.kt
index 09d7a1f..17b78eb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileUserActionInteractor.kt
@@ -24,7 +24,7 @@
* [QSTileInput.data]. It's guaranteed that [QSTileInput.userId] is the same as the id passed to
* [QSTileDataInteractor] to get [QSTileInput.data].
*
- * It's safe to run long running computations inside this function in this.
+ * It's safe to run long running computations inside this function.
*/
@WorkerThread suspend fun handleInput(input: QSTileInput<DATA_TYPE>)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/di/QSTilesModule.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/di/QSTilesModule.kt
index 4a342766..b325b4d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/di/QSTilesModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/di/QSTilesModule.kt
@@ -16,6 +16,8 @@
package com.android.systemui.qs.tiles.di
+import android.content.Context
+import android.content.res.Resources.Theme
import com.android.systemui.qs.external.CustomTileStatePersister
import com.android.systemui.qs.external.CustomTileStatePersisterImpl
import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
@@ -27,6 +29,7 @@
import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
import dagger.Binds
import dagger.Module
+import dagger.Provides
import dagger.multibindings.Multibinds
/** Module listing subcomponents */
@@ -57,4 +60,9 @@
@Binds
fun bindCustomTileStatePersister(impl: CustomTileStatePersisterImpl): CustomTileStatePersister
+
+ companion object {
+
+ @Provides fun provideTilesTheme(context: Context): Theme = context.theme
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
index 2350b5d..9d214e7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
@@ -59,12 +59,12 @@
import com.android.internal.logging.UiEventLogger;
import com.android.settingslib.wifi.WifiEnterpriseRestrictionUtils;
import com.android.systemui.Prefs;
-import com.android.systemui.res.R;
import com.android.systemui.accessibility.floatingmenu.AnnotationLinkSpan;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.wifitrackerlib.WifiEntry;
@@ -157,14 +157,6 @@
// Wi-Fi scanning progress bar
protected boolean mIsProgressBarVisible;
- protected boolean mIsSearchingHidden;
- protected final Runnable mHideProgressBarRunnable = () -> {
- setProgressBarVisible(false);
- };
- protected Runnable mHideSearchingRunnable = () -> {
- mIsSearchingHidden = true;
- mInternetDialogSubTitle.setText(getSubtitleText());
- };
@Inject
public InternetDialog(Context context, InternetDialogFactory internetDialogFactory,
@@ -285,8 +277,6 @@
if (DEBUG) {
Log.d(TAG, "onStop");
}
- mHandler.removeCallbacks(mHideProgressBarRunnable);
- mHandler.removeCallbacks(mHideSearchingRunnable);
mMobileNetworkLayout.setOnClickListener(null);
mConnectedWifListLayout.setOnClickListener(null);
if (mSecondaryMobileNetworkLayout != null) {
@@ -335,7 +325,6 @@
return;
}
- showProgressBar();
final boolean isDeviceLocked = mInternetDialogController.isDeviceLocked();
final boolean isWifiEnabled = mInternetDialogController.isWifiEnabled();
final boolean isWifiScanEnabled = mInternetDialogController.isWifiScanEnabled();
@@ -641,8 +630,7 @@
@Nullable
CharSequence getSubtitleText() {
- return mInternetDialogController.getSubtitleText(
- mIsProgressBarVisible && !mIsSearchingHidden);
+ return mInternetDialogController.getSubtitleText(mIsProgressBarVisible);
}
private Drawable getSignalStrengthDrawable(int subId) {
@@ -657,20 +645,6 @@
return mInternetDialogController.getMobileNetworkSummary(subId);
}
- protected void showProgressBar() {
- if (!mInternetDialogController.isWifiEnabled()
- || mInternetDialogController.isDeviceLocked()) {
- setProgressBarVisible(false);
- return;
- }
- setProgressBarVisible(true);
- if (mConnectedWifiEntry != null || mWifiEntriesCount > 0) {
- mHandler.postDelayed(mHideProgressBarRunnable, PROGRESS_DELAY_MS);
- } else if (!mIsSearchingHidden) {
- mHandler.postDelayed(mHideSearchingRunnable, PROGRESS_DELAY_MS);
- }
- }
-
private void setProgressBarVisible(boolean visible) {
if (mIsProgressBarVisible == visible) {
return;
@@ -823,6 +797,11 @@
}
@Override
+ public void onWifiScan(boolean isScan) {
+ setProgressBarVisible(isScan);
+ }
+
+ @Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (mAlertDialog != null && !mAlertDialog.isShowing()) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
index f516f55..592cb3b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -75,7 +75,6 @@
import com.android.settingslib.net.SignalStrengthUtil;
import com.android.settingslib.wifi.WifiUtils;
import com.android.settingslib.wifi.dpp.WifiDppIntentHelper;
-import com.android.systemui.res.R;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
@@ -84,6 +83,7 @@
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.connectivity.AccessPointController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.LocationController;
@@ -1129,6 +1129,15 @@
public void onSettingsActivityTriggered(Intent settingsIntent) {
}
+ @Override
+ public void onWifiScan(boolean isScan) {
+ if (!isWifiEnabled() || isDeviceLocked()) {
+ mCallback.onWifiScan(false);
+ return;
+ }
+ mCallback.onWifiScan(isScan);
+ }
+
private class InternetTelephonyCallback extends TelephonyCallback implements
TelephonyCallback.DataConnectionStateListener,
TelephonyCallback.DisplayInfoListener,
@@ -1372,6 +1381,8 @@
void onAccessPointsChanged(@Nullable List<WifiEntry> wifiEntries,
@Nullable WifiEntry connectedEntry, boolean hasMoreWifiEntries);
+
+ void onWifiScan(boolean isScan);
}
void makeOverlayToast(int stringId) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt
index cfb5442..9b8dba1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt
@@ -17,6 +17,7 @@
package com.android.systemui.qs.tiles.impl.airplane.domain
import android.content.res.Resources
+import android.content.res.Resources.Theme
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
@@ -27,18 +28,25 @@
import javax.inject.Inject
/** Maps [AirplaneModeTileModel] to [QSTileState]. */
-class AirplaneModeMapper @Inject constructor(@Main private val resources: Resources) :
- QSTileDataToStateMapper<AirplaneModeTileModel> {
+class AirplaneModeMapper
+@Inject
+constructor(
+ @Main private val resources: Resources,
+ val theme: Theme,
+) : QSTileDataToStateMapper<AirplaneModeTileModel> {
override fun map(config: QSTileConfig, data: AirplaneModeTileModel): QSTileState =
- QSTileState.build(resources, config.uiConfig) {
+ QSTileState.build(resources, theme, config.uiConfig) {
val icon =
- Icon.Resource(
- if (data.isEnabled) {
- R.drawable.qs_airplane_icon_on
- } else {
- R.drawable.qs_airplane_icon_off
- },
+ Icon.Loaded(
+ resources.getDrawable(
+ if (data.isEnabled) {
+ R.drawable.qs_airplane_icon_on
+ } else {
+ R.drawable.qs_airplane_icon_off
+ },
+ theme,
+ ),
contentDescription = null
)
this.icon = { icon }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapper.kt
index 6386577..e075e76 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapper.kt
@@ -17,6 +17,7 @@
package com.android.systemui.qs.tiles.impl.alarm.domain
import android.content.res.Resources
+import android.content.res.Resources.Theme
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
import com.android.systemui.qs.tiles.impl.alarm.domain.model.AlarmTileModel
@@ -30,14 +31,18 @@
import javax.inject.Inject
/** Maps [AlarmTileModel] to [QSTileState]. */
-class AlarmTileMapper @Inject constructor(@Main private val resources: Resources) :
- QSTileDataToStateMapper<AlarmTileModel> {
+class AlarmTileMapper
+@Inject
+constructor(
+ @Main private val resources: Resources,
+ private val theme: Theme,
+) : QSTileDataToStateMapper<AlarmTileModel> {
companion object {
val formatter12Hour: DateTimeFormatter = DateTimeFormatter.ofPattern("E hh:mm a")
val formatter24Hour: DateTimeFormatter = DateTimeFormatter.ofPattern("E HH:mm")
}
override fun map(config: QSTileConfig, data: AlarmTileModel): QSTileState =
- QSTileState.build(resources, config.uiConfig) {
+ QSTileState.build(resources, theme, config.uiConfig) {
when (data) {
is AlarmTileModel.NextAlarmSet -> {
activationState = QSTileState.ActivationState.ACTIVE
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt
index 881a6bd..1b3b584 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt
@@ -17,6 +17,7 @@
package com.android.systemui.qs.tiles.impl.flashlight.domain
import android.content.res.Resources
+import android.content.res.Resources.Theme
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
@@ -27,18 +28,25 @@
import javax.inject.Inject
/** Maps [FlashlightTileModel] to [QSTileState]. */
-class FlashlightMapper @Inject constructor(@Main private val resources: Resources) :
- QSTileDataToStateMapper<FlashlightTileModel> {
+class FlashlightMapper
+@Inject
+constructor(
+ @Main private val resources: Resources,
+ private val theme: Theme,
+) : QSTileDataToStateMapper<FlashlightTileModel> {
override fun map(config: QSTileConfig, data: FlashlightTileModel): QSTileState =
- QSTileState.build(resources, config.uiConfig) {
+ QSTileState.build(resources, theme, config.uiConfig) {
val icon =
- Icon.Resource(
- if (data.isEnabled) {
- R.drawable.qs_flashlight_icon_on
- } else {
- R.drawable.qs_flashlight_icon_off
- },
+ Icon.Loaded(
+ resources.getDrawable(
+ if (data.isEnabled) {
+ R.drawable.qs_flashlight_icon_on
+ } else {
+ R.drawable.qs_flashlight_icon_off
+ },
+ theme,
+ ),
contentDescription = null
)
this.icon = { icon }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt
index 7e7034d..fe5445d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt
@@ -17,6 +17,7 @@
package com.android.systemui.qs.tiles.impl.location.domain
import android.content.res.Resources
+import android.content.res.Resources.Theme
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
@@ -27,18 +28,25 @@
import javax.inject.Inject
/** Maps [LocationTileModel] to [QSTileState]. */
-class LocationTileMapper @Inject constructor(@Main private val resources: Resources) :
- QSTileDataToStateMapper<LocationTileModel> {
+class LocationTileMapper
+@Inject
+constructor(
+ @Main private val resources: Resources,
+ private val theme: Theme,
+) : QSTileDataToStateMapper<LocationTileModel> {
override fun map(config: QSTileConfig, data: LocationTileModel): QSTileState =
- QSTileState.build(resources, config.uiConfig) {
+ QSTileState.build(resources, theme, config.uiConfig) {
val icon =
- Icon.Resource(
- if (data.isEnabled) {
- R.drawable.qs_location_icon_on
- } else {
- R.drawable.qs_location_icon_off
- },
+ Icon.Loaded(
+ resources.getDrawable(
+ if (data.isEnabled) {
+ R.drawable.qs_location_icon_on
+ } else {
+ R.drawable.qs_location_icon_off
+ },
+ theme,
+ ),
contentDescription = null
)
this.icon = { icon }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapper.kt
index 25b0913..df25600 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapper.kt
@@ -27,20 +27,28 @@
import javax.inject.Inject
/** Maps [DataSaverTileModel] to [QSTileState]. */
-class DataSaverTileMapper @Inject constructor(@Main private val resources: Resources) :
- QSTileDataToStateMapper<DataSaverTileModel> {
+class DataSaverTileMapper
+@Inject
+constructor(
+ @Main private val resources: Resources,
+ private val theme: Resources.Theme,
+) : QSTileDataToStateMapper<DataSaverTileModel> {
override fun map(config: QSTileConfig, data: DataSaverTileModel): QSTileState =
- QSTileState.build(resources, config.uiConfig) {
+ QSTileState.build(resources, theme, config.uiConfig) {
with(data) {
+ val iconRes: Int
if (isEnabled) {
activationState = QSTileState.ActivationState.ACTIVE
- icon = { Icon.Resource(R.drawable.qs_data_saver_icon_on, null) }
+ iconRes = R.drawable.qs_data_saver_icon_on
secondaryLabel = resources.getStringArray(R.array.tile_states_saver)[2]
} else {
activationState = QSTileState.ActivationState.INACTIVE
- icon = { Icon.Resource(R.drawable.qs_data_saver_icon_off, null) }
+ iconRes = R.drawable.qs_data_saver_icon_off
secondaryLabel = resources.getStringArray(R.array.tile_states_saver)[1]
}
+ val loadedIcon =
+ Icon.Loaded(resources.getDrawable(iconRes, theme), contentDescription = null)
+ icon = { loadedIcon }
contentDescription = label
supportedActions =
setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapper.kt
index 3f30c75..ffef2b6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapper.kt
@@ -18,6 +18,7 @@
import android.app.UiModeManager
import android.content.res.Resources
+import android.content.res.Resources.Theme
import android.text.TextUtils
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.qualifiers.Main
@@ -31,15 +32,19 @@
import javax.inject.Inject
/** Maps [UiModeNightTileModel] to [QSTileState]. */
-class UiModeNightTileMapper @Inject constructor(@Main private val resources: Resources) :
- QSTileDataToStateMapper<UiModeNightTileModel> {
+class UiModeNightTileMapper
+@Inject
+constructor(
+ @Main private val resources: Resources,
+ private val theme: Theme,
+) : QSTileDataToStateMapper<UiModeNightTileModel> {
companion object {
val formatter12Hour: DateTimeFormatter = DateTimeFormatter.ofPattern("hh:mm a")
val formatter24Hour: DateTimeFormatter = DateTimeFormatter.ofPattern("HH:mm")
}
override fun map(config: QSTileConfig, data: UiModeNightTileModel): QSTileState =
with(data) {
- QSTileState.build(resources, config.uiConfig) {
+ QSTileState.build(resources, theme, config.uiConfig) {
var shouldSetSecondaryLabel = false
if (isPowerSave) {
@@ -116,8 +121,9 @@
if (activationState == QSTileState.ActivationState.ACTIVE)
R.drawable.qs_light_dark_theme_icon_on
else R.drawable.qs_light_dark_theme_icon_off
- val iconResource = Icon.Resource(iconRes, null)
- icon = { iconResource }
+ val loadedIcon =
+ Icon.Loaded(resources.getDrawable(iconRes, theme), contentDescription = null)
+ icon = { loadedIcon }
supportedActions =
if (activationState == QSTileState.ActivationState.UNAVAILABLE)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
index 23e0cb6..be1b740 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
@@ -17,6 +17,7 @@
package com.android.systemui.qs.tiles.viewmodel
import android.content.res.Resources
+import android.content.res.Resources.Theme
import android.service.quicksettings.Tile
import android.view.View
import android.widget.Switch
@@ -47,14 +48,17 @@
fun build(
resources: Resources,
+ theme: Theme,
config: QSTileUIConfig,
build: Builder.() -> Unit
- ): QSTileState =
- build(
- { Icon.Resource(config.iconRes, null) },
+ ): QSTileState {
+ val iconDrawable = resources.getDrawable(config.iconRes, theme)
+ return build(
+ { Icon.Loaded(iconDrawable, null) },
resources.getString(config.labelRes),
build,
)
+ }
fun build(icon: () -> Icon, label: CharSequence, build: Builder.() -> Unit): QSTileState =
Builder(icon, label).apply(build).build()
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt
index 8a93ef6..d3459b1 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt
@@ -32,6 +32,7 @@
* TODO(b/200063118): Make this class the one source of truth for the state of panel expansion.
*/
@SysUISingleton
+@Deprecated("Use ShadeInteractor instead")
class ShadeExpansionStateManager @Inject constructor() {
private val expansionListeners = CopyOnWriteArrayList<ShadeExpansionListener>()
@@ -49,6 +50,7 @@
*
* @see #addExpansionListener
*/
+ @Deprecated("Use ShadeInteractor instead")
fun addExpansionListener(listener: ShadeExpansionListener): ShadeExpansionChangeEvent {
expansionListeners.add(listener)
return ShadeExpansionChangeEvent(fraction, expanded, tracking, dragDownPxAmount)
@@ -60,6 +62,7 @@
}
/** Adds a listener that will be notified when the panel state has changed. */
+ @Deprecated("Use ShadeInteractor instead")
fun addStateListener(listener: ShadeStateListener) {
stateListeners.add(listener)
}
diff --git a/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt b/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt
index c59ef26..d26fded1 100644
--- a/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt
@@ -59,6 +59,11 @@
* The BcSmartspaceDataPlugin for the standalone weather.
*/
const val WEATHER_SMARTSPACE_DATA_PLUGIN = "weather_smartspace_data_plugin"
+
+ /**
+ * The BcSmartspaceDataProvider for the glanceable hub.
+ */
+ const val GLANCEABLE_HUB_SMARTSPACE_DATA_PLUGIN = "glanceable_hub_smartspace_data_plugin"
}
@BindsOptionalOf
@@ -78,4 +83,8 @@
abstract fun bindSmartspacePrecondition(
lockscreenPrecondition: LockscreenPrecondition?
): SmartspacePrecondition?
+
+ @BindsOptionalOf
+ @Named(GLANCEABLE_HUB_SMARTSPACE_DATA_PLUGIN)
+ abstract fun optionalBcSmartspaceDataPlugin(): BcSmartspaceDataPlugin?
}
diff --git a/packages/SystemUI/src/com/android/systemui/smartspace/data/repository/SmartspaceRepository.kt b/packages/SystemUI/src/com/android/systemui/smartspace/data/repository/SmartspaceRepository.kt
index 2fc0ec2..095d30e 100644
--- a/packages/SystemUI/src/com/android/systemui/smartspace/data/repository/SmartspaceRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/smartspace/data/repository/SmartspaceRepository.kt
@@ -19,9 +19,9 @@
import android.app.smartspace.SmartspaceTarget
import android.os.Parcelable
import android.widget.RemoteViews
+import com.android.systemui.communal.smartspace.CommunalSmartspaceController
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.plugins.BcSmartspaceDataPlugin
-import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
@@ -32,37 +32,37 @@
/** Whether [RemoteViews] are passed through smartspace targets. */
val isSmartspaceRemoteViewsEnabled: Boolean
- /** Smartspace targets for the lockscreen surface. */
- val lockscreenSmartspaceTargets: Flow<List<SmartspaceTarget>>
+ /** Smartspace targets for the communal surface. */
+ val communalSmartspaceTargets: Flow<List<SmartspaceTarget>>
}
@SysUISingleton
class SmartspaceRepositoryImpl
@Inject
constructor(
- private val lockscreenSmartspaceController: LockscreenSmartspaceController,
+ private val communalSmartspaceController: CommunalSmartspaceController,
) : SmartspaceRepository, BcSmartspaceDataPlugin.SmartspaceTargetListener {
override val isSmartspaceRemoteViewsEnabled: Boolean
get() = android.app.smartspace.flags.Flags.remoteViews()
- private val _lockscreenSmartspaceTargets: MutableStateFlow<List<SmartspaceTarget>> =
+ private val _communalSmartspaceTargets: MutableStateFlow<List<SmartspaceTarget>> =
MutableStateFlow(emptyList())
- override val lockscreenSmartspaceTargets: Flow<List<SmartspaceTarget>> =
- _lockscreenSmartspaceTargets
+ override val communalSmartspaceTargets: Flow<List<SmartspaceTarget>> =
+ _communalSmartspaceTargets
.onStart {
- lockscreenSmartspaceController.addListener(listener = this@SmartspaceRepositoryImpl)
+ communalSmartspaceController.addListener(listener = this@SmartspaceRepositoryImpl)
}
.onCompletion {
- lockscreenSmartspaceController.removeListener(
+ communalSmartspaceController.removeListener(
listener = this@SmartspaceRepositoryImpl
)
}
override fun onSmartspaceTargetsUpdated(targetsNullable: MutableList<out Parcelable>?) {
targetsNullable?.let { targets ->
- _lockscreenSmartspaceTargets.value = targets.filterIsInstance<SmartspaceTarget>()
+ _communalSmartspaceTargets.value = targets.filterIsInstance<SmartspaceTarget>()
}
- ?: run { _lockscreenSmartspaceTargets.value = emptyList() }
+ ?: run { _communalSmartspaceTargets.value = emptyList() }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointController.kt
index 490994d..fc474d2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointController.kt
@@ -89,5 +89,12 @@
* "wifi_start_connect_ssid" set as an extra
*/
fun onSettingsActivityTriggered(settingsIntent: Intent?)
+
+ /**
+ * Called whenever a Wi-Fi scan is triggered.
+ *
+ * @param isScan Whether Wi-Fi scan is triggered or not.
+ */
+ fun onWifiScan(isScan: Boolean)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImpl.java
index 91ca148..3a31851 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImpl.java
@@ -213,6 +213,12 @@
}
}
+ private void fireWifiScanCallback(boolean isScan) {
+ for (AccessPointCallback callback : mCallbacks) {
+ callback.onWifiScan(isScan);
+ }
+ }
+
void dump(PrintWriter pw) {
IndentingPrintWriter ipw = new IndentingPrintWriter(pw);
ipw.println("AccessPointControllerImpl:");
@@ -240,6 +246,14 @@
}
@Override
+ public void onWifiEntriesChanged(@WifiPickerTracker.WifiEntriesChangedReason int reason) {
+ onWifiEntriesChanged();
+ if (reason == WifiPickerTracker.WIFI_ENTRIES_CHANGED_REASON_SCAN_RESULTS) {
+ fireWifiScanCallback(false /* isScan */);
+ }
+ }
+
+ @Override
public void onNumSavedNetworksChanged() {
// Do nothing
}
@@ -249,6 +263,11 @@
// Do nothing
}
+ @Override
+ public void onScanRequested() {
+ fireWifiScanCallback(true /* isScan */);
+ }
+
private final WifiEntry.ConnectCallback mConnectCallback = new WifiEntry.ConnectCallback() {
@Override
public void onConnectResult(int status) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt
index 92391e7..e1e30e1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt
@@ -17,7 +17,9 @@
import android.graphics.Color
import android.graphics.Rect
+import android.util.Log
import android.view.View
+import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.annotation.ColorInt
import androidx.collection.ArrayMap
@@ -220,7 +222,7 @@
notifyBindingFailures: (Collection<String>) -> Unit,
viewStore: IconViewStore,
bindIcon: suspend (iconKey: String, view: StatusBarIconView) -> Unit = { _, _ -> },
- ): Unit = coroutineScope {
+ ) {
val iconSizeFlow: Flow<Int> =
configuration.getDimensionPixelSize(
com.android.internal.R.dimen.status_bar_icon_size_sp,
@@ -235,6 +237,21 @@
->
FrameLayout.LayoutParams(iconSize + 2 * iconHPadding, statusBarHeight)
}
+ try {
+ bindIcons(view, layoutParams, notifyBindingFailures, viewStore, bindIcon)
+ } finally {
+ // Detach everything so that child SBIVs don't hold onto a reference to the container.
+ view.detachAllIcons()
+ }
+ }
+
+ private suspend fun Flow<NotificationIconsViewData>.bindIcons(
+ view: NotificationIconContainer,
+ layoutParams: Flow<FrameLayout.LayoutParams>,
+ notifyBindingFailures: (Collection<String>) -> Unit,
+ viewStore: IconViewStore,
+ bindIcon: suspend (iconKey: String, view: StatusBarIconView) -> Unit,
+ ): Unit = coroutineScope {
val failedBindings = mutableSetOf<String>()
val boundViewsByNotifKey = ArrayMap<String, Pair<StatusBarIconView, Job>>()
var prevIcons = NotificationIconsViewData()
@@ -266,9 +283,17 @@
continue
}
failedBindings.remove(notifKey)
- // The view might still be transiently added if it was just removed and added
- // again
- view.removeTransientView(sbiv)
+ (sbiv.parent as? ViewGroup)?.run {
+ if (this !== view) {
+ Log.wtf(TAG, "StatusBarIconView($notifKey) has an unexpected parent")
+ }
+ // If the container was re-inflated and re-bound, then SBIVs might still be
+ // attached to the prior view.
+ removeView(sbiv)
+ // The view might still be transiently added if it was just removed and
+ // added again.
+ removeTransientView(sbiv)
+ }
view.addView(sbiv, idx)
boundViewsByNotifKey.remove(notifKey)?.second?.cancel()
boundViewsByNotifKey[notifKey] =
@@ -351,7 +376,8 @@
fun iconView(key: String): StatusBarIconView?
}
- @ColorInt private val DEFAULT_AOD_ICON_COLOR = Color.WHITE
+ @ColorInt private const val DEFAULT_AOD_ICON_COLOR = Color.WHITE
+ private const val TAG = "NotifIconContainerViewBinder"
}
/** [IconViewStore] for the [com.android.systemui.statusbar.NotificationShelf] */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt
index adf6cca..625fdc1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt
@@ -20,6 +20,8 @@
import android.content.Context
import com.android.systemui.common.ui.data.repository.ConfigurationRepository
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.SplitShadeStateController
import javax.inject.Inject
@@ -28,6 +30,7 @@
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
@@ -39,7 +42,9 @@
constructor(
configurationRepository: ConfigurationRepository,
private val context: Context,
- private val splitShadeStateController: SplitShadeStateController
+ private val splitShadeStateController: SplitShadeStateController,
+ keyguardInteractor: KeyguardInteractor,
+ deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
) {
private val _topPosition = MutableStateFlow(0f)
@@ -75,6 +80,19 @@
}
.distinctUntilChanged()
+ /**
+ * The notification shelf can extend over the lock icon area if:
+ * * UDFPS supported. Ambient indication will always appear below
+ * * UDFPS not supported and ambient indication not visible, which will appear above lock icon
+ */
+ val useExtraShelfSpace: Flow<Boolean> =
+ combine(
+ keyguardInteractor.ambientIndicationVisible,
+ deviceEntryUdfpsInteractor.isUdfpsSupported,
+ ) { ambientIndicationVisible, isUdfpsSupported ->
+ isUdfpsSupported || !ambientIndicationVisible
+ }
+
val isSplitShadeEnabled: Flow<Boolean> =
configurationBasedDimensions
.map { dimens: ConfigurationBasedDimensions -> dimens.useSplitShade }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
index af56a3f..12927b8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
@@ -101,12 +101,13 @@
launch {
viewModel
- .getMaxNotifications { space ->
+ .getMaxNotifications { space, extraShelfSpace ->
+ val shelfHeight = controller.getShelfHeight().toFloat()
notificationStackSizeCalculator.computeMaxKeyguardNotifications(
controller.getView(),
space,
- 0f, // Vertical space for shelf is already accounted for
- controller.getShelfHeight().toFloat(),
+ if (extraShelfSpace) shelfHeight else 0f,
+ shelfHeight,
)
}
.collect { controller.setMaxDisplayedNotifications(it) }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
index 9594bc3..eff91e5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
@@ -229,7 +229,7 @@
* When expanding or when the user is interacting with the shade, keep the count stable; do not
* emit a value.
*/
- fun getMaxNotifications(calculateSpace: (Float) -> Int): Flow<Int> {
+ fun getMaxNotifications(calculateSpace: (Float, Boolean) -> Int): Flow<Int> {
val showLimitedNotifications = isOnLockscreenWithoutShade
val showUnlimitedNotifications =
combine(
@@ -245,11 +245,17 @@
shadeInteractor.isUserInteracting,
bounds,
interactor.notificationStackChanged.onStart { emit(Unit) },
- ) { showLimitedNotifications, showUnlimitedNotifications, isUserInteracting, bounds, _
- ->
+ interactor.useExtraShelfSpace,
+ ) { flows ->
+ val showLimitedNotifications = flows[0] as Boolean
+ val showUnlimitedNotifications = flows[1] as Boolean
+ val isUserInteracting = flows[2] as Boolean
+ val bounds = flows[3] as NotificationContainerBounds
+ val useExtraShelfSpace = flows[5] as Boolean
+
if (!isUserInteracting) {
if (showLimitedNotifications) {
- emit(calculateSpace(bounds.bottom - bounds.top))
+ emit(calculateSpace(bounds.bottom - bounds.top, useExtraShelfSpace))
} else if (showUnlimitedNotifications) {
emit(-1)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index 00e78a4..0dabafb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -400,6 +400,21 @@
}
}
+ /**
+ * Removes all child {@link StatusBarIconView} instances from this container, immediately and
+ * without animation. This should be called when tearing down this container so that external
+ * icon views are not holding onto a reference thru {@link View#getParent()}.
+ */
+ public void detachAllIcons() {
+ boolean animsWereEnabled = mAnimationsEnabled;
+ boolean wasChangingPositions = mChangingViewPositions;
+ mAnimationsEnabled = false;
+ mChangingViewPositions = true;
+ removeAllViews();
+ mChangingViewPositions = wasChangingPositions;
+ mAnimationsEnabled = animsWereEnabled;
+ }
+
public boolean areIconsOverflowing() {
return mIsShowingOverflowDot;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/contrast/ContrastDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/contrast/ContrastDialogDelegateTest.kt
index e931384..65f68f9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/contrast/ContrastDialogDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/contrast/ContrastDialogDelegateTest.kt
@@ -22,6 +22,7 @@
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
@@ -39,6 +40,8 @@
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
@@ -73,13 +76,20 @@
if (Looper.myLooper() == null) Looper.prepare()
mContrastDialogDelegate =
- ContrastDialogDelegate(
- sysuiDialogFactory,
- mainExecutor,
- mockUiModeManager,
- mockUserTracker,
- mockSecureSettings
- )
+ 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
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
index 6878007..459a74c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
@@ -184,6 +184,13 @@
}
@Test
+ fun translationYInitialValueIsZero() =
+ testScope.runTest {
+ val translationY by collectLastValue(underTest.translationY)
+ assertThat(translationY).isEqualTo(0)
+ }
+
+ @Test
fun translationAndScaleFromBurnInNotDozing() =
testScope.runTest {
val translationX by collectLastValue(underTest.translationX)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerV2Test.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerV2Test.kt
index 0a8c0ab..e4432f3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerV2Test.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerV2Test.kt
@@ -31,18 +31,20 @@
import android.permission.PermissionManager
import android.testing.AndroidTestingRunner
import android.view.View
+import android.widget.LinearLayout
import androidx.test.filters.SmallTest
import com.android.internal.logging.UiEventLogger
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.DialogLaunchAnimator
+import com.android.systemui.animation.LaunchableView
import com.android.systemui.appops.AppOpsController
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.privacy.logging.PrivacyLogger
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
@@ -56,12 +58,12 @@
import org.mockito.ArgumentMatchers.anyString
import org.mockito.Captor
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.atLeastOnce
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@SmallTest
@@ -83,60 +85,48 @@
private val TEST_INTENT = Intent("test_intent_action")
}
- @Mock
- private lateinit var dialog: PrivacyDialogV2
- @Mock
- private lateinit var permissionManager: PermissionManager
- @Mock
- private lateinit var packageManager: PackageManager
- @Mock
- private lateinit var privacyItemController: PrivacyItemController
- @Mock
- private lateinit var userTracker: UserTracker
- @Mock
- private lateinit var activityStarter: ActivityStarter
- @Mock
- private lateinit var privacyLogger: PrivacyLogger
- @Mock
- private lateinit var keyguardStateController: KeyguardStateController
- @Mock
- private lateinit var appOpsController: AppOpsController
+ @Mock private lateinit var dialog: PrivacyDialogV2
+ @Mock private lateinit var permissionManager: PermissionManager
+ @Mock private lateinit var packageManager: PackageManager
+ @Mock private lateinit var privacyItemController: PrivacyItemController
+ @Mock private lateinit var userTracker: UserTracker
+ @Mock private lateinit var activityStarter: ActivityStarter
+ @Mock private lateinit var privacyLogger: PrivacyLogger
+ @Mock private lateinit var keyguardStateController: KeyguardStateController
+ @Mock private lateinit var appOpsController: AppOpsController
@Captor
private lateinit var dialogDismissedCaptor: ArgumentCaptor<PrivacyDialogV2.OnDialogDismissed>
- @Captor
- private lateinit var activityStartedCaptor: ArgumentCaptor<ActivityStarter.Callback>
- @Captor
- private lateinit var intentCaptor: ArgumentCaptor<Intent>
- @Mock
- private lateinit var uiEventLogger: UiEventLogger
- @Mock
- private lateinit var dialogLaunchAnimator: DialogLaunchAnimator
+ @Captor private lateinit var activityStartedCaptor: ArgumentCaptor<ActivityStarter.Callback>
+ @Captor private lateinit var intentCaptor: ArgumentCaptor<Intent>
+ @Mock private lateinit var uiEventLogger: UiEventLogger
+ @Mock private lateinit var dialogLaunchAnimator: DialogLaunchAnimator
private val backgroundExecutor = FakeExecutor(FakeSystemClock())
private val uiExecutor = FakeExecutor(FakeSystemClock())
private lateinit var controller: PrivacyDialogControllerV2
private var nextUid: Int = 0
- private val dialogProvider = object : PrivacyDialogControllerV2.DialogProvider {
- var list: List<PrivacyDialogV2.PrivacyElement>? = null
- var manageApp: ((String, Int, Intent) -> Unit)? = null
- var closeApp: ((String, Int) -> Unit)? = null
- var openPrivacyDashboard: (() -> Unit)? = null
+ private val dialogProvider =
+ object : PrivacyDialogControllerV2.DialogProvider {
+ var list: List<PrivacyDialogV2.PrivacyElement>? = null
+ var manageApp: ((String, Int, Intent) -> Unit)? = null
+ var closeApp: ((String, Int) -> Unit)? = null
+ var openPrivacyDashboard: (() -> Unit)? = null
- override fun makeDialog(
- context: Context,
- list: List<PrivacyDialogV2.PrivacyElement>,
- manageApp: (String, Int, Intent) -> Unit,
- closeApp: (String, Int) -> Unit,
- openPrivacyDashboard: () -> Unit
- ): PrivacyDialogV2 {
- this.list = list
- this.manageApp = manageApp
- this.closeApp = closeApp
- this.openPrivacyDashboard = openPrivacyDashboard
- return dialog
+ override fun makeDialog(
+ context: Context,
+ list: List<PrivacyDialogV2.PrivacyElement>,
+ manageApp: (String, Int, Intent) -> Unit,
+ closeApp: (String, Int) -> Unit,
+ openPrivacyDashboard: () -> Unit
+ ): PrivacyDialogV2 {
+ this.list = list
+ this.manageApp = manageApp
+ this.closeApp = closeApp
+ this.openPrivacyDashboard = openPrivacyDashboard
+ return dialog
+ }
}
- }
@Before
fun setUp() {
@@ -144,7 +134,8 @@
nextUid = 0
setUpDefaultMockResponses()
- controller = PrivacyDialogControllerV2(
+ controller =
+ PrivacyDialogControllerV2(
permissionManager,
packageManager,
privacyItemController,
@@ -158,7 +149,7 @@
uiEventLogger,
dialogLaunchAnimator,
dialogProvider
- )
+ )
}
@After
@@ -197,7 +188,7 @@
verify(packageManager, never()).getApplicationInfoAsUser(anyString(), anyInt(), anyInt())
backgroundExecutor.runAllReady()
verify(packageManager, atLeastOnce())
- .getApplicationInfoAsUser(anyString(), anyInt(), anyInt())
+ .getApplicationInfoAsUser(anyString(), anyInt(), anyInt())
}
@Test
@@ -208,20 +199,25 @@
controller.showDialog(context)
exhaustExecutors()
- verify(dialogLaunchAnimator, never()).showFromView(any(), any(), any(), anyBoolean())
+ verify(dialogLaunchAnimator, never()).show(any(), any(), anyBoolean())
verify(dialog).show()
}
@Test
fun testShowDialogShowsDialogWithView() {
- val view = View(context)
+ val parent = LinearLayout(context)
+ val view =
+ object : View(context), LaunchableView {
+ override fun setShouldBlockVisibilityChanges(block: Boolean) {}
+ }
+ parent.addView(view)
val usage = createMockPermGroupUsage()
`when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(listOf(usage))
controller.showDialog(context, view)
exhaustExecutors()
- verify(dialogLaunchAnimator).showFromView(dialog, view)
+ verify(dialogLaunchAnimator).show(eq(dialog), any(), anyBoolean())
verify(dialog, never()).show()
}
@@ -276,7 +272,8 @@
@Test
fun testSingleElementInList() {
- val usage = createMockPermGroupUsage(
+ val usage =
+ createMockPermGroupUsage(
packageName = TEST_PACKAGE_NAME,
uid = generateUidForUser(USER_ID),
permissionGroupName = PERM_CAMERA,
@@ -285,7 +282,7 @@
isPhoneCall = false,
attributionTag = null,
proxyLabel = TEST_PROXY_LABEL
- )
+ )
`when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(listOf(usage))
controller.showDialog(context)
@@ -304,33 +301,38 @@
assertThat(list.get(0).isPhoneCall).isFalse()
assertThat(list.get(0).isService).isFalse()
assertThat(list.get(0).permGroupName).isEqualTo(PERM_CAMERA)
- assertThat(isIntentEqual(list.get(0).navigationIntent!!,
- controller.getDefaultManageAppPermissionsIntent(TEST_PACKAGE_NAME, USER_ID)))
- .isTrue()
+ assertThat(
+ isIntentEqual(
+ list.get(0).navigationIntent!!,
+ controller.getDefaultManageAppPermissionsIntent(TEST_PACKAGE_NAME, USER_ID)
+ )
+ )
+ .isTrue()
}
}
private fun isIntentEqual(actual: Intent, expected: Intent): Boolean {
return actual.action == expected.action &&
- actual.getStringExtra(Intent.EXTRA_PACKAGE_NAME) ==
+ actual.getStringExtra(Intent.EXTRA_PACKAGE_NAME) ==
expected.getStringExtra(Intent.EXTRA_PACKAGE_NAME) &&
- actual.getParcelableExtra(Intent.EXTRA_USER) as? UserHandle ==
+ actual.getParcelableExtra(Intent.EXTRA_USER) as? UserHandle ==
expected.getParcelableExtra(Intent.EXTRA_USER) as? UserHandle
}
@Test
fun testTwoElementsDifferentType_sorted() {
- val usage_camera = createMockPermGroupUsage(
+ val usage_camera =
+ createMockPermGroupUsage(
packageName = "${TEST_PACKAGE_NAME}_camera",
permissionGroupName = PERM_CAMERA
- )
- val usage_microphone = createMockPermGroupUsage(
+ )
+ val usage_microphone =
+ createMockPermGroupUsage(
packageName = "${TEST_PACKAGE_NAME}_microphone",
permissionGroupName = PERM_MICROPHONE
- )
- `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(
- listOf(usage_microphone, usage_camera)
- )
+ )
+ `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean()))
+ .thenReturn(listOf(usage_microphone, usage_camera))
controller.showDialog(context)
exhaustExecutors()
@@ -343,17 +345,12 @@
@Test
fun testTwoElementsSameType_oneActive() {
- val usage_active = createMockPermGroupUsage(
- packageName = "${TEST_PACKAGE_NAME}_active",
- isActive = true
- )
- val usage_recent = createMockPermGroupUsage(
- packageName = "${TEST_PACKAGE_NAME}_recent",
- isActive = false
- )
- `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(
- listOf(usage_recent, usage_active)
- )
+ val usage_active =
+ createMockPermGroupUsage(packageName = "${TEST_PACKAGE_NAME}_active", isActive = true)
+ val usage_recent =
+ createMockPermGroupUsage(packageName = "${TEST_PACKAGE_NAME}_recent", isActive = false)
+ `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean()))
+ .thenReturn(listOf(usage_recent, usage_active))
controller.showDialog(context)
exhaustExecutors()
@@ -364,19 +361,20 @@
@Test
fun testTwoElementsSameType_twoActive() {
- val usage_active = createMockPermGroupUsage(
+ val usage_active =
+ createMockPermGroupUsage(
packageName = "${TEST_PACKAGE_NAME}_active",
isActive = true,
lastAccessTimeMillis = 0L
- )
- val usage_active_moreRecent = createMockPermGroupUsage(
+ )
+ val usage_active_moreRecent =
+ createMockPermGroupUsage(
packageName = "${TEST_PACKAGE_NAME}_active_recent",
isActive = true,
lastAccessTimeMillis = 1L
- )
- `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(
- listOf(usage_active, usage_active_moreRecent)
- )
+ )
+ `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean()))
+ .thenReturn(listOf(usage_active, usage_active_moreRecent))
controller.showDialog(context)
exhaustExecutors()
assertThat(dialogProvider.list).hasSize(2)
@@ -386,24 +384,26 @@
@Test
fun testManyElementsSameType_bothRecent() {
- val usage_recent = createMockPermGroupUsage(
+ val usage_recent =
+ createMockPermGroupUsage(
packageName = "${TEST_PACKAGE_NAME}_recent",
isActive = false,
lastAccessTimeMillis = 0L
- )
- val usage_moreRecent = createMockPermGroupUsage(
+ )
+ val usage_moreRecent =
+ createMockPermGroupUsage(
packageName = "${TEST_PACKAGE_NAME}_moreRecent",
isActive = false,
lastAccessTimeMillis = 1L
- )
- val usage_mostRecent = createMockPermGroupUsage(
+ )
+ val usage_mostRecent =
+ createMockPermGroupUsage(
packageName = "${TEST_PACKAGE_NAME}_mostRecent",
isActive = false,
lastAccessTimeMillis = 2L
- )
- `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(
- listOf(usage_recent, usage_mostRecent, usage_moreRecent)
- )
+ )
+ `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean()))
+ .thenReturn(listOf(usage_recent, usage_mostRecent, usage_moreRecent))
controller.showDialog(context)
exhaustExecutors()
@@ -414,19 +414,12 @@
@Test
fun testMicAndCameraDisabled() {
- val usage_camera = createMockPermGroupUsage(
- permissionGroupName = PERM_CAMERA
- )
- val usage_microphone = createMockPermGroupUsage(
- permissionGroupName = PERM_MICROPHONE
- )
- val usage_location = createMockPermGroupUsage(
- permissionGroupName = PERM_LOCATION
- )
+ val usage_camera = createMockPermGroupUsage(permissionGroupName = PERM_CAMERA)
+ val usage_microphone = createMockPermGroupUsage(permissionGroupName = PERM_MICROPHONE)
+ val usage_location = createMockPermGroupUsage(permissionGroupName = PERM_LOCATION)
- `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(
- listOf(usage_camera, usage_location, usage_microphone)
- )
+ `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean()))
+ .thenReturn(listOf(usage_camera, usage_location, usage_microphone))
`when`(privacyItemController.micCameraAvailable).thenReturn(false)
controller.showDialog(context)
@@ -438,45 +431,29 @@
@Test
fun testLocationDisabled() {
- val usage_camera = createMockPermGroupUsage(
- permissionGroupName = PERM_CAMERA
- )
- val usage_microphone = createMockPermGroupUsage(
- permissionGroupName = PERM_MICROPHONE
- )
- val usage_location = createMockPermGroupUsage(
- permissionGroupName = PERM_LOCATION
- )
+ val usage_camera = createMockPermGroupUsage(permissionGroupName = PERM_CAMERA)
+ val usage_microphone = createMockPermGroupUsage(permissionGroupName = PERM_MICROPHONE)
+ val usage_location = createMockPermGroupUsage(permissionGroupName = PERM_LOCATION)
- `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(
- listOf(usage_camera, usage_location, usage_microphone)
- )
+ `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean()))
+ .thenReturn(listOf(usage_camera, usage_location, usage_microphone))
`when`(privacyItemController.locationAvailable).thenReturn(false)
controller.showDialog(context)
exhaustExecutors()
assertThat(dialogProvider.list).hasSize(2)
- dialogProvider.list?.forEach {
- assertThat(it.type).isNotEqualTo(PrivacyType.TYPE_LOCATION)
- }
+ dialogProvider.list?.forEach { assertThat(it.type).isNotEqualTo(PrivacyType.TYPE_LOCATION) }
}
@Test
fun testAllIndicatorsAvailable() {
- val usage_camera = createMockPermGroupUsage(
- permissionGroupName = PERM_CAMERA
- )
- val usage_microphone = createMockPermGroupUsage(
- permissionGroupName = PERM_MICROPHONE
- )
- val usage_location = createMockPermGroupUsage(
- permissionGroupName = PERM_LOCATION
- )
+ val usage_camera = createMockPermGroupUsage(permissionGroupName = PERM_CAMERA)
+ val usage_microphone = createMockPermGroupUsage(permissionGroupName = PERM_MICROPHONE)
+ val usage_location = createMockPermGroupUsage(permissionGroupName = PERM_LOCATION)
- `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(
- listOf(usage_camera, usage_location, usage_microphone)
- )
+ `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean()))
+ .thenReturn(listOf(usage_camera, usage_location, usage_microphone))
`when`(privacyItemController.micCameraAvailable).thenReturn(true)
`when`(privacyItemController.locationAvailable).thenReturn(true)
@@ -488,19 +465,12 @@
@Test
fun testNoIndicatorsAvailable() {
- val usage_camera = createMockPermGroupUsage(
- permissionGroupName = PERM_CAMERA
- )
- val usage_microphone = createMockPermGroupUsage(
- permissionGroupName = PERM_MICROPHONE
- )
- val usage_location = createMockPermGroupUsage(
- permissionGroupName = PERM_LOCATION
- )
+ val usage_camera = createMockPermGroupUsage(permissionGroupName = PERM_CAMERA)
+ val usage_microphone = createMockPermGroupUsage(permissionGroupName = PERM_MICROPHONE)
+ val usage_location = createMockPermGroupUsage(permissionGroupName = PERM_LOCATION)
- `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(
- listOf(usage_camera, usage_location, usage_microphone)
- )
+ `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean()))
+ .thenReturn(listOf(usage_camera, usage_location, usage_microphone))
`when`(privacyItemController.micCameraAvailable).thenReturn(false)
`when`(privacyItemController.locationAvailable).thenReturn(false)
@@ -512,11 +482,9 @@
@Test
fun testNotCurrentUser() {
- val usage_other = createMockPermGroupUsage(
- uid = generateUidForUser(ENT_USER_ID + 1)
- )
+ val usage_other = createMockPermGroupUsage(uid = generateUidForUser(ENT_USER_ID + 1))
`when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean()))
- .thenReturn(listOf(usage_other))
+ .thenReturn(listOf(usage_other))
controller.showDialog(context)
exhaustExecutors()
@@ -559,9 +527,7 @@
// Calls happen in
val usage = createMockPermGroupUsage(uid = SYSTEM_UID, isPhoneCall = true)
`when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(listOf(usage))
- `when`(userTracker.userProfiles).thenReturn(listOf(
- UserInfo(ENT_USER_ID, "", 0)
- ))
+ `when`(userTracker.userProfiles).thenReturn(listOf(UserInfo(ENT_USER_ID, "", 0)))
controller.showDialog(context)
exhaustExecutors()
@@ -577,8 +543,12 @@
exhaustExecutors()
dialogProvider.manageApp?.invoke(TEST_PACKAGE_NAME, USER_ID, TEST_INTENT)
- verify(uiEventLogger).log(PrivacyDialogEvent.PRIVACY_DIALOG_ITEM_CLICKED_TO_APP_SETTINGS,
- USER_ID, TEST_PACKAGE_NAME)
+ verify(uiEventLogger)
+ .log(
+ PrivacyDialogEvent.PRIVACY_DIALOG_ITEM_CLICKED_TO_APP_SETTINGS,
+ USER_ID,
+ TEST_PACKAGE_NAME
+ )
}
@Test
@@ -589,8 +559,12 @@
exhaustExecutors()
dialogProvider.closeApp?.invoke(TEST_PACKAGE_NAME, USER_ID)
- verify(uiEventLogger).log(PrivacyDialogEvent.PRIVACY_DIALOG_ITEM_CLICKED_TO_CLOSE_APP,
- USER_ID, TEST_PACKAGE_NAME)
+ verify(uiEventLogger)
+ .log(
+ PrivacyDialogEvent.PRIVACY_DIALOG_ITEM_CLICKED_TO_CLOSE_APP,
+ USER_ID,
+ TEST_PACKAGE_NAME
+ )
}
@Test
@@ -629,9 +603,13 @@
exhaustExecutors()
dialogProvider.list?.let { list ->
- assertThat(isIntentEqual(list.get(0).navigationIntent!!,
- controller.getDefaultManageAppPermissionsIntent(TEST_PACKAGE_NAME, USER_ID)))
- .isTrue()
+ assertThat(
+ isIntentEqual(
+ list.get(0).navigationIntent!!,
+ controller.getDefaultManageAppPermissionsIntent(TEST_PACKAGE_NAME, USER_ID)
+ )
+ )
+ .isTrue()
assertThat(list.get(0).isService).isFalse()
}
}
@@ -648,45 +626,58 @@
exhaustExecutors()
dialogProvider.list?.let { list ->
- assertThat(isIntentEqual(list.get(0).navigationIntent!!,
- controller.getDefaultManageAppPermissionsIntent(
- TEST_PACKAGE_NAME, ENT_USER_ID)))
- .isTrue()
+ assertThat(
+ isIntentEqual(
+ list.get(0).navigationIntent!!,
+ controller.getDefaultManageAppPermissionsIntent(
+ TEST_PACKAGE_NAME,
+ ENT_USER_ID
+ )
+ )
+ )
+ .isTrue()
assertThat(list.get(0).isService).isFalse()
}
}
@Test
fun testDefaultIntentOnInvalidAttributionTag() {
- val usage = createMockPermGroupUsage(
+ val usage =
+ createMockPermGroupUsage(
attributionTag = "INVALID_ATTRIBUTION_TAG",
proxyLabel = TEST_PROXY_LABEL
- )
+ )
`when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(listOf(usage))
controller.showDialog(context)
exhaustExecutors()
dialogProvider.list?.let { list ->
- assertThat(isIntentEqual(list.get(0).navigationIntent!!,
- controller.getDefaultManageAppPermissionsIntent(TEST_PACKAGE_NAME, USER_ID)))
- .isTrue()
+ assertThat(
+ isIntentEqual(
+ list.get(0).navigationIntent!!,
+ controller.getDefaultManageAppPermissionsIntent(TEST_PACKAGE_NAME, USER_ID)
+ )
+ )
+ .isTrue()
assertThat(list.get(0).isService).isFalse()
}
}
@Test
fun testServiceIntentOnCorrectSubAttribution() {
- val usage = createMockPermGroupUsage(
+ val usage =
+ createMockPermGroupUsage(
attributionTag = TEST_ATTRIBUTION_TAG,
attributionLabel = "TEST_LABEL"
- )
+ )
val activityInfo = createMockActivityInfo()
val resolveInfo = createMockResolveInfo(activityInfo)
`when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(listOf(usage))
- `when`(packageManager.resolveActivity(any(), any<ResolveInfoFlags>()))
- .thenAnswer { resolveInfo }
+ `when`(packageManager.resolveActivity(any(), any<ResolveInfoFlags>())).thenAnswer {
+ resolveInfo
+ }
controller.showDialog(context)
exhaustExecutors()
@@ -694,57 +685,61 @@
val navigationIntent = list.get(0).navigationIntent!!
assertThat(navigationIntent.action).isEqualTo(Intent.ACTION_MANAGE_PERMISSION_USAGE)
assertThat(navigationIntent.getStringExtra(Intent.EXTRA_PERMISSION_GROUP_NAME))
- .isEqualTo(PERM_CAMERA)
+ .isEqualTo(PERM_CAMERA)
assertThat(navigationIntent.getStringArrayExtra(Intent.EXTRA_ATTRIBUTION_TAGS))
- .isEqualTo(arrayOf(TEST_ATTRIBUTION_TAG.toString()))
+ .isEqualTo(arrayOf(TEST_ATTRIBUTION_TAG.toString()))
assertThat(navigationIntent.getBooleanExtra(Intent.EXTRA_SHOWING_ATTRIBUTION, false))
- .isTrue()
+ .isTrue()
assertThat(list.get(0).isService).isTrue()
}
}
@Test
fun testDefaultIntentOnMissingAttributionLabel() {
- val usage = createMockPermGroupUsage(
- attributionTag = TEST_ATTRIBUTION_TAG
- )
+ val usage = createMockPermGroupUsage(attributionTag = TEST_ATTRIBUTION_TAG)
val activityInfo = createMockActivityInfo()
val resolveInfo = createMockResolveInfo(activityInfo)
`when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(listOf(usage))
- `when`(packageManager.resolveActivity(any(), any<ResolveInfoFlags>()))
- .thenAnswer { resolveInfo }
+ `when`(packageManager.resolveActivity(any(), any<ResolveInfoFlags>())).thenAnswer {
+ resolveInfo
+ }
controller.showDialog(context)
exhaustExecutors()
dialogProvider.list?.let { list ->
- assertThat(isIntentEqual(list.get(0).navigationIntent!!,
- controller.getDefaultManageAppPermissionsIntent(TEST_PACKAGE_NAME, USER_ID)))
- .isTrue()
+ assertThat(
+ isIntentEqual(
+ list.get(0).navigationIntent!!,
+ controller.getDefaultManageAppPermissionsIntent(TEST_PACKAGE_NAME, USER_ID)
+ )
+ )
+ .isTrue()
assertThat(list.get(0).isService).isFalse()
}
}
@Test
fun testDefaultIntentOnIncorrectPermission() {
- val usage = createMockPermGroupUsage(
- attributionTag = TEST_ATTRIBUTION_TAG
- )
+ val usage = createMockPermGroupUsage(attributionTag = TEST_ATTRIBUTION_TAG)
- val activityInfo = createMockActivityInfo(
- permission = "INCORRECT_PERMISSION"
- )
+ val activityInfo = createMockActivityInfo(permission = "INCORRECT_PERMISSION")
val resolveInfo = createMockResolveInfo(activityInfo)
`when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(listOf(usage))
- `when`(packageManager.resolveActivity(any(), any<ResolveInfoFlags>()))
- .thenAnswer { resolveInfo }
+ `when`(packageManager.resolveActivity(any(), any<ResolveInfoFlags>())).thenAnswer {
+ resolveInfo
+ }
controller.showDialog(context)
exhaustExecutors()
dialogProvider.list?.let { list ->
- assertThat(isIntentEqual(list.get(0).navigationIntent!!,
- controller.getDefaultManageAppPermissionsIntent(TEST_PACKAGE_NAME, USER_ID)))
- .isTrue()
+ assertThat(
+ isIntentEqual(
+ list.get(0).navigationIntent!!,
+ controller.getDefaultManageAppPermissionsIntent(TEST_PACKAGE_NAME, USER_ID)
+ )
+ )
+ .isTrue()
assertThat(list.get(0).isService).isFalse()
}
}
@@ -758,15 +753,18 @@
`when`(appOpsController.isMicMuted).thenReturn(false)
`when`(packageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
- .thenAnswer { FakeApplicationInfo(it.getArgument(0)) }
+ .thenAnswer { FakeApplicationInfo(it.getArgument(0)) }
`when`(privacyItemController.locationAvailable).thenReturn(true)
`when`(privacyItemController.micCameraAvailable).thenReturn(true)
- `when`(userTracker.userProfiles).thenReturn(listOf(
- UserInfo(USER_ID, "", 0),
- UserInfo(ENT_USER_ID, "", UserInfo.FLAG_MANAGED_PROFILE)
- ))
+ `when`(userTracker.userProfiles)
+ .thenReturn(
+ listOf(
+ UserInfo(USER_ID, "", 0),
+ UserInfo(ENT_USER_ID, "", UserInfo.FLAG_MANAGED_PROFILE)
+ )
+ )
`when`(keyguardStateController.isUnlocked).thenReturn(true)
}
@@ -781,9 +779,7 @@
return user * UserHandle.PER_USER_RANGE + nextUid++
}
- private fun createMockResolveInfo(
- activityInfo: ActivityInfo? = null
- ): ResolveInfo {
+ private fun createMockResolveInfo(activityInfo: ActivityInfo? = null): ResolveInfo {
val resolveInfo = mock(ResolveInfo::class.java)
resolveInfo.activityInfo = activityInfo
return resolveInfo
@@ -822,4 +818,4 @@
`when`(usage.proxyLabel).thenReturn(proxyLabel)
return usage
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
index 6dc7a06..b24b877 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
@@ -63,13 +63,13 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.settingslib.wifi.WifiUtils;
import com.android.settingslib.wifi.dpp.WifiDppIntentHelper;
-import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.connectivity.AccessPointController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.LocationController;
@@ -738,6 +738,44 @@
}
@Test
+ public void onWifiScan_isWifiEnabledFalse_callbackOnWifiScanFalse() {
+ reset(mInternetDialogCallback);
+ when(mWifiStateWorker.isWifiEnabled()).thenReturn(false);
+
+ mInternetDialogController.onWifiScan(true);
+
+ verify(mInternetDialogCallback).onWifiScan(false);
+ }
+
+ @Test
+ public void onWifiScan_isDeviceLockedTrue_callbackOnWifiScanFalse() {
+ reset(mInternetDialogCallback);
+ when(mKeyguardStateController.isUnlocked()).thenReturn(false);
+
+ mInternetDialogController.onWifiScan(true);
+
+ verify(mInternetDialogCallback).onWifiScan(false);
+ }
+
+ @Test
+ public void onWifiScan_onWifiScanFalse_callbackOnWifiScanFalse() {
+ reset(mInternetDialogCallback);
+
+ mInternetDialogController.onWifiScan(false);
+
+ verify(mInternetDialogCallback).onWifiScan(false);
+ }
+
+ @Test
+ public void onWifiScan_onWifiScanTrue_callbackOnWifiScanTrue() {
+ reset(mInternetDialogCallback);
+
+ mInternetDialogController.onWifiScan(true);
+
+ verify(mInternetDialogCallback).onWifiScan(true);
+ }
+
+ @Test
public void setMergedCarrierWifiEnabledIfNeed_carrierProvisionsEnabled_doNothing() {
when(mCarrierConfigTracker.getCarrierProvisionsWifiMergedNetworksBool(SUB_ID))
.thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
index 039e58a..916bb79 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
@@ -6,12 +6,10 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -33,9 +31,9 @@
import com.android.dx.mockito.inline.extended.ExtendedMockito;
import com.android.internal.logging.UiEventLogger;
import com.android.settingslib.wifi.WifiEnterpriseRestrictionUtils;
-import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.DialogLaunchAnimator;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
@@ -48,7 +46,6 @@
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
-import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.MockitoSession;
@@ -613,66 +610,21 @@
}
@Test
- public void showProgressBar_wifiDisabled_hideProgressBar() {
- Mockito.reset(mHandler);
- when(mInternetDialogController.isWifiEnabled()).thenReturn(false);
+ public void onWifiScan_isScanTrue_setProgressBarVisibleTrue() {
+ mInternetDialog.mIsProgressBarVisible = false;
- mInternetDialog.showProgressBar();
+ mInternetDialog.onWifiScan(true);
- assertThat(mInternetDialog.mIsProgressBarVisible).isFalse();
- verify(mHandler, never()).postDelayed(any(Runnable.class), anyLong());
+ assertThat(mInternetDialog.mIsProgressBarVisible).isTrue();
}
@Test
- public void showProgressBar_deviceLocked_hideProgressBar() {
- Mockito.reset(mHandler);
- when(mInternetDialogController.isDeviceLocked()).thenReturn(true);
+ public void onWifiScan_isScanFalse_setProgressBarVisibleFalse() {
+ mInternetDialog.mIsProgressBarVisible = true;
- mInternetDialog.showProgressBar();
+ mInternetDialog.onWifiScan(false);
assertThat(mInternetDialog.mIsProgressBarVisible).isFalse();
- verify(mHandler, never()).postDelayed(any(Runnable.class), anyLong());
- }
-
- @Test
- public void showProgressBar_wifiEnabledWithWifiEntry_showProgressBarThenHide() {
- Mockito.reset(mHandler);
- when(mInternetDialogController.isWifiEnabled()).thenReturn(true);
-
- mInternetDialog.showProgressBar();
-
- // Show progress bar
- assertThat(mInternetDialog.mIsProgressBarVisible).isTrue();
-
- ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
- verify(mHandler).postDelayed(runnableCaptor.capture(),
- eq(InternetDialog.PROGRESS_DELAY_MS));
- runnableCaptor.getValue().run();
-
- // Then hide progress bar
- assertThat(mInternetDialog.mIsProgressBarVisible).isFalse();
- }
-
- @Test
- public void showProgressBar_wifiEnabledWithoutWifiEntries_showProgressBarThenHideSearch() {
- Mockito.reset(mHandler);
- when(mInternetDialogController.isWifiEnabled()).thenReturn(true);
- mInternetDialog.mConnectedWifiEntry = null;
- mInternetDialog.mWifiEntriesCount = 0;
-
- mInternetDialog.showProgressBar();
-
- // Show progress bar
- assertThat(mInternetDialog.mIsProgressBarVisible).isTrue();
-
- ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
- verify(mHandler).postDelayed(runnableCaptor.capture(),
- eq(InternetDialog.PROGRESS_DELAY_MS));
- runnableCaptor.getValue().run();
-
- // Then hide searching sub-title only
- assertThat(mInternetDialog.mIsProgressBarVisible).isTrue();
- assertThat(mInternetDialog.mIsSearchingHidden).isTrue();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index 657f912..e572dcc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -23,6 +23,8 @@
import static com.google.common.truth.Truth.assertThat;
+import static kotlinx.coroutines.flow.FlowKt.emptyFlow;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyFloat;
@@ -37,8 +39,6 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import static kotlinx.coroutines.flow.FlowKt.emptyFlow;
-
import android.annotation.IdRes;
import android.content.ContentResolver;
import android.content.res.Configuration;
@@ -86,6 +86,7 @@
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository;
import com.android.systemui.common.ui.view.LongPressHandlingView;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor;
import com.android.systemui.doze.DozeLog;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FakeFeatureFlagsClassic;
@@ -402,6 +403,10 @@
mPowerInteractor = keyguardInteractorDeps.getPowerInteractor();
when(mKeyguardTransitionInteractor.isInTransitionToStateWhere(any())).thenReturn(
StateFlowKt.MutableStateFlow(false));
+ DeviceEntryUdfpsInteractor deviceEntryUdfpsInteractor =
+ mock(DeviceEntryUdfpsInteractor.class);
+ when(deviceEntryUdfpsInteractor.isUdfpsSupported()).thenReturn(emptyFlow());
+
mShadeInteractor = new ShadeInteractorImpl(
mTestScope.getBackgroundScope(),
new FakeDeviceProvisioningRepository(),
@@ -418,7 +423,9 @@
new SharedNotificationContainerInteractor(
new FakeConfigurationRepository(),
mContext,
- new ResourcesSplitShadeStateController()
+ new ResourcesSplitShadeStateController(),
+ mKeyguardInteractor,
+ deviceEntryUdfpsInteractor
),
mShadeRepository
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
index 5ffbe65..9d8b214 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
@@ -23,6 +23,8 @@
import static com.google.common.truth.Truth.assertThat;
+import static kotlinx.coroutines.flow.FlowKt.emptyFlow;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
@@ -54,6 +56,7 @@
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository;
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FakeFeatureFlagsClassic;
import com.android.systemui.keyguard.KeyguardViewMediator;
@@ -235,6 +238,11 @@
mKeyguardSecurityModel,
mSelectedUserInteractor,
powerInteractor);
+
+ DeviceEntryUdfpsInteractor deviceEntryUdfpsInteractor =
+ mock(DeviceEntryUdfpsInteractor.class);
+ when(deviceEntryUdfpsInteractor.isUdfpsSupported()).thenReturn(emptyFlow());
+
mShadeInteractor = new ShadeInteractorImpl(
mTestScope.getBackgroundScope(),
new FakeDeviceProvisioningRepository(),
@@ -251,7 +259,9 @@
new SharedNotificationContainerInteractor(
configurationRepository,
mContext,
- new ResourcesSplitShadeStateController()),
+ new ResourcesSplitShadeStateController(),
+ keyguardInteractor,
+ deviceEntryUdfpsInteractor),
shadeRepository
)
);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
index e723d7d..eb5633b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
@@ -22,6 +22,7 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import static kotlinx.coroutines.flow.FlowKt.emptyFlow;
import static kotlinx.coroutines.test.TestCoroutineDispatchersKt.StandardTestDispatcher;
import android.content.res.Resources;
@@ -41,6 +42,7 @@
import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository;
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository;
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FakeFeatureFlagsClassic;
import com.android.systemui.flags.FeatureFlags;
@@ -275,6 +277,10 @@
ResourcesSplitShadeStateController splitShadeStateController =
new ResourcesSplitShadeStateController();
+ DeviceEntryUdfpsInteractor deviceEntryUdfpsInteractor =
+ mock(DeviceEntryUdfpsInteractor.class);
+ when(deviceEntryUdfpsInteractor.isUdfpsSupported()).thenReturn(emptyFlow());
+
mShadeInteractor = new ShadeInteractorImpl(
mTestScope.getBackgroundScope(),
deviceProvisioningRepository,
@@ -291,7 +297,9 @@
new SharedNotificationContainerInteractor(
configurationRepository,
mContext,
- splitShadeStateController),
+ splitShadeStateController,
+ keyguardInteractor,
+ deviceEntryUdfpsInteractor),
mShadeRepository
)
);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
index dff91dd..f25ce0a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
@@ -27,6 +27,7 @@
import com.android.systemui.classifier.FalsingCollectorFake
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor;
import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.FakeCommandQueue
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
@@ -53,6 +54,7 @@
import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository
import com.android.systemui.util.mockito.mock
+import kotlinx.coroutines.flow.emptyFlow
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
@@ -83,6 +85,7 @@
FromPrimaryBouncerTransitionInteractor
@Mock lateinit var interactionJankMonitor: InteractionJankMonitor
@Mock lateinit var mockDarkAnimator: ObjectAnimator
+ @Mock lateinit var deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor
private lateinit var controller: StatusBarStateControllerImpl
private lateinit var uiEventLogger: UiEventLoggerFake
@@ -164,6 +167,8 @@
mock(),
powerInteractor
)
+
+ whenever(deviceEntryUdfpsInteractor.isUdfpsSupported).thenReturn(emptyFlow())
shadeInteractor =
ShadeInteractorImpl(
testScope.backgroundScope,
@@ -181,7 +186,9 @@
SharedNotificationContainerInteractor(
configurationRepository,
mContext,
- ResourcesSplitShadeStateController()
+ ResourcesSplitShadeStateController(),
+ keyguardInteractor,
+ deviceEntryUdfpsInteractor,
),
shadeRepository,
)
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 3fef1d9..5bc75e8 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
@@ -148,6 +148,24 @@
}
@Test
+ fun onWifiEntriesChanged_reasonIsScanResults_fireWifiScanCallbackFalse() {
+ controller.addAccessPointCallback(callback)
+
+ controller.onWifiEntriesChanged(WifiPickerTracker.WIFI_ENTRIES_CHANGED_REASON_SCAN_RESULTS)
+
+ verify(callback).onWifiScan(false)
+ }
+
+ @Test
+ fun onScanRequested_fireWifiScanCallbackTrue() {
+ controller.addAccessPointCallback(callback)
+
+ controller.onScanRequested()
+
+ verify(callback).onWifiScan(true)
+ }
+
+ @Test
fun testOnNumSavedNetworksChangedDoesntTriggerCallback() {
controller.addAccessPointCallback(callback)
controller.onNumSavedNetworksChanged()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
index bfa03ee..8cf64a5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
@@ -48,7 +48,6 @@
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.ArrayMap;
-import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -56,6 +55,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.log.LogAssertKt;
import com.android.systemui.statusbar.NotificationInteractionTracker;
import com.android.systemui.statusbar.RankingBuilder;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
@@ -76,6 +76,7 @@
import com.android.systemui.statusbar.notification.collection.notifcollection.CollectionReadyForBuildListener;
import com.android.systemui.util.time.FakeSystemClock;
+import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -129,10 +130,6 @@
private Map<String, Integer> mNextIdMap = new ArrayMap<>();
private int mNextRank = 0;
- private Log.TerribleFailureHandler mOldWtfHandler = null;
- private Log.TerribleFailure mLastWtf = null;
- private int mWtfCount = 0;
-
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
@@ -1756,20 +1753,19 @@
mListBuilder.addPreGroupFilter(filter);
mListBuilder.addOnBeforeTransformGroupsListener(listener);
- interceptWtfs();
-
// WHEN we try to run the pipeline and the filter is invalidated exactly
// MAX_CONSECUTIVE_REENTRANT_REBUILDS times,
addNotif(0, PACKAGE_2);
invalidator.setInvalidationCount(MAX_CONSECUTIVE_REENTRANT_REBUILDS);
- dispatchBuild();
- runWhileScheduledUpTo(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2);
// THEN an exception is NOT thrown directly, but a WTF IS logged.
- expectWtfs(MAX_CONSECUTIVE_REENTRANT_REBUILDS);
+ LogAssertKt.assertLogsWtfs(() -> {
+ dispatchBuild();
+ runWhileScheduledUpTo(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2);
+ });
}
- @Test(expected = IllegalStateException.class)
+ @Test
public void testOutOfOrderPreGroupFilterInvalidationThrowsAfterTooManyRuns() {
// GIVEN a PreGroupNotifFilter that gets invalidated during the grouping stage,
NotifFilter filter = new PackageFilter(PACKAGE_1);
@@ -1778,20 +1774,20 @@
mListBuilder.addPreGroupFilter(filter);
mListBuilder.addOnBeforeTransformGroupsListener(listener);
- interceptWtfs();
-
// WHEN we try to run the pipeline and the filter is invalidated more than
// MAX_CONSECUTIVE_REENTRANT_REBUILDS times,
- addNotif(0, PACKAGE_2);
- invalidator.setInvalidationCount(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 1);
- dispatchBuild();
- try {
- runWhileScheduledUpTo(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2);
- } finally {
- expectWtfs(MAX_CONSECUTIVE_REENTRANT_REBUILDS);
- }
// THEN an exception IS thrown.
+
+ addNotif(0, PACKAGE_2);
+ invalidator.setInvalidationCount(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 1);
+
+ LogAssertKt.assertLogsWtfs(() -> {
+ Assert.assertThrows(IllegalStateException.class, () -> {
+ dispatchBuild();
+ runWhileScheduledUpTo(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2);
+ });
+ });
}
@Test
@@ -1803,26 +1799,30 @@
mListBuilder.addPreGroupFilter(filter);
mListBuilder.addOnBeforeTransformGroupsListener(listener);
- interceptWtfs();
-
// WHEN we try to run the pipeline and the filter is invalidated
// MAX_CONSECUTIVE_REENTRANT_REBUILDS times, the pipeline runs for a non-reentrant reason,
// and then the filter is invalidated MAX_CONSECUTIVE_REENTRANT_REBUILDS times again,
- addNotif(0, PACKAGE_2);
- invalidator.setInvalidationCount(MAX_CONSECUTIVE_REENTRANT_REBUILDS);
- dispatchBuild();
- runWhileScheduledUpTo(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2);
- invalidator.setInvalidationCount(MAX_CONSECUTIVE_REENTRANT_REBUILDS);
- // Note: dispatchBuild itself triggers a non-reentrant pipeline run.
- dispatchBuild();
- runWhileScheduledUpTo(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2);
// THEN an exception is NOT thrown, but WTFs ARE logged.
- expectWtfs(MAX_CONSECUTIVE_REENTRANT_REBUILDS * 2);
+
+ addNotif(0, PACKAGE_2);
+
+ invalidator.setInvalidationCount(MAX_CONSECUTIVE_REENTRANT_REBUILDS);
+ LogAssertKt.assertLogsWtfs(() -> {
+ dispatchBuild();
+ runWhileScheduledUpTo(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2);
+ });
+
+ invalidator.setInvalidationCount(MAX_CONSECUTIVE_REENTRANT_REBUILDS);
+ LogAssertKt.assertLogsWtfs(() -> {
+ // Note: dispatchBuild itself triggers a non-reentrant pipeline run.
+ dispatchBuild();
+ runWhileScheduledUpTo(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2);
+ });
}
@Test
- public void testOutOfOrderPrompterInvalidationDoesNotThrowBeforeTooManyRuns() {
+ public void testOutOfOrderPromoterInvalidationDoesNotThrowBeforeTooManyRuns() {
// GIVEN a NotifPromoter that gets invalidated during the sorting stage,
NotifPromoter promoter = new IdPromoter(47);
CountingInvalidator invalidator = new CountingInvalidator(promoter);
@@ -1830,22 +1830,22 @@
mListBuilder.addPromoter(promoter);
mListBuilder.addOnBeforeSortListener(listener);
- interceptWtfs();
-
// WHEN we try to run the pipeline and the promoter is invalidated exactly
// MAX_CONSECUTIVE_REENTRANT_REBUILDS times,
- addNotif(0, PACKAGE_1);
- invalidator.setInvalidationCount(MAX_CONSECUTIVE_REENTRANT_REBUILDS);
- dispatchBuild();
- runWhileScheduledUpTo(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2);
// THEN an exception is NOT thrown directly, but a WTF IS logged.
- expectWtfs(MAX_CONSECUTIVE_REENTRANT_REBUILDS);
+ addNotif(0, PACKAGE_1);
+ invalidator.setInvalidationCount(MAX_CONSECUTIVE_REENTRANT_REBUILDS);
+
+ LogAssertKt.assertLogsWtfs(() -> {
+ dispatchBuild();
+ runWhileScheduledUpTo(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2);
+ });
}
- @Test(expected = IllegalStateException.class)
- public void testOutOfOrderPrompterInvalidationThrowsAfterTooManyRuns() {
+ @Test
+ public void testOutOfOrderPromoterInvalidationThrowsAfterTooManyRuns() {
// GIVEN a NotifPromoter that gets invalidated during the sorting stage,
NotifPromoter promoter = new IdPromoter(47);
CountingInvalidator invalidator = new CountingInvalidator(promoter);
@@ -1853,20 +1853,20 @@
mListBuilder.addPromoter(promoter);
mListBuilder.addOnBeforeSortListener(listener);
- interceptWtfs();
-
// WHEN we try to run the pipeline and the promoter is invalidated more than
// MAX_CONSECUTIVE_REENTRANT_REBUILDS times,
- addNotif(0, PACKAGE_1);
- invalidator.setInvalidationCount(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 1);
- dispatchBuild();
- try {
- runWhileScheduledUpTo(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2);
- } finally {
- expectWtfs(MAX_CONSECUTIVE_REENTRANT_REBUILDS);
- }
// THEN an exception IS thrown.
+
+ addNotif(0, PACKAGE_1);
+ invalidator.setInvalidationCount(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 1);
+
+ LogAssertKt.assertLogsWtfs(() -> {
+ Assert.assertThrows(IllegalStateException.class, () -> {
+ dispatchBuild();
+ runWhileScheduledUpTo(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2);
+ });
+ });
}
@Test
@@ -1878,20 +1878,21 @@
mListBuilder.setComparators(singletonList(comparator));
mListBuilder.addOnBeforeRenderListListener(listener);
- interceptWtfs();
-
// WHEN we try to run the pipeline and the comparator is invalidated exactly
// MAX_CONSECUTIVE_REENTRANT_REBUILDS times,
- addNotif(0, PACKAGE_2);
- invalidator.setInvalidationCount(MAX_CONSECUTIVE_REENTRANT_REBUILDS);
- dispatchBuild();
- runWhileScheduledUpTo(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2);
// THEN an exception is NOT thrown directly, but a WTF IS logged.
- expectWtfs(MAX_CONSECUTIVE_REENTRANT_REBUILDS);
+
+ addNotif(0, PACKAGE_2);
+ invalidator.setInvalidationCount(MAX_CONSECUTIVE_REENTRANT_REBUILDS);
+
+ LogAssertKt.assertLogsWtfs(() -> {
+ dispatchBuild();
+ runWhileScheduledUpTo(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2);
+ });
}
- @Test(expected = IllegalStateException.class)
+ @Test
public void testOutOfOrderComparatorInvalidationThrowsAfterTooManyRuns() {
// GIVEN a NotifComparator that gets invalidated during the finalizing stage,
NotifComparator comparator = new HypeComparator(PACKAGE_1);
@@ -1900,16 +1901,20 @@
mListBuilder.setComparators(singletonList(comparator));
mListBuilder.addOnBeforeRenderListListener(listener);
- interceptWtfs();
-
// WHEN we try to run the pipeline and the comparator is invalidated more than
// MAX_CONSECUTIVE_REENTRANT_REBUILDS times,
- addNotif(0, PACKAGE_2);
- invalidator.setInvalidationCount(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 1);
- dispatchBuild();
- runWhileScheduledUpTo(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2);
// THEN an exception IS thrown.
+
+ addNotif(0, PACKAGE_2);
+ invalidator.setInvalidationCount(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 1);
+
+ LogAssertKt.assertLogsWtfs(() -> {
+ Assert.assertThrows(IllegalStateException.class, () -> {
+ dispatchBuild();
+ runWhileScheduledUpTo(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2);
+ });
+ });
}
@Test
@@ -1921,20 +1926,21 @@
mListBuilder.addFinalizeFilter(filter);
mListBuilder.addOnBeforeRenderListListener(listener);
- interceptWtfs();
-
// WHEN we try to run the pipeline and the PreRenderFilter is invalidated exactly
// MAX_CONSECUTIVE_REENTRANT_REBUILDS times,
- addNotif(0, PACKAGE_2);
- invalidator.setInvalidationCount(MAX_CONSECUTIVE_REENTRANT_REBUILDS);
- dispatchBuild();
- runWhileScheduledUpTo(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2);
// THEN an exception is NOT thrown directly, but a WTF IS logged.
- expectWtfs(MAX_CONSECUTIVE_REENTRANT_REBUILDS);
+
+ addNotif(0, PACKAGE_2);
+ invalidator.setInvalidationCount(MAX_CONSECUTIVE_REENTRANT_REBUILDS);
+
+ LogAssertKt.assertLogsWtfs(() -> {
+ dispatchBuild();
+ runWhileScheduledUpTo(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2);
+ });
}
- @Test(expected = IllegalStateException.class)
+ @Test
public void testOutOfOrderPreRenderFilterInvalidationThrowsAfterTooManyRuns() {
// GIVEN a PreRenderNotifFilter that gets invalidated during the finalizing stage,
NotifFilter filter = new PackageFilter(PACKAGE_1);
@@ -1943,59 +1949,22 @@
mListBuilder.addFinalizeFilter(filter);
mListBuilder.addOnBeforeRenderListListener(listener);
- interceptWtfs();
-
// WHEN we try to run the pipeline and the PreRenderFilter is invalidated more than
// MAX_CONSECUTIVE_REENTRANT_REBUILDS times,
- addNotif(0, PACKAGE_2);
- invalidator.setInvalidationCount(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 1);
- dispatchBuild();
- try {
- runWhileScheduledUpTo(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2);
- } finally {
- expectWtfs(MAX_CONSECUTIVE_REENTRANT_REBUILDS);
- }
// THEN an exception IS thrown.
- }
- private void interceptWtfs() {
- assertNull(mOldWtfHandler);
+ addNotif(0, PACKAGE_2);
+ invalidator.setInvalidationCount(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 1);
- mLastWtf = null;
- mWtfCount = 0;
-
- mOldWtfHandler = Log.setWtfHandler((tag, e, system) -> {
- Log.e("ShadeListBuilderTest", "Observed WTF: " + e);
- mLastWtf = e;
- mWtfCount++;
+ LogAssertKt.assertLogsWtfs(() -> {
+ Assert.assertThrows(IllegalStateException.class, () -> {
+ dispatchBuild();
+ runWhileScheduledUpTo(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2);
+ });
});
}
- private void expectNoWtfs() {
- assertNull(expectWtfs(0));
- }
-
- private Log.TerribleFailure expectWtf() {
- return expectWtfs(1);
- }
-
- private Log.TerribleFailure expectWtfs(int expectedWtfCount) {
- assertNotNull(mOldWtfHandler);
-
- Log.setWtfHandler(mOldWtfHandler);
- mOldWtfHandler = null;
-
- Log.TerribleFailure wtf = mLastWtf;
- int wtfCount = mWtfCount;
-
- mLastWtf = null;
- mWtfCount = 0;
-
- assertEquals(expectedWtfCount, wtfCount);
- return wtf;
- }
-
@Test
public void testStableOrdering() {
mStabilityManager.setAllowEntryReordering(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorTest.kt
index a07b570..327a07d6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorTest.kt
@@ -20,57 +20,86 @@
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.FakeConfigurationRepository
+import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository
+import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
+import com.android.systemui.kosmos.testScope
import com.android.systemui.res.R
-import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
-import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
class SharedNotificationContainerInteractorTest : SysuiTestCase() {
- private lateinit var configurationRepository: FakeConfigurationRepository
- private lateinit var underTest: SharedNotificationContainerInteractor
-
- @Before
- fun setUp() {
- configurationRepository = FakeConfigurationRepository()
- underTest =
- SharedNotificationContainerInteractor(
- configurationRepository,
- mContext,
- ResourcesSplitShadeStateController()
- )
- }
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val keyguardRepository = kosmos.fakeKeyguardRepository
+ private val configurationRepository = kosmos.fakeConfigurationRepository
+ private val fingerprintPropertyRepository = kosmos.fingerprintPropertyRepository
+ private val underTest = kosmos.sharedNotificationContainerInteractor
@Test
- fun validateConfigValues() = runTest {
- overrideResource(R.bool.config_use_split_notification_shade, true)
- overrideResource(R.bool.config_use_large_screen_shade_header, false)
- overrideResource(R.dimen.notification_panel_margin_horizontal, 0)
- overrideResource(R.dimen.notification_panel_margin_bottom, 10)
- overrideResource(R.dimen.notification_panel_margin_top, 10)
- overrideResource(R.dimen.large_screen_shade_header_height, 0)
- overrideResource(R.dimen.keyguard_split_shade_top_margin, 55)
+ fun validateConfigValues() =
+ testScope.runTest {
+ overrideResource(R.bool.config_use_split_notification_shade, true)
+ overrideResource(R.bool.config_use_large_screen_shade_header, false)
+ overrideResource(R.dimen.notification_panel_margin_horizontal, 0)
+ overrideResource(R.dimen.notification_panel_margin_bottom, 10)
+ overrideResource(R.dimen.notification_panel_margin_top, 10)
+ overrideResource(R.dimen.large_screen_shade_header_height, 0)
+ overrideResource(R.dimen.keyguard_split_shade_top_margin, 55)
- val dimens = collectLastValue(underTest.configurationBasedDimensions)
+ val dimens = collectLastValue(underTest.configurationBasedDimensions)
- configurationRepository.onAnyConfigurationChange()
- runCurrent()
+ configurationRepository.onAnyConfigurationChange()
+ runCurrent()
- val lastDimens = dimens()!!
+ val lastDimens = dimens()!!
- assertThat(lastDimens.useSplitShade).isTrue()
- assertThat(lastDimens.useLargeScreenHeader).isFalse()
- assertThat(lastDimens.marginHorizontal).isEqualTo(0)
- assertThat(lastDimens.marginBottom).isGreaterThan(0)
- assertThat(lastDimens.marginTop).isGreaterThan(0)
- assertThat(lastDimens.marginTopLargeScreen).isEqualTo(0)
- assertThat(lastDimens.keyguardSplitShadeTopMargin).isEqualTo(55)
- }
+ assertThat(lastDimens.useSplitShade).isTrue()
+ assertThat(lastDimens.useLargeScreenHeader).isFalse()
+ assertThat(lastDimens.marginHorizontal).isEqualTo(0)
+ assertThat(lastDimens.marginBottom).isGreaterThan(0)
+ assertThat(lastDimens.marginTop).isGreaterThan(0)
+ assertThat(lastDimens.marginTopLargeScreen).isEqualTo(0)
+ assertThat(lastDimens.keyguardSplitShadeTopMargin).isEqualTo(55)
+ }
+
+ @Test
+ fun useExtraShelfSpaceIsTrueWithUdfps() =
+ testScope.runTest {
+ val useExtraShelfSpace by collectLastValue(underTest.useExtraShelfSpace)
+
+ keyguardRepository.ambientIndicationVisible.value = true
+ fingerprintPropertyRepository.supportsUdfps()
+
+ assertThat(useExtraShelfSpace).isEqualTo(true)
+ }
+
+ @Test
+ fun useExtraShelfSpaceIsTrueWithRearFpsAndNoAmbientIndicationArea() =
+ testScope.runTest {
+ val useExtraShelfSpace by collectLastValue(underTest.useExtraShelfSpace)
+
+ keyguardRepository.ambientIndicationVisible.value = false
+ fingerprintPropertyRepository.supportsRearFps()
+
+ assertThat(useExtraShelfSpace).isEqualTo(true)
+ }
+
+ @Test
+ fun useExtraShelfSpaceIsFalseWithRearFpsAndAmbientIndicationArea() =
+ testScope.runTest {
+ val useExtraShelfSpace by collectLastValue(underTest.useExtraShelfSpace)
+
+ keyguardRepository.ambientIndicationVisible.value = true
+ fingerprintPropertyRepository.supportsRearFps()
+
+ assertThat(useExtraShelfSpace).isEqualTo(false)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
index f0205b3..36a4712 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
@@ -332,8 +332,8 @@
fun maxNotificationsOnLockscreen() =
testScope.runTest {
var notificationCount = 10
- val maxNotifications by
- collectLastValue(underTest.getMaxNotifications { notificationCount })
+ val calculateSpace = { space: Float, useExtraShelfSpace: Boolean -> notificationCount }
+ val maxNotifications by collectLastValue(underTest.getMaxNotifications(calculateSpace))
showLockscreen()
@@ -355,8 +355,8 @@
fun maxNotificationsOnLockscreen_DoesNotUpdateWhenUserInteracting() =
testScope.runTest {
var notificationCount = 10
- val maxNotifications by
- collectLastValue(underTest.getMaxNotifications { notificationCount })
+ val calculateSpace = { space: Float, useExtraShelfSpace: Boolean -> notificationCount }
+ val maxNotifications by collectLastValue(underTest.getMaxNotifications(calculateSpace))
showLockscreen()
@@ -390,7 +390,8 @@
@Test
fun maxNotificationsOnShade() =
testScope.runTest {
- val maxNotifications by collectLastValue(underTest.getMaxNotifications { 10 })
+ val calculateSpace = { space: Float, useExtraShelfSpace: Boolean -> 10 }
+ val maxNotifications by collectLastValue(underTest.getMaxNotifications(calculateSpace))
// Show lockscreen with shade expanded
showLockscreenWithShadeExpanded()
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 5b9b390..b217195 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -29,6 +29,8 @@
import static com.google.common.truth.Truth.assertThat;
+import static kotlinx.coroutines.flow.FlowKt.emptyFlow;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -97,6 +99,7 @@
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository;
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.flags.FakeFeatureFlagsClassic;
@@ -465,6 +468,10 @@
ResourcesSplitShadeStateController splitShadeStateController =
new ResourcesSplitShadeStateController();
+ DeviceEntryUdfpsInteractor deviceEntryUdfpsInteractor =
+ mock(DeviceEntryUdfpsInteractor.class);
+ when(deviceEntryUdfpsInteractor.isUdfpsSupported()).thenReturn(emptyFlow());
+
mShadeInteractor =
new ShadeInteractorImpl(
mTestScope.getBackgroundScope(),
@@ -481,7 +488,9 @@
new SharedNotificationContainerInteractor(
configurationRepository,
mContext,
- splitShadeStateController),
+ splitShadeStateController,
+ keyguardInteractor,
+ deviceEntryUdfpsInteractor),
shadeRepository
)
);
diff --git a/packages/SystemUI/tests/utils/src/android/graphics/drawable/TestStubDrawable.kt b/packages/SystemUI/tests/utils/src/android/graphics/drawable/TestStubDrawable.kt
new file mode 100644
index 0000000..b88f302
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/android/graphics/drawable/TestStubDrawable.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics.drawable
+
+import android.graphics.Canvas
+import android.graphics.ColorFilter
+import android.graphics.PixelFormat
+
+/**
+ * Stub drawable that does nothing. It's to be used in tests as a mock drawable and checked for the
+ * same instance
+ */
+class TestStubDrawable : Drawable() {
+
+ override fun draw(canvas: Canvas) = Unit
+ override fun setAlpha(alpha: Int) = Unit
+ override fun setColorFilter(colorFilter: ColorFilter?) = Unit
+ override fun getOpacity(): Int = PixelFormat.UNKNOWN
+
+ override fun equals(other: Any?): Boolean = this === other
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index 0e7c662..c5d745a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -121,6 +121,8 @@
override val lastRootViewTapPosition: MutableStateFlow<Point?> = MutableStateFlow(null)
+ override val ambientIndicationVisible: MutableStateFlow<Boolean> = MutableStateFlow(false)
+
override fun setQuickSettingsVisible(isVisible: Boolean) {
_isQuickSettingsVisible.value = isVisible
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/log/LogAssert.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/log/LogAssert.kt
index 10f9346..6ccb3bc 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/log/LogAssert.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/log/LogAssert.kt
@@ -59,6 +59,17 @@
): TerribleFailureLog =
assertLogsWtf(message = message, allowMultiple = allowMultiple) { loggingRunnable.run() }
+fun assertLogsWtfs(
+ message: String = "Expected Log.wtf to be called once or more",
+ loggingBlock: () -> Unit,
+): TerribleFailureLog = assertLogsWtf(message, allowMultiple = true, loggingBlock)
+
+@JvmOverloads
+fun assertLogsWtfs(
+ message: String = "Expected Log.wtf to be called once or more",
+ loggingRunnable: Runnable,
+): TerribleFailureLog = assertLogsWtfs(message) { loggingRunnable.run() }
+
/** The data passed to [TerribleFailureHandler.onTerribleFailure] */
data class TerribleFailureLog(
val tag: String,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/QuickSettingsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/QuickSettingsKosmos.kt
index 1cb2587..6332c1a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/QuickSettingsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/QuickSettingsKosmos.kt
@@ -19,9 +19,14 @@
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.systemui.InstanceIdSequenceFake
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.plugins.qs.QSFactory
+import com.android.systemui.qs.tiles.di.NewQSTileFactory
val Kosmos.instanceIdSequenceFake: InstanceIdSequenceFake by
Kosmos.Fixture { InstanceIdSequenceFake(0) }
val Kosmos.uiEventLogger: UiEventLoggerFake by Kosmos.Fixture { UiEventLoggerFake() }
val Kosmos.qsEventLogger: QsEventLoggerFake by
Kosmos.Fixture { QsEventLoggerFake(uiEventLogger, instanceIdSequenceFake) }
+
+var Kosmos.newQSTileFactory by Kosmos.Fixture<NewQSTileFactory>()
+var Kosmos.qsTileFactory by Kosmos.Fixture<QSFactory>()
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/CustomTileStatePersisterKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/CustomTileStatePersisterKosmos.kt
new file mode 100644
index 0000000..f01e3aa
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/CustomTileStatePersisterKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.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.external
+
+import com.android.systemui.kosmos.Kosmos
+
+var Kosmos.customTileStatePersister: CustomTileStatePersister by
+ Kosmos.Fixture { fakeCustomTileStatePersister }
+val Kosmos.fakeCustomTileStatePersister by Kosmos.Fixture { FakeCustomTileStatePersister() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/TileLifecycleManagerFactoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/TileLifecycleManagerFactoryKosmos.kt
new file mode 100644
index 0000000..f8ce707
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/TileLifecycleManagerFactoryKosmos.kt
@@ -0,0 +1,24 @@
+/*
+ * 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.qs.external
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
+
+/** Returns mocks */
+var Kosmos.tileLifecycleManagerFactory: TileLifecycleManager.Factory by
+ Kosmos.Fixture { TileLifecycleManager.Factory { _, _ -> mock<TileLifecycleManager>() } }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/model/RestoreProcessorsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/model/RestoreProcessorsKosmos.kt
new file mode 100644
index 0000000..d93dd8d
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/model/RestoreProcessorsKosmos.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.pipeline.data.model
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.pipeline.data.restoreprocessors.WorkTileRestoreProcessor
+
+val Kosmos.workTileRestoreProcessor by Kosmos.Fixture { WorkTileRestoreProcessor() }
+
+var Kosmos.restoreProcessors by
+ Kosmos.Fixture {
+ setOf(
+ workTileRestoreProcessor,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/QSPipelineRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/QSPipelineRepositoryKosmos.kt
new file mode 100644
index 0000000..0091482
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/QSPipelineRepositoryKosmos.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.pipeline.data.repository
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.fakeTileSpecRepository by Kosmos.Fixture { FakeTileSpecRepository() }
+var Kosmos.tileSpecRepository: TileSpecRepository by Kosmos.Fixture { fakeTileSpecRepository }
+
+val Kosmos.fakeAutoAddRepository by Kosmos.Fixture { FakeAutoAddRepository() }
+var Kosmos.autoAddRepository: AutoAddRepository by Kosmos.Fixture { fakeAutoAddRepository }
+
+val Kosmos.fakeRestoreRepository by Kosmos.Fixture { FakeQSSettingsRestoredRepository() }
+var Kosmos.restoreRepository: QSSettingsRestoredRepository by
+ Kosmos.Fixture { fakeRestoreRepository }
+
+val Kosmos.fakeInstalledTilesRepository by
+ Kosmos.Fixture { FakeInstalledTilesComponentRepository() }
+var Kosmos.installedTilesRepository: InstalledTilesComponentRepository by
+ Kosmos.Fixture { fakeInstalledTilesRepository }
+
+val Kosmos.fakeCustomTileAddedRepository by Kosmos.Fixture { FakeCustomTileAddedRepository() }
+var Kosmos.customTileAddedRepository: CustomTileAddedRepository by
+ Kosmos.Fixture { fakeCustomTileAddedRepository }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddablesKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddablesKosmos.kt
new file mode 100644
index 0000000..35f178b
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddablesKosmos.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.pipeline.domain.autoaddable
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.pipeline.data.model.workTileRestoreProcessor
+import com.android.systemui.settings.userTracker
+
+val Kosmos.workTileAutoAddable by
+ Kosmos.Fixture { WorkTileAutoAddable(userTracker, workTileRestoreProcessor) }
+
+var Kosmos.autoAddables by Kosmos.Fixture { setOf(workTileAutoAddable) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/autoaddable/FakeAutoAddable.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/autoaddable/FakeAutoAddable.kt
index ebdd6fd..bf8f4da 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/autoaddable/FakeAutoAddable.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/autoaddable/FakeAutoAddable.kt
@@ -39,14 +39,18 @@
return getFlow(userId).asStateFlow().filterNotNull()
}
- suspend fun sendRemoveSignal(userId: Int) {
+ fun sendRemoveSignal(userId: Int) {
getFlow(userId).value = AutoAddSignal.Remove(spec)
}
- suspend fun sendAddSignal(userId: Int, position: Int = POSITION_AT_END) {
+ fun sendAddSignal(userId: Int, position: Int = POSITION_AT_END) {
getFlow(userId).value = AutoAddSignal.Add(spec, position)
}
+ fun sendRemoveTrackingSignal(userId: Int) {
+ getFlow(userId).value = AutoAddSignal.RemoveTracking(spec)
+ }
+
override val description: String
get() = "FakeAutoAddable($spec)"
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorKosmos.kt
new file mode 100644
index 0000000..5e8471c
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorKosmos.kt
@@ -0,0 +1,35 @@
+/*
+ * 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.qs.pipeline.domain.interactor
+
+import com.android.systemui.dump.dumpManager
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.qs.pipeline.data.repository.autoAddRepository
+import com.android.systemui.qs.pipeline.domain.autoaddable.autoAddables
+import com.android.systemui.qs.pipeline.shared.logging.qsLogger
+
+val Kosmos.autoAddInteractor by
+ Kosmos.Fixture {
+ AutoAddInteractor(
+ autoAddables,
+ autoAddRepository,
+ dumpManager,
+ qsLogger,
+ applicationCoroutineScope,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorKosmos.kt
new file mode 100644
index 0000000..67df563
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorKosmos.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.pipeline.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.qs.external.customTileStatePersister
+import com.android.systemui.qs.external.tileLifecycleManagerFactory
+import com.android.systemui.qs.newQSTileFactory
+import com.android.systemui.qs.pipeline.data.repository.customTileAddedRepository
+import com.android.systemui.qs.pipeline.data.repository.installedTilesRepository
+import com.android.systemui.qs.pipeline.data.repository.tileSpecRepository
+import com.android.systemui.qs.pipeline.shared.logging.qsLogger
+import com.android.systemui.qs.pipeline.shared.pipelineFlagsRepository
+import com.android.systemui.qs.qsTileFactory
+import com.android.systemui.settings.userTracker
+import com.android.systemui.user.data.repository.userRepository
+
+val Kosmos.currentTilesInteractor: CurrentTilesInteractor by
+ Kosmos.Fixture {
+ CurrentTilesInteractorImpl(
+ tileSpecRepository,
+ installedTilesRepository,
+ userRepository,
+ customTileStatePersister,
+ { newQSTileFactory },
+ qsTileFactory,
+ customTileAddedRepository,
+ tileLifecycleManagerFactory,
+ userTracker,
+ testDispatcher,
+ testDispatcher,
+ applicationCoroutineScope,
+ qsLogger,
+ pipelineFlagsRepository,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorKosmos.kt
new file mode 100644
index 0000000..55c23d4
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorKosmos.kt
@@ -0,0 +1,39 @@
+/*
+ * 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.qs.pipeline.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.qs.pipeline.data.model.restoreProcessors
+import com.android.systemui.qs.pipeline.data.repository.autoAddRepository
+import com.android.systemui.qs.pipeline.data.repository.restoreRepository
+import com.android.systemui.qs.pipeline.data.repository.tileSpecRepository
+import com.android.systemui.qs.pipeline.shared.logging.qsLogger
+
+val Kosmos.restoreReconciliationInteractor by
+ Kosmos.Fixture {
+ RestoreReconciliationInteractor(
+ tileSpecRepository,
+ autoAddRepository,
+ restoreRepository,
+ restoreProcessors,
+ qsLogger,
+ applicationCoroutineScope,
+ testDispatcher,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagRepositoryKosmos.kt
new file mode 100644
index 0000000..961545a
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagRepositoryKosmos.kt
@@ -0,0 +1,21 @@
+/*
+ * 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.qs.pipeline.shared
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.pipelineFlagsRepository by Kosmos.Fixture { QSPipelineFlagsRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLoggerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLoggerKosmos.kt
new file mode 100644
index 0000000..7d52f5d
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLoggerKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.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.pipeline.shared.logging
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
+
+/** mock */
+var Kosmos.qsLogger: QSPipelineLogger by Kosmos.Fixture { mock<QSPipelineLogger>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt
index 7494ccf..2ca338a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt
@@ -72,6 +72,7 @@
onBeforeUserSwitching()
onUserChanging()
onUserChanged()
+ onProfileChanged()
}
fun onBeforeUserSwitching(userId: Int = _userId) {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/UserTrackerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/UserTrackerKosmos.kt
new file mode 100644
index 0000000..ffa86ff
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/UserTrackerKosmos.kt
@@ -0,0 +1,22 @@
+/*
+ * 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.settings
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.fakeUserTracker by Kosmos.Fixture { FakeUserTracker() }
+var Kosmos.userTracker: UserTracker by Kosmos.Fixture { fakeUserTracker }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/smartspace/data/repository/FakeSmartspaceRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/smartspace/data/repository/FakeSmartspaceRepository.kt
index c8013ef..862e52d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/smartspace/data/repository/FakeSmartspaceRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/smartspace/data/repository/FakeSmartspaceRepository.kt
@@ -10,12 +10,12 @@
override val isSmartspaceRemoteViewsEnabled = smartspaceRemoteViewsEnabled
- private val _lockscreenSmartspaceTargets: MutableStateFlow<List<SmartspaceTarget>> =
+ private val _communalSmartspaceTargets: MutableStateFlow<List<SmartspaceTarget>> =
MutableStateFlow(emptyList())
- override val lockscreenSmartspaceTargets: Flow<List<SmartspaceTarget>> =
- _lockscreenSmartspaceTargets
+ override val communalSmartspaceTargets: Flow<List<SmartspaceTarget>> =
+ _communalSmartspaceTargets
- fun setLockscreenSmartspaceTargets(targets: List<SmartspaceTarget>) {
- _lockscreenSmartspaceTargets.value = targets
+ fun setCommunalSmartspaceTargets(targets: List<SmartspaceTarget>) {
+ _communalSmartspaceTargets.value = targets
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorKosmos.kt
index 3403227..13d577b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorKosmos.kt
@@ -18,6 +18,8 @@
import android.content.applicationContext
import com.android.systemui.common.ui.data.repository.configurationRepository
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.statusbar.policy.splitShadeStateController
@@ -27,5 +29,7 @@
configurationRepository = configurationRepository,
context = applicationContext,
splitShadeStateController = splitShadeStateController,
+ keyguardInteractor = keyguardInteractor,
+ deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor,
)
}
diff --git a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
index b315f4a..7fcef9c 100644
--- a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
+++ b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
@@ -76,6 +76,7 @@
import android.view.Surface;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.camera.extensions.impl.AutoImageCaptureExtenderImpl;
import androidx.camera.extensions.impl.AutoPreviewExtenderImpl;
import androidx.camera.extensions.impl.BeautyImageCaptureExtenderImpl;
@@ -94,6 +95,7 @@
import androidx.camera.extensions.impl.PreviewExtenderImpl.ProcessorType;
import androidx.camera.extensions.impl.PreviewImageProcessorImpl;
import androidx.camera.extensions.impl.ProcessResultImpl;
+import androidx.camera.extensions.impl.ProcessorImpl;
import androidx.camera.extensions.impl.RequestUpdateProcessorImpl;
import androidx.camera.extensions.impl.advanced.AdvancedExtenderImpl;
import androidx.camera.extensions.impl.advanced.AutoAdvancedExtenderImpl;
@@ -101,6 +103,7 @@
import androidx.camera.extensions.impl.advanced.BokehAdvancedExtenderImpl;
import androidx.camera.extensions.impl.advanced.Camera2OutputConfigImpl;
import androidx.camera.extensions.impl.advanced.Camera2SessionConfigImpl;
+import androidx.camera.extensions.impl.advanced.EyesFreeVideographyAdvancedExtenderImpl;
import androidx.camera.extensions.impl.advanced.HdrAdvancedExtenderImpl;
import androidx.camera.extensions.impl.advanced.ImageProcessorImpl;
import androidx.camera.extensions.impl.advanced.ImageReaderOutputConfigImpl;
@@ -112,6 +115,8 @@
import androidx.camera.extensions.impl.advanced.SessionProcessorImpl;
import androidx.camera.extensions.impl.advanced.SurfaceOutputConfigImpl;
+import com.android.internal.camera.flags.Flags;
+
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
@@ -135,22 +140,28 @@
private static final String RESULTS_VERSION_PREFIX = "1.3";
// Support for various latency improvements
private static final String LATENCY_VERSION_PREFIX = "1.4";
- private static final String[] ADVANCED_VERSION_PREFIXES = {LATENCY_VERSION_PREFIX,
- ADVANCED_VERSION_PREFIX, RESULTS_VERSION_PREFIX };
- private static final String[] SUPPORTED_VERSION_PREFIXES = {LATENCY_VERSION_PREFIX,
- RESULTS_VERSION_PREFIX, ADVANCED_VERSION_PREFIX, "1.1", NON_INIT_VERSION_PREFIX};
+ private static final String EFV_VERSION_PREFIX = "1.5";
+ private static final String[] ADVANCED_VERSION_PREFIXES = {EFV_VERSION_PREFIX,
+ LATENCY_VERSION_PREFIX, ADVANCED_VERSION_PREFIX, RESULTS_VERSION_PREFIX };
+ private static final String[] SUPPORTED_VERSION_PREFIXES = {EFV_VERSION_PREFIX,
+ LATENCY_VERSION_PREFIX, RESULTS_VERSION_PREFIX, ADVANCED_VERSION_PREFIX, "1.1",
+ NON_INIT_VERSION_PREFIX};
private static final boolean EXTENSIONS_PRESENT = checkForExtensions();
private static final String EXTENSIONS_VERSION = EXTENSIONS_PRESENT ?
(new ExtensionVersionImpl()).checkApiVersion(LATEST_VERSION) : null;
private static final boolean ESTIMATED_LATENCY_API_SUPPORTED = checkForLatencyAPI();
private static final boolean LATENCY_IMPROVEMENTS_SUPPORTED = EXTENSIONS_PRESENT &&
- (EXTENSIONS_VERSION.startsWith(LATENCY_VERSION_PREFIX));
+ (EXTENSIONS_VERSION.startsWith(LATENCY_VERSION_PREFIX) ||
+ (EXTENSIONS_VERSION.startsWith(EFV_VERSION_PREFIX)));
+ private static final boolean EFV_SUPPORTED = EXTENSIONS_PRESENT &&
+ (EXTENSIONS_VERSION.startsWith(EFV_VERSION_PREFIX));
private static final boolean ADVANCED_API_SUPPORTED = checkForAdvancedAPI();
private static final boolean INIT_API_SUPPORTED = EXTENSIONS_PRESENT &&
(!EXTENSIONS_VERSION.startsWith(NON_INIT_VERSION_PREFIX));
private static final boolean RESULT_API_SUPPORTED = EXTENSIONS_PRESENT &&
(EXTENSIONS_VERSION.startsWith(RESULTS_VERSION_PREFIX) ||
- EXTENSIONS_VERSION.startsWith(LATENCY_VERSION_PREFIX));
+ EXTENSIONS_VERSION.startsWith(LATENCY_VERSION_PREFIX) ||
+ EXTENSIONS_VERSION.startsWith(EFV_VERSION_PREFIX));
private HashMap<String, Long> mMetadataVendorIdMap = new HashMap<>();
private CameraManager mCameraManager;
@@ -509,6 +520,167 @@
*/
public static Pair<PreviewExtenderImpl, ImageCaptureExtenderImpl> initializeExtension(
int extensionType) {
+ if (Flags.concertMode()) {
+ if (extensionType == CameraExtensionCharacteristics.EXTENSION_EYES_FREE_VIDEOGRAPHY) {
+ // Basic extensions are deprecated starting with extension version 1.5
+ return new Pair<>(new PreviewExtenderImpl() {
+ @Override
+ public boolean isExtensionAvailable(String cameraId,
+ CameraCharacteristics cameraCharacteristics) {
+ return false;
+ }
+
+ @Override
+ public void init(String cameraId, CameraCharacteristics cameraCharacteristics) {
+
+ }
+
+ @Override
+ public androidx.camera.extensions.impl.CaptureStageImpl getCaptureStage() {
+ return null;
+ }
+
+ @Override
+ public ProcessorType getProcessorType() {
+ return null;
+ }
+
+ @Override
+ public ProcessorImpl getProcessor() {
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+ return null;
+ }
+
+ @Override
+ public void onInit(String cameraId, CameraCharacteristics cameraCharacteristics,
+ Context context) { }
+
+ @Override
+ public void onDeInit() { }
+
+ @Override
+ public androidx.camera.extensions.impl.CaptureStageImpl onPresetSession() {
+ return null;
+ }
+
+ @Override
+ public androidx.camera.extensions.impl.CaptureStageImpl onEnableSession() {
+ return null;
+ }
+
+ @Override
+ public androidx.camera.extensions.impl.CaptureStageImpl onDisableSession() {
+ return null;
+ }
+
+ @Override
+ public int onSessionType() {
+ return 0;
+ }
+ }, new ImageCaptureExtenderImpl() {
+ @Override
+ public boolean isExtensionAvailable(String cameraId,
+ CameraCharacteristics cameraCharacteristics) {
+ return false;
+ }
+
+ @Override
+ public void init(String cameraId,
+ CameraCharacteristics cameraCharacteristics) { }
+
+ @Override
+ public CaptureProcessorImpl getCaptureProcessor() {
+ return null;
+ }
+
+ @Override
+ public
+ List<androidx.camera.extensions.impl.CaptureStageImpl> getCaptureStages() {
+ return null;
+ }
+
+ @Override
+ public int getMaxCaptureStage() {
+ return 0;
+ }
+
+ @Override
+ public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+ return null;
+ }
+
+ @Override
+ public List<Pair<Integer, Size[]>> getSupportedPostviewResolutions(
+ Size captureSize) {
+ return null;
+ }
+
+ @Override
+ public Range<Long> getEstimatedCaptureLatencyRange(
+ Size captureOutputSize) {
+ return null;
+ }
+
+ @Override
+ public List<CaptureRequest.Key> getAvailableCaptureRequestKeys() {
+ return null;
+ }
+
+ @Override
+ public List<CaptureResult.Key> getAvailableCaptureResultKeys() {
+ return null;
+ }
+
+ @Override
+ public boolean isCaptureProcessProgressAvailable() {
+ return false;
+ }
+
+ @Override
+ public Pair<Long, Long> getRealtimeCaptureLatency() {
+ return null;
+ }
+
+ @Override
+ public boolean isPostviewAvailable() {
+ return false;
+ }
+
+ @Override
+ public void onInit(String cameraId,
+ CameraCharacteristics cameraCharacteristics, Context context) { }
+
+ @Override
+ public void onDeInit() { }
+
+ @Override
+ public androidx.camera.extensions.impl.CaptureStageImpl onPresetSession() {
+ return null;
+ }
+
+ @Override
+ public androidx.camera.extensions.impl.CaptureStageImpl onEnableSession() {
+ return null;
+ }
+
+ @Override
+ public androidx.camera.extensions.impl.CaptureStageImpl onDisableSession() {
+ return null;
+ }
+
+ @Override
+ public int onSessionType() {
+ return 0;
+ }
+ });
+ }
+ }
+
switch (extensionType) {
case CameraExtensionCharacteristics.EXTENSION_AUTOMATIC:
return new Pair<>(new AutoPreviewExtenderImpl(),
@@ -533,6 +705,82 @@
* @hide
*/
public static AdvancedExtenderImpl initializeAdvancedExtensionImpl(int extensionType) {
+ if (Flags.concertMode()) {
+ if (extensionType == CameraExtensionCharacteristics.EXTENSION_EYES_FREE_VIDEOGRAPHY) {
+ if (EFV_SUPPORTED) {
+ return new EyesFreeVideographyAdvancedExtenderImpl();
+ } else {
+ return new AdvancedExtenderImpl() {
+ @Override
+ public boolean isExtensionAvailable(String cameraId,
+ Map<String, CameraCharacteristics> characteristicsMap) {
+ return false;
+ }
+
+ @Override
+ public void init(String cameraId,
+ Map<String, CameraCharacteristics> characteristicsMap) {
+
+ }
+
+ @Override
+ public Range<Long> getEstimatedCaptureLatencyRange(String cameraId,
+ Size captureOutputSize, int imageFormat) {
+ return null;
+ }
+
+ @Override
+ public Map<Integer, List<Size>> getSupportedPreviewOutputResolutions(
+ String cameraId) {
+ return null;
+ }
+
+ @Override
+ public Map<Integer, List<Size>> getSupportedCaptureOutputResolutions(
+ String cameraId) {
+ return null;
+ }
+
+ @Override
+ public Map<Integer, List<Size>> getSupportedPostviewResolutions(
+ Size captureSize) {
+ return null;
+ }
+
+ @Override
+ public List<Size> getSupportedYuvAnalysisResolutions(String cameraId) {
+ return null;
+ }
+
+ @Override
+ public SessionProcessorImpl createSessionProcessor() {
+ return null;
+ }
+
+ @Override
+ public List<CaptureRequest.Key> getAvailableCaptureRequestKeys() {
+ return null;
+ }
+
+ @Override
+ public List<CaptureResult.Key> getAvailableCaptureResultKeys() {
+ return null;
+ }
+
+ @Override
+ public boolean isCaptureProcessProgressAvailable() {
+ return false;
+ }
+
+ @Override
+ public boolean isPostviewAvailable() {
+ return false;
+ }
+ };
+ }
+ }
+ }
+
switch (extensionType) {
case CameraExtensionCharacteristics.EXTENSION_AUTOMATIC:
return new AutoAdvancedExtenderImpl();
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
index d175713..513c095 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
@@ -16,6 +16,8 @@
package android.platform.test.ravenwood;
+import static org.junit.Assert.fail;
+
import android.platform.test.annotations.IgnoreUnderRavenwood;
import org.junit.Assume;
@@ -36,6 +38,15 @@
private static final boolean IS_UNDER_RAVENWOOD = RavenwoodRuleImpl.isUnderRavenwood();
+ /**
+ * When probing is enabled, all tests will be unconditionally run under Ravenwood to detect
+ * cases where a test is able to pass despite being marked as {@code IgnoreUnderRavenwood}.
+ *
+ * This is typically helpful for internal maintainers discovering tests that had previously
+ * been ignored, but now have enough Ravenwood-supported functionality to be enabled.
+ */
+ private static final boolean ENABLE_PROBE_IGNORED = false; // DO NOT SUBMIT WITH TRUE
+
private static final int SYSTEM_UID = 1000;
private static final int NOBODY_UID = 9999;
private static final int FIRST_APPLICATION_UID = 10000;
@@ -97,26 +108,76 @@
return IS_UNDER_RAVENWOOD;
}
+ /**
+ * Test if the given {@link Description} has been marked with an {@link IgnoreUnderRavenwood}
+ * annotation, either at the method or class level.
+ */
+ private static boolean hasIgnoreUnderRavenwoodAnnotation(Description description) {
+ if (description.getTestClass().getAnnotation(IgnoreUnderRavenwood.class) != null) {
+ return true;
+ } else if (description.getAnnotation(IgnoreUnderRavenwood.class) != null) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
@Override
public Statement apply(Statement base, Description description) {
+ if (ENABLE_PROBE_IGNORED) {
+ return applyProbeIgnored(base, description);
+ } else {
+ return applyDefault(base, description);
+ }
+ }
+
+ /**
+ * Run the given {@link Statement} with no special treatment.
+ */
+ private Statement applyDefault(Statement base, Description description) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
- if (description.getTestClass().getAnnotation(IgnoreUnderRavenwood.class) != null) {
+ if (hasIgnoreUnderRavenwoodAnnotation(description)) {
Assume.assumeFalse(IS_UNDER_RAVENWOOD);
}
- if (description.getAnnotation(IgnoreUnderRavenwood.class) != null) {
- Assume.assumeFalse(IS_UNDER_RAVENWOOD);
- }
- if (IS_UNDER_RAVENWOOD) {
- RavenwoodRuleImpl.init(RavenwoodRule.this);
- }
+
+ RavenwoodRuleImpl.init(RavenwoodRule.this);
try {
base.evaluate();
} finally {
- if (IS_UNDER_RAVENWOOD) {
- RavenwoodRuleImpl.reset(RavenwoodRule.this);
+ RavenwoodRuleImpl.reset(RavenwoodRule.this);
+ }
+ }
+ };
+ }
+
+ /**
+ * Run the given {@link Statement} with probing enabled. All tests will be unconditionally
+ * run under Ravenwood to detect cases where a test is able to pass despite being marked as
+ * {@code IgnoreUnderRavenwood}.
+ */
+ private Statement applyProbeIgnored(Statement base, Description description) {
+ return new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ RavenwoodRuleImpl.init(RavenwoodRule.this);
+ try {
+ base.evaluate();
+ } catch (Throwable t) {
+ if (hasIgnoreUnderRavenwoodAnnotation(description)) {
+ // This failure is expected, so eat the exception and report the
+ // assumption failure that test authors expect
+ Assume.assumeFalse(IS_UNDER_RAVENWOOD);
}
+ throw t;
+ } finally {
+ RavenwoodRuleImpl.reset(RavenwoodRule.this);
+ }
+
+ if (hasIgnoreUnderRavenwoodAnnotation(description) && IS_UNDER_RAVENWOOD) {
+ fail("Test was annotated with IgnoreUnderRavenwood, but it actually "
+ + "passed under Ravenwood; consider removing the annotation");
}
}
};
diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java b/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
index fb71e9d..0ff6a1a 100644
--- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
+++ b/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
@@ -22,12 +22,10 @@
}
public static void init(RavenwoodRule rule) {
- // Must be provided by impl to reference runtime internals
- throw new UnsupportedOperationException();
+ // No-op when running on a real device
}
public static void reset(RavenwoodRule rule) {
- // Must be provided by impl to reference runtime internals
- throw new UnsupportedOperationException();
+ // No-op when running on a real device
}
}
diff --git a/ravenwood/ravenwood-annotation-allowed-classes.txt b/ravenwood/ravenwood-annotation-allowed-classes.txt
index 9fcabd6..13908f1 100644
--- a/ravenwood/ravenwood-annotation-allowed-classes.txt
+++ b/ravenwood/ravenwood-annotation-allowed-classes.txt
@@ -1,8 +1,16 @@
# Only classes listed here can use the Ravenwood annotations.
com.android.internal.util.ArrayUtils
+com.android.internal.os.BatteryStatsHistory
+com.android.internal.os.BatteryStatsHistory$TraceDelegate
+com.android.internal.os.BatteryStatsHistory$VarintParceler
+com.android.internal.os.BatteryStatsHistoryIterator
+com.android.internal.os.Clock
com.android.internal.os.LongArrayMultiStateCounter
com.android.internal.os.LongArrayMultiStateCounter$LongArrayContainer
+com.android.internal.os.MonotonicClock
+com.android.internal.os.PowerStats
+com.android.internal.os.PowerStats$Descriptor
android.util.AtomicFile
android.util.DataUnit
@@ -25,10 +33,15 @@
android.util.Xml
android.os.BatteryConsumer
+android.os.BatteryStats$HistoryItem
+android.os.BatteryStats$HistoryStepDetails
+android.os.BatteryStats$HistoryTag
+android.os.BatteryStats$ProcessStateChange
android.os.Binder
android.os.Binder$IdentitySupplier
android.os.Broadcaster
android.os.BundleMerger
+android.os.ConditionVariable
android.os.FileUtils
android.os.FileUtils$MemoryPipe
android.os.Handler
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 0696807..97d36d4 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -209,6 +209,7 @@
final ComponentName mComponentName;
int mGenericMotionEventSources;
+ int mObservedMotionEventSources;
// the events pending events to be dispatched to this service
final SparseArray<AccessibilityEvent> mPendingEvents = new SparseArray<>();
@@ -397,6 +398,19 @@
mNotificationTimeout = info.notificationTimeout;
mIsDefault = (info.flags & DEFAULT) != 0;
mGenericMotionEventSources = info.getMotionEventSources();
+ if (android.view.accessibility.Flags.motionEventObserving()) {
+ if (mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.ACCESSIBILITY_MOTION_EVENT_OBSERVING)
+ == PackageManager.PERMISSION_GRANTED) {
+ mObservedMotionEventSources = info.getObservedMotionEventSources();
+ } else {
+ Slog.e(
+ LOG_TAG,
+ "Observing motion events requires"
+ + " android.Manifest.permission.ACCESSIBILITY_MOTION_EVENT_OBSERVING.");
+ mObservedMotionEventSources = 0;
+ }
+ }
if (supportsFlagForNotImportantViews(info)) {
if ((info.flags & AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS) != 0) {
@@ -1599,7 +1613,7 @@
final int displayId = displays[i].getDisplayId();
onDisplayRemoved(displayId);
}
- if (Flags.cleanupA11yOverlays()) {
+ if (com.android.server.accessibility.Flags.cleanupA11yOverlays()) {
detachAllOverlays();
}
}
@@ -1919,6 +1933,7 @@
return (mGenericMotionEventSources & eventSourceWithoutClass) != 0;
}
+
/**
* Called by the invocation handler to notify the service that the
* state of magnification has changed.
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index 6cac6a4..9ddc35a 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -198,6 +198,7 @@
// State tracking for generic MotionEvents is display-agnostic so we only need one.
private GenericMotionEventStreamState mGenericMotionEventStreamState;
private int mCombinedGenericMotionEventSources = 0;
+ private int mCombinedMotionEventObservedSources = 0;
private EventStreamState mKeyboardStreamState;
@@ -525,16 +526,33 @@
}
if ((mEnabledFeatures & FLAG_FEATURE_INTERCEPT_GENERIC_MOTION_EVENTS) != 0) {
- addFirstEventHandler(displayId, new BaseEventStreamTransformation() {
- @Override
- public void onMotionEvent(MotionEvent event, MotionEvent rawEvent,
- int policyFlags) {
- if (!anyServiceWantsGenericMotionEvent(rawEvent)
- || !mAms.sendMotionEventToListeningServices(rawEvent)) {
- super.onMotionEvent(event, rawEvent, policyFlags);
- }
- }
- });
+ addFirstEventHandler(
+ displayId,
+ new BaseEventStreamTransformation() {
+ @Override
+ public void onMotionEvent(
+ MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ boolean passAlongEvent = true;
+ if (anyServiceWantsGenericMotionEvent(event)) {
+ // Some service wants this event, so try to deliver it to at least
+ // one service.
+ if (mAms.sendMotionEventToListeningServices(event)) {
+ // A service accepted this event, so prevent it from passing
+ // down the stream by default.
+ passAlongEvent = false;
+ }
+ // However, if a service is observing these events instead of
+ // consuming them then ensure
+ // it is always passed along to the next stage of the event stream.
+ if (anyServiceWantsToObserveMotionEvent(event)) {
+ passAlongEvent = true;
+ }
+ }
+ if (passAlongEvent) {
+ super.onMotionEvent(event, rawEvent, policyFlags);
+ }
+ }
+ });
}
if ((mEnabledFeatures & FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER) != 0
@@ -542,15 +560,14 @@
|| ((mEnabledFeatures & FLAG_FEATURE_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP) != 0)
|| ((mEnabledFeatures & FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER) != 0)) {
final MagnificationGestureHandler magnificationGestureHandler =
- createMagnificationGestureHandler(displayId,
- displayContext);
+ createMagnificationGestureHandler(displayId, displayContext);
addFirstEventHandler(displayId, magnificationGestureHandler);
mMagnificationGestureHandler.put(displayId, magnificationGestureHandler);
}
if ((mEnabledFeatures & FLAG_FEATURE_INJECT_MOTION_EVENTS) != 0) {
- MotionEventInjector injector = new MotionEventInjector(
- mContext.getMainLooper(), mAms.getTraceManager());
+ MotionEventInjector injector =
+ new MotionEventInjector(mContext.getMainLooper(), mAms.getTraceManager());
addFirstEventHandler(displayId, injector);
mMotionEventInjectors.put(displayId, injector);
}
@@ -923,6 +940,20 @@
}
}
+ private boolean anyServiceWantsToObserveMotionEvent(MotionEvent event) {
+ // Disable SOURCE_TOUCHSCREEN generic event interception if any service is performing
+ // touch exploration.
+ if (event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)
+ && (mEnabledFeatures & FLAG_FEATURE_TOUCH_EXPLORATION) != 0) {
+ return false;
+ }
+ final int eventSourceWithoutClass = event.getSource() & ~InputDevice.SOURCE_CLASS_MASK;
+ return (mCombinedGenericMotionEventSources
+ & mCombinedMotionEventObservedSources
+ & eventSourceWithoutClass)
+ != 0;
+ }
+
private boolean anyServiceWantsGenericMotionEvent(MotionEvent event) {
// Disable SOURCE_TOUCHSCREEN generic event interception if any service is performing
// touch exploration.
@@ -938,6 +969,10 @@
mCombinedGenericMotionEventSources = sources;
}
+ public void setCombinedMotionEventObservedSources(int sources) {
+ mCombinedMotionEventObservedSources = sources;
+ }
+
/**
* Keeps state of streams of events from all keyboard devices.
*/
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 440e996..edb41639 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -2825,8 +2825,10 @@
flags |= AccessibilityInputFilter.FLAG_FEATURE_INJECT_MOTION_EVENTS;
}
int combinedGenericMotionEventSources = 0;
+ int combinedMotionEventObservedSources = 0;
for (AccessibilityServiceConnection connection : userState.mBoundServices) {
combinedGenericMotionEventSources |= connection.mGenericMotionEventSources;
+ combinedMotionEventObservedSources |= connection.mObservedMotionEventSources;
}
if (combinedGenericMotionEventSources != 0) {
flags |= AccessibilityInputFilter.FLAG_FEATURE_INTERCEPT_GENERIC_MOTION_EVENTS;
@@ -2845,6 +2847,8 @@
mInputFilter.setUserAndEnabledFeatures(userState.mUserId, flags);
mInputFilter.setCombinedGenericMotionEventSources(
combinedGenericMotionEventSources);
+ mInputFilter.setCombinedMotionEventObservedSources(
+ combinedMotionEventObservedSources);
} else {
if (mHasInputFilter) {
mHasInputFilter = false;
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
index 34787a3..145303d 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
@@ -554,6 +554,10 @@
if (mMaster.debug) Slog.d(mTag, "onActivityEvent(): no remote service");
return;
}
+ if (mRemoteService.getServiceInterface() == null) {
+ if (mMaster.debug) Slog.d(mTag, "onActivityEvent(): remote service is dead or unbound");
+ return;
+ }
final ActivityEvent event = new ActivityEvent(activityId, componentName, type);
if (mMaster.verbose) Slog.v(mTag, "onActivityEvent(): " + event);
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 20a3b9a..a0ccbf3a 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -154,6 +154,7 @@
static_libs: [
"android.frameworks.location.altitude-V1-java", // AIDL
+ "android.frameworks.vibrator-V1-java", // AIDL
"android.hardware.authsecret-V1.0-java",
"android.hardware.authsecret-V1-java",
"android.hardware.boot-V1.0-java", // HIDL
@@ -165,7 +166,7 @@
"android.hardware.health-V1.0-java", // HIDL
"android.hardware.health-V2.0-java", // HIDL
"android.hardware.health-V2.1-java", // HIDL
- "android.hardware.health-V2-java", // AIDL
+ "android.hardware.health-V3-java", // AIDL
"android.hardware.health-translate-java",
"android.hardware.light-V1-java",
"android.hardware.security.rkp-V3-java",
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index f8f3d82..ace2cfd 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -33,6 +33,7 @@
import android.annotation.NonNull;
import android.annotation.RequiresNoPermission;
import android.annotation.SuppressLint;
+import android.app.AlarmManager;
import android.app.StatsManager;
import android.app.usage.NetworkStatsManager;
import android.bluetooth.BluetoothActivityEnergyInfo;
@@ -427,13 +428,7 @@
mPowerStatsStore = new PowerStatsStore(systemDir, mHandler, aggregatedPowerStatsConfig);
mPowerStatsAggregator = new PowerStatsAggregator(aggregatedPowerStatsConfig,
mStats.getHistory());
- final long aggregatedPowerStatsSpanDuration = context.getResources().getInteger(
- com.android.internal.R.integer.config_aggregatedPowerStatsSpanDuration);
- final long powerStatsAggregationPeriod = context.getResources().getInteger(
- com.android.internal.R.integer.config_powerStatsAggregationPeriod);
- mPowerStatsScheduler = new PowerStatsScheduler(context, mPowerStatsAggregator,
- aggregatedPowerStatsSpanDuration, powerStatsAggregationPeriod, mPowerStatsStore,
- Clock.SYSTEM_CLOCK, mMonotonicClock, mHandler, mStats);
+ mPowerStatsScheduler = createPowerStatsScheduler(mContext);
PowerStatsExporter powerStatsExporter =
new PowerStatsExporter(mPowerStatsStore, mPowerStatsAggregator);
mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(context,
@@ -445,6 +440,23 @@
mConfigFile = new AtomicFile(new File(systemDir, "battery_usage_stats_config"));
}
+ private PowerStatsScheduler createPowerStatsScheduler(Context context) {
+ final long aggregatedPowerStatsSpanDuration = context.getResources().getInteger(
+ com.android.internal.R.integer.config_aggregatedPowerStatsSpanDuration);
+ final long powerStatsAggregationPeriod = context.getResources().getInteger(
+ com.android.internal.R.integer.config_powerStatsAggregationPeriod);
+ PowerStatsScheduler.AlarmScheduler alarmScheduler =
+ (triggerAtMillis, tag, onAlarmListener, aHandler) -> {
+ AlarmManager alarmManager = mContext.getSystemService(AlarmManager.class);
+ alarmManager.set(AlarmManager.ELAPSED_REALTIME, triggerAtMillis, tag,
+ onAlarmListener, aHandler);
+ };
+ return new PowerStatsScheduler(mStats::schedulePowerStatsSampleCollection,
+ mPowerStatsAggregator, aggregatedPowerStatsSpanDuration,
+ powerStatsAggregationPeriod, mPowerStatsStore, alarmScheduler, Clock.SYSTEM_CLOCK,
+ mMonotonicClock, () -> mStats.getHistory().getStartTime(), mHandler);
+ }
+
private AggregatedPowerStatsConfig getAggregatedPowerStatsConfig() {
AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig();
config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_CPU)
diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java
index 903cb7b..982076d 100644
--- a/services/core/java/com/android/server/am/CoreSettingsObserver.java
+++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java
@@ -30,7 +30,6 @@
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import java.util.ArrayList;
import java.util.HashMap;
@@ -164,12 +163,6 @@
WidgetFlags.MAGNIFIER_ASPECT_RATIO_DEFAULT));
sDeviceConfigEntries.add(new DeviceConfigEntry<Boolean>(
- DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.REMOTEVIEWS_ADAPTER_CONVERSION,
- SystemUiDeviceConfigFlags.KEY_REMOTEVIEWS_ADAPTER_CONVERSION, boolean.class,
- SystemUiDeviceConfigFlags.REMOTEVIEWS_ADAPTER_CONVERSION_DEFAULT));
-
- sDeviceConfigEntries.add(new DeviceConfigEntry<Boolean>(
TextFlags.NAMESPACE, TextFlags.ENABLE_NEW_CONTEXT_MENU,
TextFlags.KEY_ENABLE_NEW_CONTEXT_MENU, boolean.class,
TextFlags.ENABLE_NEW_CONTEXT_MENU_DEFAULT));
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index b182538..32d5cf5 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -152,7 +152,6 @@
private static final String GAME_MODE_INTERVENTION_LIST_FILE_NAME =
"game_mode_intervention.list";
-
private final Context mContext;
private final Object mLock = new Object();
private final Object mDeviceConfigLock = new Object();
@@ -184,6 +183,7 @@
@GuardedBy("mUidObserverLock")
private final Set<Integer> mForegroundGameUids = new HashSet<>();
private final GameManagerServiceSystemPropertiesWrapper mSysProps;
+ private float mGameDefaultFrameRateValue;
@VisibleForTesting
static class Injector {
@@ -1559,6 +1559,10 @@
mPowerManagerInternal.setPowerMode(Mode.GAME_LOADING, false);
Slog.v(TAG, "Game power mode OFF (game manager service start/restart)");
mPowerManagerInternal.setPowerMode(Mode.GAME, false);
+
+ mGameDefaultFrameRateValue = (float) mSysProps.getInt(
+ PROPERTY_RO_SURFACEFLINGER_GAME_DEFAULT_FRAME_RATE, 60);
+ Slog.v(TAG, "Game Default Frame Rate : " + mGameDefaultFrameRateValue);
}
private void sendUserMessage(int userId, int what, String eventForLog, int delayMillis) {
@@ -2217,8 +2221,7 @@
}
if (gameDefaultFrameRate()) {
gameDefaultFrameRate = isGameDefaultFrameRateEnabled
- ? (float) mSysProps.getInt(
- PROPERTY_RO_SURFACEFLINGER_GAME_DEFAULT_FRAME_RATE, 0) : 0.0f;
+ ? mGameDefaultFrameRateValue : 0.0f;
}
return gameDefaultFrameRate;
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 44cb136..290bb7e 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -5106,7 +5106,7 @@
private void setMasterMuteInternalNoCallerCheck(
boolean mute, int flags, int userId, String eventSource) {
if (DEBUG_VOL) {
- Log.d(TAG, TextUtils.formatSimple("Master mute %s, %d, user=%d from %s",
+ Log.d(TAG, TextUtils.formatSimple("Master mute %s, flags 0x%x, userId=%d from %s",
mute, flags, userId, eventSource));
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
index 5084b60..578d9dc 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
@@ -722,6 +722,7 @@
if (Flags.faceVhalFeature() && Utils.isVirtualEnabled(getContext())) {
if (virtualAt != -1) {
//only virtual instance should be returned
+ Slog.i(TAG, "virtual hal is used");
return new Pair(new ArrayList<>(), List.of(aidlInstances.get(virtualAt)));
} else {
Slog.e(TAG, "Could not find virtual interface while it is enabled");
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index 5ce0c8b..7695543 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -1057,6 +1057,7 @@
if (Utils.isVirtualEnabled(getContext())) {
if (virtualAt != -1) {
//only virtual instance should be returned
+ Slog.i(TAG, "virtual hal is used");
return new Pair(new ArrayList<>(), List.of(aidlInstances.get(virtualAt)));
} else {
Slog.e(TAG, "Could not find virtual interface while it is enabled");
diff --git a/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java b/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java
index 173c452..8504495 100644
--- a/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java
+++ b/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java
@@ -330,8 +330,11 @@
TextUtils.isEmpty(address)
? null
: mBluetoothRouteController.getRouteIdForBluetoothAddress(address);
- return createMediaRoute2Info(
- routeId, audioDeviceInfo.getType(), audioDeviceInfo.getProductName(), address);
+ // We use the name from the port instead AudioDeviceInfo#getProductName because the latter
+ // replaces empty names with the name of the device (example: Pixel 8). In that case we want
+ // to derive a name ourselves from the type instead.
+ String deviceName = audioDeviceInfo.getPort().name();
+ return createMediaRoute2Info(routeId, audioDeviceInfo.getType(), deviceName, address);
}
/**
@@ -339,8 +342,8 @@
*
* @param routeId A route id, or null to use an id pre-defined for the given {@code type}.
* @param audioDeviceInfoType The type as obtained from {@link AudioDeviceInfo#getType}.
- * @param productName The product name as obtained from {@link
- * AudioDeviceInfo#getProductName()}, or null to use a predefined name for the given {@code
+ * @param deviceName A human readable name to populate the route's {@link
+ * MediaRoute2Info#getName name}, or null to use a predefined name for the given {@code
* type}.
* @param address The type as obtained from {@link AudioDeviceInfo#getAddress()} or {@link
* BluetoothDevice#getAddress()}.
@@ -350,7 +353,7 @@
private MediaRoute2Info createMediaRoute2Info(
@Nullable String routeId,
int audioDeviceInfoType,
- @Nullable CharSequence productName,
+ @Nullable CharSequence deviceName,
@Nullable String address) {
SystemRouteInfo systemRouteInfo =
AUDIO_DEVICE_INFO_TYPE_TO_ROUTE_INFO.get(audioDeviceInfoType);
@@ -359,7 +362,7 @@
// earpiece.
return null;
}
- CharSequence humanReadableName = productName;
+ CharSequence humanReadableName = deviceName;
if (TextUtils.isEmpty(humanReadableName)) {
humanReadableName = mContext.getResources().getText(systemRouteInfo.mNameResource);
}
diff --git a/services/core/java/com/android/server/notification/flags.aconfig b/services/core/java/com/android/server/notification/flags.aconfig
index dcac8c9..da01745 100644
--- a/services/core/java/com/android/server/notification/flags.aconfig
+++ b/services/core/java/com/android/server/notification/flags.aconfig
@@ -20,3 +20,17 @@
description: "This flag controls the refactoring of NMS to NotificationAttentionHelper"
bug: "291907312"
}
+
+flag {
+ name: "cross_app_polite_notifications"
+ namespace: "systemui"
+ description: "This flag controls the cross-app effect of polite notifications"
+ bug: "270456865"
+}
+
+flag {
+ name: "vibrate_while_unlocked"
+ namespace: "systemui"
+ description: "This flag controls the vibrate while unlocked setting of polite notifications"
+ bug: "270456865"
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/pm/DeletePackageAction.java b/services/core/java/com/android/server/pm/DeletePackageAction.java
index 8ef6601..31544d5 100644
--- a/services/core/java/com/android/server/pm/DeletePackageAction.java
+++ b/services/core/java/com/android/server/pm/DeletePackageAction.java
@@ -16,17 +16,19 @@
package com.android.server.pm;
+import android.annotation.NonNull;
import android.os.UserHandle;
final class DeletePackageAction {
public final PackageSetting mDeletingPs;
public final PackageSetting mDisabledPs;
+ @NonNull
public final PackageRemovedInfo mRemovedInfo;
public final int mFlags;
public final UserHandle mUser;
DeletePackageAction(PackageSetting deletingPs, PackageSetting disabledPs,
- PackageRemovedInfo removedInfo, int flags, UserHandle user) {
+ @NonNull PackageRemovedInfo removedInfo, int flags, UserHandle user) {
mDeletingPs = deletingPs;
mDisabledPs = disabledPs;
mRemovedInfo = removedInfo;
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index 93836266..dcf921c 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -370,7 +370,7 @@
@GuardedBy("mPm.mInstallLock")
public boolean deletePackageLIF(@NonNull String packageName, UserHandle user,
boolean deleteCodeAndResources, @NonNull int[] allUserHandles, int flags,
- PackageRemovedInfo outInfo, boolean writeSettings) {
+ @NonNull PackageRemovedInfo outInfo, boolean writeSettings) {
final DeletePackageAction action;
synchronized (mPm.mLock) {
final PackageSetting ps = mPm.mSettings.getPackageLPr(packageName);
@@ -410,8 +410,8 @@
* deleted, {@code null} otherwise.
*/
@Nullable
- public static DeletePackageAction mayDeletePackageLocked(
- PackageRemovedInfo outInfo, PackageSetting ps, @Nullable PackageSetting disabledPs,
+ public static DeletePackageAction mayDeletePackageLocked(@NonNull PackageRemovedInfo outInfo,
+ PackageSetting ps, @Nullable PackageSetting disabledPs,
int flags, UserHandle user) {
if (ps == null) {
return null;
@@ -460,12 +460,18 @@
}
final int userId = user == null ? UserHandle.USER_ALL : user.getIdentifier();
- if (outInfo != null) {
- // Remember which users are affected, before the installed states are modified
- outInfo.mRemovedUsers = (systemApp || userId == UserHandle.USER_ALL)
- ? ps.queryUsersInstalledOrHasData(allUserHandles)
- : new int[]{userId};
- }
+ // Remember which users are affected, before the installed states are modified
+ outInfo.mRemovedUsers = (systemApp || userId == UserHandle.USER_ALL)
+ ? ps.queryUsersInstalledOrHasData(allUserHandles)
+ : new int[]{userId};
+ outInfo.populateBroadcastUsers(ps);
+ outInfo.mDataRemoved = (flags & PackageManager.DELETE_KEEP_DATA) == 0;
+ outInfo.mRemovedPackage = ps.getPackageName();
+ outInfo.mInstallerPackageName = ps.getInstallSource().mInstallerPackageName;
+ outInfo.mIsStaticSharedLib =
+ ps.getPkg() != null && ps.getPkg().getStaticSharedLibraryName() != null;
+ outInfo.mIsExternal = ps.isExternalStorage();
+ outInfo.mRemovedPackageVersionCode = ps.getVersionCode();
if ((!systemApp || (flags & PackageManager.DELETE_SYSTEM_APP) != 0)
&& userId != UserHandle.USER_ALL) {
@@ -503,7 +509,8 @@
}
}
if (clearPackageStateAndReturn) {
- mRemovePackageHelper.clearPackageStateForUserLIF(ps, userId, outInfo, flags);
+ mRemovePackageHelper.clearPackageStateForUserLIF(ps, userId, flags);
+ outInfo.mRemovedAppId = ps.getAppId();
mPm.scheduleWritePackageRestrictions(user);
return;
}
@@ -529,12 +536,8 @@
// If the package removed had SUSPEND_APPS, unset any restrictions that might have been in
// place for all affected users.
- int[] affectedUserIds = (outInfo != null) ? outInfo.mRemovedUsers : null;
- if (affectedUserIds == null) {
- affectedUserIds = mPm.resolveUserIds(userId);
- }
final Computer snapshot = mPm.snapshotComputer();
- for (final int affectedUserId : affectedUserIds) {
+ for (final int affectedUserId : outInfo.mRemovedUsers) {
if (hadSuspendAppsPermission.get(affectedUserId)) {
mPm.unsuspendForSuspendingPackage(snapshot, packageName, affectedUserId);
mPm.removeAllDistractingPackageRestrictions(snapshot, affectedUserId);
@@ -542,24 +545,20 @@
}
// Take a note whether we deleted the package for all users
- if (outInfo != null) {
- synchronized (mPm.mLock) {
- outInfo.mRemovedForAllUsers = mPm.mPackages.get(ps.getPackageName()) == null;
- }
+ synchronized (mPm.mLock) {
+ outInfo.mRemovedForAllUsers = mPm.mPackages.get(ps.getPackageName()) == null;
}
}
@GuardedBy("mPm.mInstallLock")
private void deleteInstalledPackageLIF(PackageSetting ps,
boolean deleteCodeAndResources, int flags, @NonNull int[] allUserHandles,
- PackageRemovedInfo outInfo, boolean writeSettings) {
+ @NonNull PackageRemovedInfo outInfo, boolean writeSettings) {
synchronized (mPm.mLock) {
- if (outInfo != null) {
- outInfo.mUid = ps.getAppId();
- outInfo.mBroadcastAllowList = mPm.mAppsFilter.getVisibilityAllowList(
- mPm.snapshotComputer(), ps, allUserHandles,
- mPm.mSettings.getPackagesLocked());
- }
+ outInfo.mUid = ps.getAppId();
+ outInfo.mBroadcastAllowList = mPm.mAppsFilter.getVisibilityAllowList(
+ mPm.snapshotComputer(), ps, allUserHandles,
+ mPm.mSettings.getPackagesLocked());
}
// Delete package data from internal structures and also remove data if flag is set
@@ -567,7 +566,7 @@
ps, allUserHandles, outInfo, flags, writeSettings);
// Delete application code and resources only for parent packages
- if (deleteCodeAndResources && (outInfo != null)) {
+ if (deleteCodeAndResources) {
outInfo.mArgs = new InstallArgs(
ps.getPathString(), getAppDexInstructionSets(
ps.getPrimaryCpuAbiLegacy(), ps.getSecondaryCpuAbiLegacy()));
@@ -639,7 +638,7 @@
int flags = action.mFlags;
final PackageSetting deletedPs = action.mDeletingPs;
final PackageRemovedInfo outInfo = action.mRemovedInfo;
- final boolean applyUserRestrictions = outInfo != null && (outInfo.mOrigUsers != null);
+ final boolean applyUserRestrictions = outInfo.mOrigUsers != null;
final AndroidPackage deletedPkg = deletedPs.getPkg();
// Confirm if the system package has been updated
// An updated system app can be deleted. This will also have to restore
@@ -662,10 +661,8 @@
}
}
- if (outInfo != null) {
- // Delete the updated package
- outInfo.mIsRemovedPackageSystemUpdate = true;
- }
+ // Delete the updated package
+ outInfo.mIsRemovedPackageSystemUpdate = true;
if (disabledPs.getVersionCode() < deletedPs.getVersionCode()
|| disabledPs.getAppId() != deletedPs.getAppId()) {
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 83a6f10..f1c0627 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -4126,7 +4126,7 @@
null /* request */)) {
mDeletePackageHelper.deletePackageLIF(
parsedPackage.getPackageName(), null, true,
- mPm.mUserManager.getUserIds(), 0, null, false);
+ mPm.mUserManager.getUserIds(), 0, new PackageRemovedInfo(), false);
}
} else if (newPkgVersionGreater || newSharedUserSetting) {
// The application on /system is newer than the application on /data.
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 2880f84..c5b006c 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -3044,6 +3044,7 @@
}
}
+ @NonNull
int[] resolveUserIds(int userId) {
return (userId == UserHandle.USER_ALL) ? mUserManager.getUserIds() : new int[] { userId };
}
diff --git a/services/core/java/com/android/server/pm/RemovePackageHelper.java b/services/core/java/com/android/server/pm/RemovePackageHelper.java
index 52b3131..109d7ba 100644
--- a/services/core/java/com/android/server/pm/RemovePackageHelper.java
+++ b/services/core/java/com/android/server/pm/RemovePackageHelper.java
@@ -252,8 +252,7 @@
}
}
- public void clearPackageStateForUserLIF(PackageSetting ps, int userId,
- PackageRemovedInfo outInfo, int flags) {
+ public void clearPackageStateForUserLIF(PackageSetting ps, int userId, int flags) {
final AndroidPackage pkg;
final SharedUserSetting sus;
synchronized (mPm.mLock) {
@@ -287,25 +286,12 @@
}
mPermissionManager.onPackageUninstalled(ps.getPackageName(), ps.getAppId(), ps, pkg,
sharedUserPkgs, userId);
-
- if (outInfo != null) {
- if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) {
- outInfo.mDataRemoved = true;
- }
- outInfo.mRemovedPackage = ps.getPackageName();
- outInfo.mInstallerPackageName = ps.getInstallSource().mInstallerPackageName;
- outInfo.mIsStaticSharedLib = pkg != null && pkg.getStaticSharedLibraryName() != null;
- outInfo.mRemovedAppId = ps.getAppId();
- outInfo.mBroadcastUsers = outInfo.mRemovedUsers;
- outInfo.mIsExternal = ps.isExternalStorage();
- outInfo.mRemovedPackageVersionCode = ps.getVersionCode();
- }
}
// Called to clean up disabled system packages
public void removePackageData(final PackageSetting deletedPs, @NonNull int[] allUserHandles) {
synchronized (mPm.mInstallLock) {
- removePackageDataLIF(deletedPs, allUserHandles, /* outInfo= */ null,
+ removePackageDataLIF(deletedPs, allUserHandles, new PackageRemovedInfo(),
/* flags= */ 0, /* writeSettings= */ false);
}
}
@@ -318,20 +304,11 @@
*/
@GuardedBy("mPm.mInstallLock")
public void removePackageDataLIF(final PackageSetting deletedPs, @NonNull int[] allUserHandles,
- PackageRemovedInfo outInfo, int flags, boolean writeSettings) {
+ @NonNull PackageRemovedInfo outInfo, int flags, boolean writeSettings) {
String packageName = deletedPs.getPackageName();
if (DEBUG_REMOVE) Slog.d(TAG, "removePackageDataLI: " + deletedPs);
// Retrieve object to delete permissions for shared user later on
final AndroidPackage deletedPkg = deletedPs.getPkg();
- if (outInfo != null) {
- outInfo.mRemovedPackage = packageName;
- outInfo.mInstallerPackageName = deletedPs.getInstallSource().mInstallerPackageName;
- outInfo.mIsStaticSharedLib = deletedPkg != null
- && deletedPkg.getStaticSharedLibraryName() != null;
- outInfo.populateBroadcastUsers(deletedPs);
- outInfo.mIsExternal = deletedPs.isExternalStorage();
- outInfo.mRemovedPackageVersionCode = deletedPs.getVersionCode();
- }
removePackageLI(deletedPs.getPackageName(), (flags & PackageManager.DELETE_CHATTY) != 0);
if (!deletedPs.isSystem()) {
@@ -355,9 +332,6 @@
mAppDataHelper.destroyAppDataLIF(resolvedPkg, UserHandle.USER_ALL,
FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL);
mAppDataHelper.destroyAppProfilesLIF(resolvedPkg.getPackageName());
- if (outInfo != null) {
- outInfo.mDataRemoved = true;
- }
}
int removedAppId = -1;
@@ -373,9 +347,8 @@
mPm.mAppsFilter.removePackage(snapshot,
snapshot.getPackageStateInternal(packageName));
removedAppId = mPm.mSettings.removePackageLPw(packageName);
- if (outInfo != null) {
- outInfo.mRemovedAppId = removedAppId;
- }
+ outInfo.mRemovedAppId = removedAppId;
+
if (!mPm.mSettings.isDisabledSystemPackageLPr(packageName)) {
// If we don't have a disabled system package to reinstall, the package is
// really gone and its permission state should be removed.
@@ -403,8 +376,8 @@
mBroadcastHelper.sendPreferredActivityChangedBroadcast(UserHandle.USER_ALL);
});
}
- } else if (!deletedPs.isSystem() && outInfo != null && !outInfo.mIsUpdate
- && outInfo.mRemovedUsers != null && !outInfo.mIsExternal) {
+ } else if (!deletedPs.isSystem() && !outInfo.mIsUpdate
+ && outInfo.mRemovedUsers != null && !deletedPs.isExternalStorage()) {
// For non-system uninstalls with DELETE_KEEP_DATA, set the installed state to false
// for affected users. This does not apply to app updates where the old apk is replaced
// but the old data remains.
@@ -424,7 +397,7 @@
// make sure to preserve per-user installed state if this removal was just
// a downgrade of a system app to the factory package
boolean installedStateChanged = false;
- if (outInfo != null && outInfo.mOrigUsers != null && deletedPs.isSystem()) {
+ if (outInfo.mOrigUsers != null && deletedPs.isSystem()) {
if (DEBUG_REMOVE) {
Slog.d(TAG, "Propagating install state across downgrade");
}
diff --git a/services/core/java/com/android/server/pm/SharedLibrariesImpl.java b/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
index 94495bf..ec8af2e 100644
--- a/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
+++ b/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
@@ -731,7 +731,7 @@
? PackageManager.DELETE_KEEP_DATA : 0;
synchronized (mPm.mInstallLock) {
mDeletePackageHelper.deletePackageLIF(pkg.getPackageName(), null, true,
- mPm.mUserManager.getUserIds(), flags, null,
+ mPm.mUserManager.getUserIds(), flags, new PackageRemovedInfo(),
true);
}
}
diff --git a/services/core/java/com/android/server/pm/StorageEventHelper.java b/services/core/java/com/android/server/pm/StorageEventHelper.java
index 70aa19a..b607502 100644
--- a/services/core/java/com/android/server/pm/StorageEventHelper.java
+++ b/services/core/java/com/android/server/pm/StorageEventHelper.java
@@ -256,13 +256,12 @@
final AndroidPackage pkg = ps.getPkg();
final int deleteFlags = PackageManager.DELETE_KEEP_DATA;
- final PackageRemovedInfo outInfo = new PackageRemovedInfo();
try (PackageFreezer freezer = mPm.freezePackageForDelete(ps.getPackageName(),
UserHandle.USER_ALL, deleteFlags,
"unloadPrivatePackagesInner", ApplicationExitInfo.REASON_OTHER)) {
if (mDeletePackageHelper.deletePackageLIF(ps.getPackageName(), null, false,
- userIds, deleteFlags, outInfo, false)) {
+ userIds, deleteFlags, new PackageRemovedInfo(), false)) {
unloaded.add(pkg);
} else {
Slog.w(TAG, "Failed to unload " + ps.getPath());
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsCollector.java b/services/core/java/com/android/server/power/stats/PowerStatsCollector.java
index abfe9de..e1eb8f0 100644
--- a/services/core/java/com/android/server/power/stats/PowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/PowerStatsCollector.java
@@ -20,7 +20,6 @@
import android.os.ConditionVariable;
import android.os.Handler;
import android.os.PersistableBundle;
-import android.util.FastImmutableArraySet;
import android.util.IndentingPrintWriter;
import android.util.Slog;
@@ -30,8 +29,9 @@
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
import java.util.function.Consumer;
-import java.util.stream.Stream;
/**
* Collects snapshots of power-related system statistics.
@@ -246,8 +246,7 @@
@GuardedBy("this")
@SuppressWarnings("unchecked")
- private volatile FastImmutableArraySet<Consumer<PowerStats>> mConsumerList =
- new FastImmutableArraySet<Consumer<PowerStats>>(new Consumer[0]);
+ private volatile List<Consumer<PowerStats>> mConsumerList = Collections.emptyList();
public PowerStatsCollector(Handler handler, long throttlePeriodMs, Clock clock) {
mHandler = handler;
@@ -262,9 +261,13 @@
@SuppressWarnings("unchecked")
public void addConsumer(Consumer<PowerStats> consumer) {
synchronized (this) {
- mConsumerList = new FastImmutableArraySet<Consumer<PowerStats>>(
- Stream.concat(mConsumerList.stream(), Stream.of(consumer))
- .toArray(Consumer[]::new));
+ if (mConsumerList.contains(consumer)) {
+ return;
+ }
+
+ List<Consumer<PowerStats>> newList = new ArrayList<>(mConsumerList);
+ newList.add(consumer);
+ mConsumerList = Collections.unmodifiableList(newList);
}
}
@@ -275,9 +278,9 @@
@SuppressWarnings("unchecked")
public void removeConsumer(Consumer<PowerStats> consumer) {
synchronized (this) {
- mConsumerList = new FastImmutableArraySet<Consumer<PowerStats>>(
- mConsumerList.stream().filter(c -> c != consumer)
- .toArray(Consumer[]::new));
+ List<Consumer<PowerStats>> newList = new ArrayList<>(mConsumerList);
+ newList.remove(consumer);
+ mConsumerList = Collections.unmodifiableList(newList);
}
}
@@ -302,8 +305,9 @@
if (stats == null) {
return;
}
- for (Consumer<PowerStats> consumer : mConsumerList) {
- consumer.accept(stats);
+ List<Consumer<PowerStats>> consumerList = mConsumerList;
+ for (int i = consumerList.size() - 1; i >= 0; i--) {
+ consumerList.get(i).accept(stats);
}
}
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsScheduler.java b/services/core/java/com/android/server/power/stats/PowerStatsScheduler.java
index 97d872a..121a98b 100644
--- a/services/core/java/com/android/server/power/stats/PowerStatsScheduler.java
+++ b/services/core/java/com/android/server/power/stats/PowerStatsScheduler.java
@@ -18,7 +18,6 @@
import android.annotation.DurationMillisLong;
import android.app.AlarmManager;
-import android.content.Context;
import android.os.ConditionVariable;
import android.os.Handler;
import android.util.IndentingPrintWriter;
@@ -30,6 +29,7 @@
import java.io.PrintWriter;
import java.util.Calendar;
import java.util.concurrent.TimeUnit;
+import java.util.function.Supplier;
/**
* Controls the frequency at which {@link PowerStatsSpan}'s are generated and stored in
@@ -39,7 +39,7 @@
private static final long MINUTE_IN_MILLIS = TimeUnit.MINUTES.toMillis(1);
private static final long HOUR_IN_MILLIS = TimeUnit.HOURS.toMillis(1);
- private final Context mContext;
+ private final AlarmScheduler mAlarmScheduler;
private boolean mEnablePeriodicPowerStatsCollection;
@DurationMillisLong
private final long mAggregatedPowerStatsSpanDuration;
@@ -49,24 +49,38 @@
private final Clock mClock;
private final MonotonicClock mMonotonicClock;
private final Handler mHandler;
- private final BatteryStatsImpl mBatteryStats;
+ private final Runnable mPowerStatsCollector;
+ private final Supplier<Long> mEarliestAvailableBatteryHistoryTimeMs;
private final PowerStatsAggregator mPowerStatsAggregator;
private long mLastSavedSpanEndMonotonicTime;
- public PowerStatsScheduler(Context context, PowerStatsAggregator powerStatsAggregator,
+ /**
+ * External dependency on AlarmManager.
+ */
+ public interface AlarmScheduler {
+ /**
+ * Should use AlarmManager to schedule an inexact, non-wakeup alarm.
+ */
+ void scheduleAlarm(long triggerAtMillis, String tag,
+ AlarmManager.OnAlarmListener onAlarmListener, Handler handler);
+ }
+
+ public PowerStatsScheduler(Runnable powerStatsCollector,
+ PowerStatsAggregator powerStatsAggregator,
@DurationMillisLong long aggregatedPowerStatsSpanDuration,
@DurationMillisLong long powerStatsAggregationPeriod, PowerStatsStore powerStatsStore,
- Clock clock, MonotonicClock monotonicClock, Handler handler,
- BatteryStatsImpl batteryStats) {
- mContext = context;
+ AlarmScheduler alarmScheduler, Clock clock, MonotonicClock monotonicClock,
+ Supplier<Long> earliestAvailableBatteryHistoryTimeMs, Handler handler) {
mPowerStatsAggregator = powerStatsAggregator;
mAggregatedPowerStatsSpanDuration = aggregatedPowerStatsSpanDuration;
mPowerStatsAggregationPeriod = powerStatsAggregationPeriod;
mPowerStatsStore = powerStatsStore;
+ mAlarmScheduler = alarmScheduler;
mClock = clock;
mMonotonicClock = monotonicClock;
mHandler = handler;
- mBatteryStats = batteryStats;
+ mPowerStatsCollector = powerStatsCollector;
+ mEarliestAvailableBatteryHistoryTimeMs = earliestAvailableBatteryHistoryTimeMs;
}
/**
@@ -81,9 +95,8 @@
}
private void scheduleNextPowerStatsAggregation() {
- AlarmManager alarmManager = mContext.getSystemService(AlarmManager.class);
- alarmManager.set(AlarmManager.ELAPSED_REALTIME,
- mClock.elapsedRealtime() + mPowerStatsAggregationPeriod, "PowerStats",
+ mAlarmScheduler.scheduleAlarm(mClock.elapsedRealtime() + mPowerStatsAggregationPeriod,
+ "PowerStats",
() -> {
schedulePowerStatsAggregation();
mHandler.post(this::scheduleNextPowerStatsAggregation);
@@ -96,7 +109,7 @@
@VisibleForTesting
public void schedulePowerStatsAggregation() {
// Catch up the power stats collectors
- mBatteryStats.schedulePowerStatsSampleCollection();
+ mPowerStatsCollector.run();
mHandler.post(this::aggregateAndStorePowerStats);
}
@@ -105,7 +118,7 @@
long currentMonotonicTime = mMonotonicClock.monotonicTime();
long startTime = getLastSavedSpanEndMonotonicTime();
if (startTime < 0) {
- startTime = mBatteryStats.getHistory().getStartTime();
+ startTime = mEarliestAvailableBatteryHistoryTimeMs.get();
}
long endTimeMs = alignToWallClock(startTime + mAggregatedPowerStatsSpanDuration,
mAggregatedPowerStatsSpanDuration, currentMonotonicTime, currentTimeMillis);
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index d903ad4..6f27507 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -41,6 +41,7 @@
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.UserInfo;
+import android.content.res.Resources;
import android.graphics.Rect;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
@@ -2080,45 +2081,6 @@
}
@Override
- public void stopPlayback(IBinder sessionToken, int mode, int userId) {
- final int callingUid = Binder.getCallingUid();
- final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
- userId, "stopPlayback");
- final long identity = Binder.clearCallingIdentity();
- try {
- synchronized (mLock) {
- try {
- getSessionLocked(sessionToken, callingUid, resolvedUserId).stopPlayback(
- mode);
- } catch (RemoteException | SessionNotFoundException e) {
- Slog.e(TAG, "error in stopPlayback(mode)", e);
- }
- }
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- @Override
- public void startPlayback(IBinder sessionToken, int userId) {
- final int callingUid = Binder.getCallingUid();
- final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
- userId, "stopPlayback");
- final long identity = Binder.clearCallingIdentity();
- try {
- synchronized (mLock) {
- try {
- getSessionLocked(sessionToken, callingUid, resolvedUserId).startPlayback();
- } catch (RemoteException | SessionNotFoundException e) {
- Slog.e(TAG, "error in startPlayback()", e);
- }
- }
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- @Override
public void timeShiftPlay(IBinder sessionToken, final Uri recordedProgramUri, int userId) {
final int callingUid = Binder.getCallingUid();
final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
diff --git a/services/core/java/com/android/server/vibrator/VibratorControlService.java b/services/core/java/com/android/server/vibrator/VibratorControlService.java
new file mode 100644
index 0000000..2eeb903
--- /dev/null
+++ b/services/core/java/com/android/server/vibrator/VibratorControlService.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vibrator;
+
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.frameworks.vibrator.IVibratorControlService;
+import android.frameworks.vibrator.IVibratorController;
+import android.frameworks.vibrator.VibrationParam;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import java.util.Objects;
+
+/**
+ * Implementation of {@link IVibratorControlService} which allows the registration of
+ * {@link IVibratorController} to set and receive vibration params.
+ *
+ * @hide
+ */
+public final class VibratorControlService extends IVibratorControlService.Stub {
+ private static final String TAG = "VibratorControlService";
+
+ private final VibratorControllerHolder mVibratorControllerHolder;
+ private final Object mLock;
+
+ public VibratorControlService(VibratorControllerHolder vibratorControllerHolder, Object lock) {
+ mVibratorControllerHolder = vibratorControllerHolder;
+ mLock = lock;
+ }
+
+ @Override
+ public void registerVibratorController(IVibratorController controller)
+ throws RemoteException {
+ synchronized (mLock) {
+ mVibratorControllerHolder.setVibratorController(controller);
+ }
+ }
+
+ @Override
+ public void unregisterVibratorController(@NonNull IVibratorController controller)
+ throws RemoteException {
+ Objects.requireNonNull(controller);
+
+ synchronized (mLock) {
+ if (mVibratorControllerHolder.getVibratorController() == null) {
+ Slog.w(TAG, "Received request to unregister IVibratorController = "
+ + controller + ", but no controller was previously registered. Request "
+ + "Ignored.");
+ return;
+ }
+ if (!Objects.equals(mVibratorControllerHolder.getVibratorController().asBinder(),
+ controller.asBinder())) {
+ Slog.wtf(TAG, "Failed to unregister IVibratorController. The provided "
+ + "controller doesn't match the registered one. " + this);
+ return;
+ }
+ mVibratorControllerHolder.setVibratorController(null);
+ }
+ }
+
+ @Override
+ public void setVibrationParams(
+ @SuppressLint("ArrayReturn") VibrationParam[] params, IVibratorController token)
+ throws RemoteException {
+ // TODO(b/305939964): Add set vibration implementation.
+ }
+
+ @Override
+ public void clearVibrationParams(int types, IVibratorController token) throws RemoteException {
+ // TODO(b/305939964): Add clear vibration implementation.
+ }
+
+ @Override
+ public void onRequestVibrationParamsComplete(
+ IBinder requestToken, @SuppressLint("ArrayReturn") VibrationParam[] result)
+ throws RemoteException {
+ // TODO(305942827): Cache the vibration params in VibrationScaler
+ }
+
+ @Override
+ public int getInterfaceVersion() throws RemoteException {
+ return this.VERSION;
+ }
+
+ @Override
+ public String getInterfaceHash() throws RemoteException {
+ return this.HASH;
+ }
+}
diff --git a/services/core/java/com/android/server/vibrator/VibratorControllerHolder.java b/services/core/java/com/android/server/vibrator/VibratorControllerHolder.java
new file mode 100644
index 0000000..63e69db
--- /dev/null
+++ b/services/core/java/com/android/server/vibrator/VibratorControllerHolder.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vibrator;
+
+import android.annotation.NonNull;
+import android.frameworks.vibrator.IVibratorController;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+/**
+ * Holder class for {@link IVibratorController}.
+ *
+ * @hide
+ */
+public final class VibratorControllerHolder implements IBinder.DeathRecipient {
+ private static final String TAG = "VibratorControllerHolder";
+
+ private IVibratorController mVibratorController;
+
+ public IVibratorController getVibratorController() {
+ return mVibratorController;
+ }
+
+ /**
+ * Sets the {@link IVibratorController} in {@link VibratorControllerHolder} to the new
+ * controller. This will also take care of registering and unregistering death notifications
+ * for the cached {@link IVibratorController}.
+ */
+ public void setVibratorController(IVibratorController controller) {
+ try {
+ if (mVibratorController != null) {
+ mVibratorController.asBinder().unlinkToDeath(this, 0);
+ }
+ mVibratorController = controller;
+ if (mVibratorController != null) {
+ mVibratorController.asBinder().linkToDeath(this, 0);
+ }
+ } catch (RemoteException e) {
+ Slog.wtf(TAG, "Failed to set IVibratorController: " + this, e);
+ }
+ }
+
+ @Override
+ public void binderDied(@NonNull IBinder deadBinder) {
+ if (deadBinder == mVibratorController.asBinder()) {
+ setVibratorController(null);
+ }
+ }
+
+ @Override
+ public void binderDied() {
+ // Should not be used as binderDied(IBinder who) is overridden.
+ Slog.wtf(TAG, "binderDied() called unexpectedly.");
+ }
+}
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index 7d4bd3b..fc824abd 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -91,6 +91,8 @@
public class VibratorManagerService extends IVibratorManagerService.Stub {
private static final String TAG = "VibratorManagerService";
private static final String EXTERNAL_VIBRATOR_SERVICE = "external_vibrator_service";
+ private static final String VIBRATOR_CONTROL_SERVICE =
+ "android.frameworks.vibrator.IVibratorControlService/default";
private static final boolean DEBUG = false;
private static final VibrationAttributes DEFAULT_ATTRIBUTES =
new VibrationAttributes.Builder().build();
@@ -269,6 +271,11 @@
context.registerReceiver(mIntentReceiver, filter, Context.RECEIVER_NOT_EXPORTED);
injector.addService(EXTERNAL_VIBRATOR_SERVICE, new ExternalVibratorService());
+ if (ServiceManager.isDeclared(VIBRATOR_CONTROL_SERVICE)) {
+ injector.addService(VIBRATOR_CONTROL_SERVICE,
+ new VibratorControlService(new VibratorControllerHolder(), mLock));
+ }
+
}
/** Finish initialization at boot phase {@link SystemService#PHASE_SYSTEM_SERVICES_READY}. */
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 03d6c2c..ae10ce3 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -5149,8 +5149,13 @@
/** @return the orientation of the display when it's rotation is ROTATION_0. */
int getNaturalOrientation() {
- return mBaseDisplayWidth < mBaseDisplayHeight
- ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
+ final Configuration config = getConfiguration();
+ if (config.windowConfiguration.getDisplayRotation() == ROTATION_0) {
+ return config.orientation;
+ }
+ final Rect frame = mDisplayPolicy.getDecorInsetsInfo(
+ ROTATION_0, mBaseDisplayWidth, mBaseDisplayHeight).mConfigFrame;
+ return frame.width() <= frame.height() ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
}
void performLayout(boolean initial, boolean updateInputWindows) {
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index a872fd0..4b99432 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -63,6 +63,7 @@
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_START_SHORTCUT;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
+import static com.android.server.wm.ActivityRecord.State.PAUSING;
import static com.android.server.wm.ActivityTaskManagerService.enforceTaskPermission;
import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.ActivityTaskSupervisor.REMOVE_FROM_RECENTS;
@@ -1178,6 +1179,14 @@
mService.mRootWindowContainer.moveActivityToPinnedRootTask(
pipActivity, null /* launchIntoPipHostActivity */,
"moveActivityToPinnedRootTask", null /* transition */, entryBounds);
+
+ // Continue the pausing process after potential task reparenting.
+ if (pipActivity.isState(PAUSING) && pipActivity.mPauseSchedulePendingForPip) {
+ pipActivity.getTask().schedulePauseActivity(
+ pipActivity, false /* userLeaving */,
+ false /* pauseImmediately */, true /* autoEnteringPip */, "auto-pip");
+ }
+
effects |= TRANSACT_EFFECTS_LIFECYCLE;
break;
}
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index fd5f7a3..5721750 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -66,6 +66,7 @@
import android.content.res.Configuration;
import android.os.Binder;
import android.os.Build;
+import android.os.DeadObjectException;
import android.os.FactoryTest;
import android.os.LocaleList;
import android.os.Message;
@@ -1675,6 +1676,10 @@
@NonNull ClientTransactionItem transactionItem) {
try {
mAtm.getLifecycleManager().scheduleTransactionItem(thread, transactionItem);
+ } catch (DeadObjectException e) {
+ // Expected if the process has been killed.
+ Slog.w(TAG_CONFIGURATION, "Failed for dead process. ClientTransactionItem="
+ + transactionItem + " owner=" + mOwner);
} catch (Exception e) {
Slog.e(TAG_CONFIGURATION, "Failed to schedule ClientTransactionItem="
+ transactionItem + " owner=" + mOwner, e);
diff --git a/services/foldables/devicestateprovider/src/com/android/server/policy/feature/device_state_flags.aconfig b/services/foldables/devicestateprovider/src/com/android/server/policy/feature/device_state_flags.aconfig
index 47c2a1b..29e258c 100644
--- a/services/foldables/devicestateprovider/src/com/android/server/policy/feature/device_state_flags.aconfig
+++ b/services/foldables/devicestateprovider/src/com/android/server/policy/feature/device_state_flags.aconfig
@@ -5,4 +5,12 @@
namespace: "display_manager"
description: "Feature flag for dual display blocking"
bug: "278667199"
+}
+
+flag {
+ name: "enable_foldables_posture_based_closed_state"
+ namespace: "windowing_frontend"
+ description: "Enables smarter closed device state state for foldable devices"
+ bug: "309792734"
+ is_fixed_read_only: true
}
\ No newline at end of file
diff --git a/services/manifest_services.xml b/services/manifest_services.xml
index 7638915..eae159f 100644
--- a/services/manifest_services.xml
+++ b/services/manifest_services.xml
@@ -4,4 +4,9 @@
<version>1</version>
<fqname>IAltitudeService/default</fqname>
</hal>
+ <hal format="aidl">
+ <name>android.frameworks.vibrator</name>
+ <version>1</version>
+ <fqname>IVibratorControlService/default</fqname>
+ </hal>
</manifest>
diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
index 76d4d55..9739e4b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
@@ -2423,6 +2423,14 @@
}
}));
+ when(mSysPropsMock.getInt(
+ ArgumentMatchers.eq(PROPERTY_RO_SURFACEFLINGER_GAME_DEFAULT_FRAME_RATE),
+ anyInt())).thenReturn(60);
+ when(mSysPropsMock.getBoolean(
+ ArgumentMatchers.eq(PROPERTY_PERSISTENT_GFX_GAME_DEFAULT_FRAME_RATE_ENABLED),
+ ArgumentMatchers.eq(true))).thenReturn(true);
+ gameManagerService.onBootCompleted();
+
// Set up a game in the foreground.
String[] packages = {mPackageName};
when(mMockPackageManager.getPackagesForUid(DEFAULT_PACKAGE_UID)).thenReturn(packages);
@@ -2430,12 +2438,6 @@
DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_TOP, 0, 0);
// Toggle game default frame rate on.
- when(mSysPropsMock.getInt(
- ArgumentMatchers.eq(PROPERTY_RO_SURFACEFLINGER_GAME_DEFAULT_FRAME_RATE),
- anyInt())).thenReturn(60);
- when(mSysPropsMock.getBoolean(
- ArgumentMatchers.eq(PROPERTY_PERSISTENT_GFX_GAME_DEFAULT_FRAME_RATE_ENABLED),
- ArgumentMatchers.eq(true))).thenReturn(true);
gameManagerService.toggleGameDefaultFrameRate(true);
// Verify that:
diff --git a/services/tests/powerstatstests/Android.bp b/services/tests/powerstatstests/Android.bp
index 07197b1..05e0e8f 100644
--- a/services/tests/powerstatstests/Android.bp
+++ b/services/tests/powerstatstests/Android.bp
@@ -3,6 +3,20 @@
default_applicable_licenses: ["frameworks_base_license"],
}
+filegroup {
+ name: "power_stats_ravenwood_tests",
+ srcs: [
+ "src/com/android/server/power/stats/AggregatedPowerStatsProcessorTest.java",
+ "src/com/android/server/power/stats/AggregatedPowerStatsTest.java",
+ "src/com/android/server/power/stats/MultiStateStatsTest.java",
+ "src/com/android/server/power/stats/PowerStatsAggregatorTest.java",
+ "src/com/android/server/power/stats/PowerStatsCollectorTest.java",
+ "src/com/android/server/power/stats/PowerStatsSchedulerTest.java",
+ "src/com/android/server/power/stats/PowerStatsStoreTest.java",
+ "src/com/android/server/power/stats/PowerStatsUidResolverTest.java",
+ ],
+}
+
android_test {
name: "PowerStatsTests",
@@ -12,8 +26,7 @@
],
exclude_srcs: [
- "src/com/android/server/power/stats/MultiStateStatsTest.java",
- "src/com/android/server/power/stats/PowerStatsStoreTest.java",
+ ":power_stats_ravenwood_tests",
],
static_libs: [
@@ -65,10 +78,12 @@
"modules-utils-binary-xml",
"androidx.annotation_annotation",
"androidx.test.rules",
+ "truth",
+ "mockito_ravenwood",
],
srcs: [
- "src/com/android/server/power/stats/MultiStateStatsTest.java",
- "src/com/android/server/power/stats/PowerStatsStoreTest.java",
+ ":power_stats_ravenwood_tests",
+ "src/com/android/server/power/stats/MockClock.java",
],
auto_gen_config: true,
}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatePowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatedPowerStatsProcessorTest.java
similarity index 99%
rename from services/tests/powerstatstests/src/com/android/server/power/stats/AggregatePowerStatsProcessorTest.java
rename to services/tests/powerstatstests/src/com/android/server/power/stats/AggregatedPowerStatsProcessorTest.java
index 6d61dc8..af83be0 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatePowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatedPowerStatsProcessorTest.java
@@ -35,7 +35,7 @@
@RunWith(AndroidJUnit4.class)
@SmallTest
-public class AggregatePowerStatsProcessorTest {
+public class AggregatedPowerStatsProcessorTest {
@Test
public void createPowerEstimationPlan_allDeviceStatesPresentInUidStates() {
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MultiStateStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MultiStateStatsTest.java
index e8f46b3..1b045c5 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/MultiStateStatsTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MultiStateStatsTest.java
@@ -22,12 +22,10 @@
import static org.junit.Assert.assertThrows;
import android.os.BatteryConsumer;
-import android.platform.test.ravenwood.RavenwoodRule;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -38,11 +36,6 @@
@RunWith(AndroidJUnit4.class)
@SmallTest
public class MultiStateStatsTest {
-
- @Rule
- public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
- .build();
-
public static final int DIMENSION_COUNT = 2;
@Test
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java
index 6704987..2456636 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java
@@ -24,7 +24,6 @@
import android.os.BatteryConsumer;
import android.os.BatteryStats;
import android.os.PersistableBundle;
-import android.text.format.DateFormat;
import androidx.annotation.NonNull;
import androidx.test.filters.SmallTest;
@@ -39,7 +38,8 @@
import org.junit.runner.RunWith;
import java.text.ParseException;
-import java.util.Calendar;
+import java.text.SimpleDateFormat;
+import java.util.Date;
import java.util.List;
import java.util.TimeZone;
@@ -60,7 +60,7 @@
public void setup() throws ParseException {
mHistory = new BatteryStatsHistory(32, 1024,
mock(BatteryStatsHistory.HistoryStepDetailsCalculator.class), mClock,
- mMonotonicClock);
+ mMonotonicClock, mock(BatteryStatsHistory.TraceDelegate.class));
AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig();
config.trackPowerComponent(TEST_POWER_COMPONENT)
@@ -179,9 +179,9 @@
@NonNull
private static CharSequence formatDateTime(long timeInMillis) {
- Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
- cal.setTimeInMillis(timeInMillis);
- return DateFormat.format("yyyy-MM-dd hh:mm:ss", cal);
+ SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
+ format.getCalendar().setTimeZone(TimeZone.getTimeZone("GMT"));
+ return format.format(new Date(timeInMillis));
}
@Test
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsCollectorTest.java
index 330f698..17a7d3e 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsCollectorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsCollectorTest.java
@@ -22,6 +22,7 @@
import android.os.Handler;
import android.os.HandlerThread;
import android.os.PersistableBundle;
+import android.platform.test.ravenwood.RavenwoodRule;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -29,12 +30,18 @@
import com.android.internal.os.PowerStats;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class PowerStatsCollectorTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
+ .setProvideMainThread(true)
+ .build();
+
private final MockClock mMockClock = new MockClock();
private final HandlerThread mHandlerThread = new HandlerThread("test");
private Handler mHandler;
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsSchedulerTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsSchedulerTest.java
index 7257a94..beec661 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsSchedulerTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsSchedulerTest.java
@@ -24,26 +24,26 @@
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.content.Context;
import android.os.ConditionVariable;
import android.os.Handler;
import android.os.HandlerThread;
+import android.platform.test.ravenwood.RavenwoodRule;
-import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.os.MonotonicClock;
-import com.android.internal.os.PowerProfile;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
import java.time.Duration;
import java.time.Instant;
+import java.util.ArrayList;
import java.util.List;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
@@ -51,39 +51,46 @@
@RunWith(AndroidJUnit4.class)
public class PowerStatsSchedulerTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
+ .setProvideMainThread(true)
+ .build();
+
private PowerStatsStore mPowerStatsStore;
private Handler mHandler;
private MockClock mClock = new MockClock();
private MonotonicClock mMonotonicClock = new MonotonicClock(0, mClock);
- private MockBatteryStatsImpl mBatteryStats;
private PowerStatsScheduler mPowerStatsScheduler;
- private PowerProfile mPowerProfile;
private PowerStatsAggregator mPowerStatsAggregator;
private AggregatedPowerStatsConfig mAggregatedPowerStatsConfig;
+ private List<Long> mScheduledAlarms = new ArrayList<>();
+ private boolean mPowerStatsCollectionOccurred;
+
+ private static final int START_REALTIME = 7654321;
@Before
@SuppressWarnings("GuardedBy")
- public void setup() {
- final Context context = InstrumentationRegistry.getContext();
-
+ public void setup() throws IOException {
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
mClock.currentTime = Instant.parse("2023-01-02T03:04:05.00Z").toEpochMilli();
- mClock.realtime = 7654321;
+ mClock.realtime = START_REALTIME;
HandlerThread bgThread = new HandlerThread("bg thread");
bgThread.start();
- File systemDir = context.getCacheDir();
mHandler = new Handler(bgThread.getLooper());
mAggregatedPowerStatsConfig = new AggregatedPowerStatsConfig();
- mPowerStatsStore = new PowerStatsStore(systemDir, mHandler, mAggregatedPowerStatsConfig);
- mPowerProfile = mock(PowerProfile.class);
- when(mPowerProfile.getAveragePower(PowerProfile.POWER_FLASHLIGHT)).thenReturn(1000000.0);
- mBatteryStats = new MockBatteryStatsImpl(mClock).setPowerProfile(mPowerProfile);
+ mPowerStatsStore = new PowerStatsStore(
+ Files.createTempDirectory("PowerStatsSchedulerTest").toFile(),
+ mHandler, mAggregatedPowerStatsConfig);
mPowerStatsAggregator = mock(PowerStatsAggregator.class);
- mPowerStatsScheduler = new PowerStatsScheduler(context, mPowerStatsAggregator,
- TimeUnit.MINUTES.toMillis(30), TimeUnit.HOURS.toMillis(1), mPowerStatsStore, mClock,
- mMonotonicClock, mHandler, mBatteryStats);
+ mPowerStatsScheduler = new PowerStatsScheduler(
+ () -> mPowerStatsCollectionOccurred = true,
+ mPowerStatsAggregator, TimeUnit.MINUTES.toMillis(30), TimeUnit.HOURS.toMillis(1),
+ mPowerStatsStore,
+ ((triggerAtMillis, tag, onAlarmListener, handler) ->
+ mScheduledAlarms.add(triggerAtMillis)),
+ mClock, mMonotonicClock, () -> 12345L, mHandler);
}
@Test
@@ -113,7 +120,7 @@
long endTimeWallClock =
mClock.currentTime - (mMonotonicClock.monotonicTime() - endTime);
- assertThat(startTime).isEqualTo(7654321 + 123);
+ assertThat(startTime).isEqualTo(START_REALTIME + 123);
assertThat(endTime - startTime).isAtLeast(TimeUnit.MINUTES.toMillis(30));
assertThat(Instant.ofEpochMilli(endTimeWallClock))
.isEqualTo(Instant.parse("2023-01-02T04:00:00Z"));
@@ -142,11 +149,15 @@
}).when(mPowerStatsAggregator).aggregatePowerStats(anyLong(), anyLong(),
any(Consumer.class));
- mPowerStatsScheduler.schedulePowerStatsAggregation();
+ mPowerStatsScheduler.start(/*enabled*/ true);
ConditionVariable done = new ConditionVariable();
mHandler.post(done::open);
done.block();
+ assertThat(mPowerStatsCollectionOccurred).isTrue();
+ assertThat(mScheduledAlarms).containsExactly(
+ START_REALTIME + TimeUnit.MINUTES.toMillis(90) + TimeUnit.HOURS.toMillis(1));
+
verify(mPowerStatsAggregator, times(2))
.aggregatePowerStats(anyLong(), anyLong(), any(Consumer.class));
@@ -155,7 +166,7 @@
// Skip the first entry, which was placed in the store at the beginning of this test
PowerStatsSpan.TimeFrame timeFrame1 = contents.get(1).getTimeFrames().get(0);
PowerStatsSpan.TimeFrame timeFrame2 = contents.get(2).getTimeFrames().get(0);
- assertThat(timeFrame1.startMonotonicTime).isEqualTo(7654321 + 123);
+ assertThat(timeFrame1.startMonotonicTime).isEqualTo(START_REALTIME + 123);
assertThat(timeFrame2.startMonotonicTime)
.isEqualTo(timeFrame1.startMonotonicTime + timeFrame1.duration);
assertThat(Instant.ofEpochMilli(timeFrame2.startTime))
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 4a537df..36d55a4 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -621,10 +621,8 @@
});
// TODO (b/291907312): remove feature flag
- mSetFlagsRule.enableFlags(android.app.Flags.FLAG_VISIT_RISKY_URIS);
- mSetFlagsRule.disableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR);
mSetFlagsRule.disableFlags(Flags.FLAG_REFACTOR_ATTENTION_HELPER,
- Flags.FLAG_POLITE_NOTIFICATIONS, android.app.Flags.FLAG_MODES_API);
+ Flags.FLAG_POLITE_NOTIFICATIONS);
initNMS();
}
@@ -6274,21 +6272,15 @@
verify(visitor, times(1)).accept(eq(personIcon3.getUri()));
}
- private PendingIntent getPendingIntentWithUri(Uri uri) {
- return PendingIntent.getActivity(mContext, 0,
- new Intent("action", uri),
- PendingIntent.FLAG_IMMUTABLE);
- }
-
@Test
- public void testVisitUris_callStyle_ongoingCall() {
+ public void testVisitUris_callStyle() {
Icon personIcon = Icon.createWithContentUri("content://media/person");
Icon verificationIcon = Icon.createWithContentUri("content://media/verification");
Person callingPerson = new Person.Builder().setName("Someone")
.setIcon(personIcon)
.build();
- Uri hangUpUri = Uri.parse("content://intent/hangup");
- PendingIntent hangUpIntent = getPendingIntentWithUri(hangUpUri);
+ PendingIntent hangUpIntent = PendingIntent.getActivity(mContext, 0, new Intent(),
+ PendingIntent.FLAG_IMMUTABLE);
Notification n = new Notification.Builder(mContext, "a")
.setStyle(Notification.CallStyle.forOngoingCall(callingPerson, hangUpIntent)
.setVerificationIcon(verificationIcon))
@@ -6301,35 +6293,6 @@
verify(visitor, times(1)).accept(eq(personIcon.getUri()));
verify(visitor, times(1)).accept(eq(verificationIcon.getUri()));
- verify(visitor, times(1)).accept(eq(hangUpUri));
- }
-
- @Test
- public void testVisitUris_callStyle_incomingCall() {
- Icon personIcon = Icon.createWithContentUri("content://media/person");
- Icon verificationIcon = Icon.createWithContentUri("content://media/verification");
- Person callingPerson = new Person.Builder().setName("Someone")
- .setIcon(personIcon)
- .build();
- Uri answerUri = Uri.parse("content://intent/answer");
- PendingIntent answerIntent = getPendingIntentWithUri(answerUri);
- Uri declineUri = Uri.parse("content://intent/decline");
- PendingIntent declineIntent = getPendingIntentWithUri(declineUri);
- Notification n = new Notification.Builder(mContext, "a")
- .setStyle(Notification.CallStyle.forIncomingCall(callingPerson, declineIntent,
- answerIntent)
- .setVerificationIcon(verificationIcon))
- .setContentTitle("Calling...")
- .setSmallIcon(android.R.drawable.sym_def_app_icon)
- .build();
-
- Consumer<Uri> visitor = (Consumer<Uri>) spy(Consumer.class);
- n.visitUris(visitor);
-
- verify(visitor, times(1)).accept(eq(personIcon.getUri()));
- verify(visitor, times(1)).accept(eq(verificationIcon.getUri()));
- verify(visitor, times(1)).accept(eq(answerIntent.getIntent().getData()));
- verify(visitor, times(1)).accept(eq(declineUri));
}
@Test
@@ -6381,74 +6344,20 @@
public void testVisitUris_wearableExtender() {
Icon actionIcon = Icon.createWithContentUri("content://media/action");
Icon wearActionIcon = Icon.createWithContentUri("content://media/wearAction");
- Uri displayIntentUri = Uri.parse("content://intent/display");
- PendingIntent displayIntent = getPendingIntentWithUri(displayIntentUri);
- Uri actionIntentUri = Uri.parse("content://intent/action");
- PendingIntent actionIntent = getPendingIntentWithUri(actionIntentUri);
- Uri wearActionIntentUri = Uri.parse("content://intent/wear");
- PendingIntent wearActionIntent = getPendingIntentWithUri(wearActionIntentUri);
+ PendingIntent intent = PendingIntent.getActivity(mContext, 0, new Intent(),
+ PendingIntent.FLAG_IMMUTABLE);
Notification n = new Notification.Builder(mContext, "a")
.setSmallIcon(android.R.drawable.sym_def_app_icon)
- .addAction(
- new Notification.Action.Builder(actionIcon, "Hey!", actionIntent).build())
- .extend(new Notification.WearableExtender()
- .setDisplayIntent(displayIntent)
- .addAction(new Notification.Action.Builder(wearActionIcon, "Wear!",
- wearActionIntent)
- .build()))
+ .addAction(new Notification.Action.Builder(actionIcon, "Hey!", intent).build())
+ .extend(new Notification.WearableExtender().addAction(
+ new Notification.Action.Builder(wearActionIcon, "Wear!", intent).build()))
.build();
Consumer<Uri> visitor = (Consumer<Uri>) spy(Consumer.class);
n.visitUris(visitor);
verify(visitor).accept(eq(actionIcon.getUri()));
- verify(visitor, times(1)).accept(eq(actionIntentUri));
verify(visitor).accept(eq(wearActionIcon.getUri()));
- verify(visitor, times(1)).accept(eq(wearActionIntentUri));
- }
-
- @Test
- public void testVisitUris_tvExtender() {
- Uri contentIntentUri = Uri.parse("content://intent/content");
- PendingIntent contentIntent = getPendingIntentWithUri(contentIntentUri);
- Uri deleteIntentUri = Uri.parse("content://intent/delete");
- PendingIntent deleteIntent = getPendingIntentWithUri(deleteIntentUri);
- Notification n = new Notification.Builder(mContext, "a")
- .extend(
- new Notification.TvExtender()
- .setContentIntent(contentIntent)
- .setDeleteIntent(deleteIntent))
- .build();
-
- Consumer<Uri> visitor = (Consumer<Uri>) spy(Consumer.class);
- n.visitUris(visitor);
-
- verify(visitor, times(1)).accept(eq(contentIntentUri));
- verify(visitor, times(1)).accept(eq(deleteIntentUri));
- }
-
- @Test
- public void testVisitUris_carExtender() {
- final String testParticipant = "testParticipant";
- Uri readPendingIntentUri = Uri.parse("content://intent/read");
- PendingIntent readPendingIntent = getPendingIntentWithUri(readPendingIntentUri);
- Uri replyPendingIntentUri = Uri.parse("content://intent/reply");
- PendingIntent replyPendingIntent = getPendingIntentWithUri(replyPendingIntentUri);
- final RemoteInput testRemoteInput = new RemoteInput.Builder("key").build();
-
- Notification n = new Notification.Builder(mContext, "a")
- .extend(new Notification.CarExtender().setUnreadConversation(
- new Notification.CarExtender.Builder(testParticipant)
- .setReadPendingIntent(readPendingIntent)
- .setReplyAction(replyPendingIntent, testRemoteInput)
- .build()))
- .build();
-
- Consumer<Uri> visitor = (Consumer<Uri>) spy(Consumer.class);
- n.visitUris(visitor);
-
- verify(visitor, times(1)).accept(eq(readPendingIntentUri));
- verify(visitor, times(1)).accept(eq(replyPendingIntentUri));
}
@Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java
index ea948ca..44dbe385 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java
@@ -146,10 +146,6 @@
.put(Notification.Action.Builder.class, "extend")
// Overwrites icon supplied to constructor.
.put(Notification.BubbleMetadata.Builder.class, "setIcon")
- // Overwrites intent supplied to constructor.
- .put(Notification.BubbleMetadata.Builder.class, "setIntent")
- // Overwrites intent supplied to constructor.
- .put(Notification.BubbleMetadata.Builder.class, "setDeleteIntent")
// Discards previously-added actions.
.put(RemoteViews.class, "mergeRemoteViews")
.build();
@@ -684,14 +680,14 @@
}
if (clazz == Intent.class) {
- return new Intent("action", generateUri(where.plus(Intent.class)));
+ // TODO(b/281044385): Are Intent Uris (new Intent(String,Uri)) relevant?
+ return new Intent("action");
}
if (clazz == PendingIntent.class) {
- // PendingIntent can have an Intent with a Uri.
- Uri intentUri = generateUri(where.plus(PendingIntent.class));
- return PendingIntent.getActivity(mContext, 0,
- new Intent("action", intentUri),
+ // PendingIntent can have an Intent with a Uri but those are inaccessible and
+ // not inspected.
+ return PendingIntent.getActivity(mContext, 0, new Intent("action"),
PendingIntent.FLAG_IMMUTABLE);
}
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java
new file mode 100644
index 0000000..49efd1b
--- /dev/null
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vibrator;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.RemoteException;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class VibratorControlServiceTest {
+
+ private VibratorControlService mVibratorControlService;
+ private final Object mLock = new Object();
+
+ @Before
+ public void setUp() throws Exception {
+ mVibratorControlService = new VibratorControlService(new VibratorControllerHolder(), mLock);
+ }
+
+ @Test
+ public void testRegisterVibratorController() throws RemoteException {
+ FakeVibratorController fakeController = new FakeVibratorController();
+ mVibratorControlService.registerVibratorController(fakeController);
+
+ assertThat(fakeController.isLinkedToDeath).isTrue();
+ }
+
+ @Test
+ public void testUnregisterVibratorController_providingTheRegisteredController_performsRequest()
+ throws RemoteException {
+ FakeVibratorController fakeController = new FakeVibratorController();
+ mVibratorControlService.registerVibratorController(fakeController);
+ mVibratorControlService.unregisterVibratorController(fakeController);
+ assertThat(fakeController.isLinkedToDeath).isFalse();
+ }
+
+ @Test
+ public void testUnregisterVibratorController_providingAnInvalidController_ignoresRequest()
+ throws RemoteException {
+ FakeVibratorController fakeController1 = new FakeVibratorController();
+ FakeVibratorController fakeController2 = new FakeVibratorController();
+ mVibratorControlService.registerVibratorController(fakeController1);
+
+ mVibratorControlService.unregisterVibratorController(fakeController2);
+ assertThat(fakeController1.isLinkedToDeath).isTrue();
+ }
+}
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControllerHolderTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControllerHolderTest.java
new file mode 100644
index 0000000..79abe21
--- /dev/null
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControllerHolderTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vibrator;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.RemoteException;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class VibratorControllerHolderTest {
+
+ private final FakeVibratorController mFakeVibratorController = new FakeVibratorController();
+ private VibratorControllerHolder mVibratorControllerHolder;
+
+ @Before
+ public void setUp() throws Exception {
+ mVibratorControllerHolder = new VibratorControllerHolder();
+ }
+
+ @Test
+ public void testSetVibratorController_linksVibratorControllerToDeath() throws RemoteException {
+ mVibratorControllerHolder.setVibratorController(mFakeVibratorController);
+ assertThat(mVibratorControllerHolder.getVibratorController())
+ .isEqualTo(mFakeVibratorController);
+ assertThat(mFakeVibratorController.isLinkedToDeath).isTrue();
+ }
+
+ @Test
+ public void testSetVibratorController_setControllerToNull_unlinksVibratorControllerToDeath()
+ throws RemoteException {
+ mVibratorControllerHolder.setVibratorController(mFakeVibratorController);
+ mVibratorControllerHolder.setVibratorController(null);
+ assertThat(mFakeVibratorController.isLinkedToDeath).isFalse();
+ assertThat(mVibratorControllerHolder.getVibratorController()).isNull();
+ }
+
+ @Test
+ public void testBinderDied_withValidController_unlinksVibratorControllerToDeath()
+ throws RemoteException {
+ mVibratorControllerHolder.setVibratorController(mFakeVibratorController);
+ mVibratorControllerHolder.binderDied(mFakeVibratorController);
+ assertThat(mFakeVibratorController.isLinkedToDeath).isFalse();
+ assertThat(mVibratorControllerHolder.getVibratorController()).isNull();
+ }
+
+ @Test
+ public void testBinderDied_withInvalidController_ignoresRequest()
+ throws RemoteException {
+ mVibratorControllerHolder.setVibratorController(mFakeVibratorController);
+ FakeVibratorController imposterVibratorController = new FakeVibratorController();
+ mVibratorControllerHolder.binderDied(imposterVibratorController);
+ assertThat(mFakeVibratorController.isLinkedToDeath).isTrue();
+ assertThat(mVibratorControllerHolder.getVibratorController())
+ .isEqualTo(mFakeVibratorController);
+ }
+}
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index 4e9bbe0..d6b2116 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -307,9 +307,10 @@
@Override
void addService(String name, IBinder service) {
- Object serviceInstance = service;
- mExternalVibratorService =
- (VibratorManagerService.ExternalVibratorService) serviceInstance;
+ if (service instanceof VibratorManagerService.ExternalVibratorService) {
+ mExternalVibratorService =
+ (VibratorManagerService.ExternalVibratorService) service;
+ }
}
HapticFeedbackVibrationProvider createHapticFeedbackVibrationProvider(
diff --git a/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorController.java b/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorController.java
new file mode 100644
index 0000000..7e23587
--- /dev/null
+++ b/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorController.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vibrator;
+
+import android.annotation.NonNull;
+import android.frameworks.vibrator.IVibratorController;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+/**
+ * Provides a fake implementation of {@link android.frameworks.vibrator.IVibratorController} for
+ * testing.
+ */
+public final class FakeVibratorController extends IVibratorController.Stub {
+
+ public boolean isLinkedToDeath = false;
+
+ @Override
+ public void requestVibrationParams(int i, long l, IBinder iBinder) throws RemoteException {
+
+ }
+
+ @Override
+ public int getInterfaceVersion() throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public String getInterfaceHash() throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public void linkToDeath(@NonNull DeathRecipient recipient, int flags) {
+ super.linkToDeath(recipient, flags);
+ isLinkedToDeath = true;
+ }
+
+ @Override
+ public boolean unlinkToDeath(@NonNull DeathRecipient recipient, int flags) {
+ isLinkedToDeath = false;
+ return super.unlinkToDeath(recipient, flags);
+ }
+}
diff --git a/services/tests/voiceinteractiontests/Android.bp b/services/tests/voiceinteractiontests/Android.bp
index 744cb63..8a79fe4 100644
--- a/services/tests/voiceinteractiontests/Android.bp
+++ b/services/tests/voiceinteractiontests/Android.bp
@@ -44,6 +44,7 @@
"servicestests-core-utils",
"servicestests-utils-mockito-extended",
"truth",
+ "frameworks-base-testutils",
],
libs: [
diff --git a/services/tests/voiceinteractiontests/src/com/android/server/voiceinteraction/SetSandboxedTrainingDataAllowedTest.java b/services/tests/voiceinteractiontests/src/com/android/server/voiceinteraction/SetSandboxedTrainingDataAllowedTest.java
new file mode 100644
index 0000000..656957c
--- /dev/null
+++ b/services/tests/voiceinteractiontests/src/com/android/server/voiceinteraction/SetSandboxedTrainingDataAllowedTest.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.voiceinteraction;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.Manifest;
+import android.app.ActivityManagerInternal;
+import android.app.AppOpsManager;
+import android.app.role.RoleManager;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.os.PermissionEnforcer;
+import android.os.Process;
+import android.os.test.FakePermissionEnforcer;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.modules.utils.testing.ExtendedMockitoRule;
+import com.android.server.LocalServices;
+import com.android.server.pm.UserManagerInternal;
+import com.android.server.pm.permission.LegacyPermissionManagerInternal;
+import com.android.server.pm.permission.PermissionManagerServiceInternal;
+import com.android.server.wm.ActivityTaskManagerInternal;
+
+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.MockitoAnnotations;
+import org.mockito.quality.Strictness;
+
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class SetSandboxedTrainingDataAllowedTest {
+
+ @Captor private ArgumentCaptor<Integer> mOpIdCaptor, mUidCaptor, mOpModeCaptor;
+
+ @Mock
+ private AppOpsManager mAppOpsManager;
+
+ @Mock
+ private VoiceInteractionManagerServiceImpl mVoiceInteractionManagerServiceImpl;
+
+ private FakePermissionEnforcer mPermissionEnforcer = new FakePermissionEnforcer();
+
+ private Context mContext;
+
+ private VoiceInteractionManagerService mVoiceInteractionManagerService;
+ private VoiceInteractionManagerService.VoiceInteractionManagerServiceStub
+ mVoiceInteractionManagerServiceStub;
+
+ private ApplicationInfo mApplicationInfo = new ApplicationInfo();
+
+ @Rule
+ public final ExtendedMockitoRule mExtendedMockitoRule =
+ new ExtendedMockitoRule.Builder(this)
+ .setStrictness(Strictness.WARN)
+ .mockStatic(LocalServices.class)
+ .mockStatic(PermissionEnforcer.class)
+ .build();
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mContext = spy(ApplicationProvider.getApplicationContext());
+
+ doReturn(mPermissionEnforcer).when(() -> PermissionEnforcer.fromContext(any()));
+ doReturn(mock(PermissionManagerServiceInternal.class)).when(
+ () -> LocalServices.getService(PermissionManagerServiceInternal.class));
+ doReturn(mock(ActivityManagerInternal.class)).when(
+ () -> LocalServices.getService(ActivityManagerInternal.class));
+ doReturn(mock(UserManagerInternal.class)).when(
+ () -> LocalServices.getService(UserManagerInternal.class));
+ doReturn(mock(ActivityTaskManagerInternal.class)).when(
+ () -> LocalServices.getService(ActivityTaskManagerInternal.class));
+ doReturn(mock(LegacyPermissionManagerInternal.class)).when(
+ () -> LocalServices.getService(LegacyPermissionManagerInternal.class));
+ doReturn(mock(RoleManager.class)).when(mContext).getSystemService(RoleManager.class);
+ doReturn(mAppOpsManager).when(mContext).getSystemService(Context.APP_OPS_SERVICE);
+ doReturn(mApplicationInfo).when(mVoiceInteractionManagerServiceImpl).getApplicationInfo();
+
+ mVoiceInteractionManagerService = new VoiceInteractionManagerService(mContext);
+ mVoiceInteractionManagerServiceStub =
+ mVoiceInteractionManagerService.new VoiceInteractionManagerServiceStub();
+ mVoiceInteractionManagerServiceStub.mImpl = mVoiceInteractionManagerServiceImpl;
+ mPermissionEnforcer.grant(Manifest.permission.MANAGE_HOTWORD_DETECTION);
+ }
+
+ @Test
+ public void setIsReceiveSandboxedTrainingDataAllowed_currentAndPreinstalledAssistant_setsOp() {
+ // Set application info so current app is the current and preinstalled assistant.
+ mApplicationInfo.uid = Process.myUid();
+ mApplicationInfo.flags = ApplicationInfo.FLAG_SYSTEM;
+
+ mVoiceInteractionManagerServiceStub.setIsReceiveSandboxedTrainingDataAllowed(
+ /* allowed= */ true);
+
+ verify(mAppOpsManager).setUidMode(mOpIdCaptor.capture(), mUidCaptor.capture(),
+ mOpModeCaptor.capture());
+ assertThat(mOpIdCaptor.getValue()).isEqualTo(
+ AppOpsManager.OP_RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA);
+ assertThat(mOpModeCaptor.getValue()).isEqualTo(AppOpsManager.MODE_ALLOWED);
+ assertThat(mUidCaptor.getValue()).isEqualTo(Process.myUid());
+ }
+
+ @Test
+ public void setIsReceiveSandboxedTrainingDataAllowed_missingPermission_doesNotSetOp() {
+ // Set application info so current app is the current and preinstalled assistant.
+ mApplicationInfo.uid = Process.myUid();
+ mApplicationInfo.flags = ApplicationInfo.FLAG_SYSTEM;
+
+ // Simulate missing MANAGE_HOTWORD_DETECTION permission.
+ mPermissionEnforcer.revoke(Manifest.permission.MANAGE_HOTWORD_DETECTION);
+
+ assertThrows(SecurityException.class,
+ () -> mVoiceInteractionManagerServiceStub.setIsReceiveSandboxedTrainingDataAllowed(
+ /* allowed= */ true));
+
+ verify(mAppOpsManager, never()).setUidMode(anyInt(), anyInt(), anyInt());
+ }
+
+ @Test
+ public void setIsReceiveSandboxedTrainingDataAllowed_notPreinstalledAssistant_doesNotSetOp() {
+ // Set application info so current app is not preinstalled assistant.
+ mApplicationInfo.uid = Process.myUid();
+ mApplicationInfo.flags = ApplicationInfo.FLAG_INSTALLED; // Does not contain FLAG_SYSTEM.
+
+ assertThrows(SecurityException.class,
+ () -> mVoiceInteractionManagerServiceStub.setIsReceiveSandboxedTrainingDataAllowed(
+ /* allowed= */ true));
+
+ verify(mAppOpsManager, never()).setUidMode(anyInt(), anyInt(), anyInt());
+ }
+
+ @Test
+ public void setIsReceiveSandboxedTrainingDataAllowed_notCurrentAssistant_doesNotSetOp() {
+ // Set application info so current app is not current assistant.
+ mApplicationInfo.uid = Process.SHELL_UID; // Set current assistant uid to shell UID.
+ mApplicationInfo.flags = ApplicationInfo.FLAG_SYSTEM;
+
+ assertThrows(SecurityException.class,
+ () -> mVoiceInteractionManagerServiceStub.setIsReceiveSandboxedTrainingDataAllowed(
+ /* allowed= */ true));
+
+ verify(mAppOpsManager, never()).setUidMode(anyInt(), anyInt(), anyInt());
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 71d2504..dfe79bf 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -993,7 +993,9 @@
dc.getDisplayPolicy().getDecorInsetsInfo(ROTATION_0, dc.mBaseDisplayHeight,
dc.mBaseDisplayWidth).mConfigFrame.set(0, 0, 1000, 990);
dc.computeScreenConfiguration(config, ROTATION_0);
+ dc.onRequestedOverrideConfigurationChanged(config);
assertEquals(Configuration.ORIENTATION_LANDSCAPE, config.orientation);
+ assertEquals(Configuration.ORIENTATION_LANDSCAPE, dc.getNaturalOrientation());
}
@Test
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 3d2340c..72db7fe 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -2278,6 +2278,12 @@
"Only the system or holders of the REPORT_USAGE_STATS"
+ " permission are allowed to call reportUserInteraction");
}
+ if (userId != UserHandle.getCallingUserId()) {
+ // Cross-user event reporting.
+ getContext().enforceCallingPermission(
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ "Caller doesn't have INTERACT_ACROSS_USERS_FULL permission");
+ }
} else {
if (!isCallingUidSystem()) {
throw new SecurityException("Only system is allowed to call"
@@ -2287,7 +2293,8 @@
// Verify if this package exists before reporting an event for it.
if (mPackageManagerInternal.getPackageUid(packageName, 0, userId) < 0) {
- throw new IllegalArgumentException("Package " + packageName + "not exist!");
+ throw new IllegalArgumentException("Package " + packageName
+ + " does not exist!");
}
final Event event = new Event(USER_INTERACTION, SystemClock.elapsedRealtime());
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index b214591..6e4f13a 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -2273,9 +2273,9 @@
private boolean isCallerPreinstalledAssistant() {
return mImpl != null
- && mImpl.mInfo.getServiceInfo().applicationInfo.uid == Binder.getCallingUid()
- && (mImpl.mInfo.getServiceInfo().applicationInfo.isSystemApp()
- || mImpl.mInfo.getServiceInfo().applicationInfo.isUpdatedSystemApp());
+ && mImpl.getApplicationInfo().uid == Binder.getCallingUid()
+ && (mImpl.getApplicationInfo().isSystemApp()
+ || mImpl.getApplicationInfo().isUpdatedSystemApp());
}
private void setImplLocked(VoiceInteractionManagerServiceImpl impl) {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index 3c4b58f..7e0cbad 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -40,6 +40,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
@@ -540,6 +541,10 @@
return mInfo.getSupportsLocalInteraction();
}
+ public ApplicationInfo getApplicationInfo() {
+ return mInfo.getServiceInfo().applicationInfo;
+ }
+
public void startListeningVisibleActivityChangedLocked(@NonNull IBinder token) {
if (DEBUG) {
Slog.d(TAG, "startListeningVisibleActivityChangedLocked: token=" + token);
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index b356fde..326b6f5 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -1508,8 +1508,14 @@
public void addOnSubscriptionsChangedListener(OnSubscriptionsChangedListener listener) {
if (listener == null) return;
- addOnSubscriptionsChangedListener(
- new HandlerExecutor(new Handler(listener.getCreatorLooper())), listener);
+ Looper looper = listener.getCreatorLooper();
+ if (looper == null) {
+ throw new RuntimeException(
+ "Can't create handler inside thread " + Thread.currentThread()
+ + " that has not called Looper.prepare()");
+ }
+
+ addOnSubscriptionsChangedListener(new HandlerExecutor(new Handler(looper)), listener);
}
/**
diff --git a/tools/aapt2/integration-tests/SymlinkTest/Android.bp b/tools/aapt2/integration-tests/SymlinkTest/Android.bp
index 15a6a20..6fcdf1c 100644
--- a/tools/aapt2/integration-tests/SymlinkTest/Android.bp
+++ b/tools/aapt2/integration-tests/SymlinkTest/Android.bp
@@ -27,4 +27,7 @@
name: "AaptSymlinkTest",
sdk_version: "current",
use_resource_processor: false,
+ compile_data: [
+ "targets/*",
+ ],
}
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/LongArrayMultiStateCounter_host.java b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/LongArrayMultiStateCounter_host.java
index 068dfe8..a135623 100644
--- a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/LongArrayMultiStateCounter_host.java
+++ b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/LongArrayMultiStateCounter_host.java
@@ -100,6 +100,10 @@
mLastStateChangeTimestampMs = timestampMs;
}
+ public void setValue(int state, long[] values) {
+ System.arraycopy(values, 0, mStates[state].mCounter, 0, mArrayLength);
+ }
+
public void updateValue(long[] values, long timestampMs) {
if (mEnabled || mLastUpdateTimestampMs < mLastStateChangeTimestampMs) {
if (timestampMs < mLastStateChangeTimestampMs) {
@@ -306,6 +310,11 @@
return getInstance(instanceId).mArrayLength;
}
+ public static void native_setValues(long instanceId, int state, long containerInstanceId) {
+ getInstance(instanceId).setValue(state,
+ LongArrayContainer_host.getInstance(containerInstanceId));
+ }
+
public static void native_updateValues(long instanceId, long containerInstanceId,
long timestampMs) {
getInstance(instanceId).updateValue(
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java
index 3bcabcb..d63bff6 100644
--- a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java
+++ b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java
@@ -343,6 +343,28 @@
p.mPos += length;
p.updateSize();
}
+ public static int nativeCompareData(long thisNativePtr, long otherNativePtr) {
+ var a = getInstance(thisNativePtr);
+ var b = getInstance(otherNativePtr);
+ if ((a.mSize == b.mSize) && Arrays.equals(a.mBuffer, b.mBuffer)) {
+ return 0;
+ } else {
+ return -1;
+ }
+ }
+ public static boolean nativeCompareDataInRange(
+ long ptrA, int offsetA, long ptrB, int offsetB, int length) {
+ var a = getInstance(ptrA);
+ var b = getInstance(ptrB);
+ if (offsetA < 0 || offsetA + length > a.mSize) {
+ throw new IllegalArgumentException();
+ }
+ if (offsetB < 0 || offsetB + length > b.mSize) {
+ throw new IllegalArgumentException();
+ }
+ return Arrays.equals(Arrays.copyOfRange(a.mBuffer, offsetA, offsetA + length),
+ Arrays.copyOfRange(b.mBuffer, offsetB, offsetB + length));
+ }
public static void nativeAppendFrom(
long thisNativePtr, long otherNativePtr, int srcOffset, int length) {
var dst = getInstance(thisNativePtr);