diff options
381 files changed, 6693 insertions, 1313 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index 44a6c6b2803b..5456c15040ce 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -43901,6 +43901,7 @@ package android.telephony { field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT = "satellite_connection_hysteresis_sec_int"; field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_SATELLITE_ENTITLEMENT_STATUS_REFRESH_DAYS_INT = "satellite_entitlement_status_refresh_days_int"; field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL = "satellite_entitlement_supported_bool"; + field @FlaggedApi("com.android.internal.telephony.flags.carrier_roaming_nb_iot_ntn") public static final String KEY_SATELLITE_ESOS_SUPPORTED_BOOL = "satellite_esos_supported_bool"; field public static final String KEY_SHOW_4G_FOR_3G_DATA_ICON_BOOL = "show_4g_for_3g_data_icon_bool"; field public static final String KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL = "show_4g_for_lte_data_icon_bool"; field public static final String KEY_SHOW_APN_SETTING_CDMA_BOOL = "show_apn_setting_cdma_bool"; diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 4536f6f740ad..cd2c9ca55959 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -3524,6 +3524,7 @@ package android.companion.virtual { field @Deprecated public static final int NAVIGATION_POLICY_DEFAULT_BLOCKED = 1; // 0x1 field @FlaggedApi("android.companion.virtual.flags.dynamic_policy") public static final int POLICY_TYPE_ACTIVITY = 3; // 0x3 field public static final int POLICY_TYPE_AUDIO = 1; // 0x1 + field @FlaggedApi("android.companion.virtualdevice.flags.activity_control_api") public static final int POLICY_TYPE_BLOCKED_ACTIVITY_BEHAVIOR = 6; // 0x6 field @FlaggedApi("android.companion.virtual.flags.virtual_camera") public static final int POLICY_TYPE_CAMERA = 5; // 0x5 field @FlaggedApi("android.companion.virtual.flags.cross_device_clipboard") public static final int POLICY_TYPE_CLIPBOARD = 4; // 0x4 field public static final int POLICY_TYPE_RECENTS = 2; // 0x2 diff --git a/core/java/Android.bp b/core/java/Android.bp index fae411d495ca..128fb62e21c9 100644 --- a/core/java/Android.bp +++ b/core/java/Android.bp @@ -20,10 +20,43 @@ filegroup { "**/*.java", "**/*.aidl", ":framework-nfc-non-updatable-sources", + ":messagequeue-gen", + ], + // Exactly one of the below will be added to srcs by messagequeue-gen + exclude_srcs: [ + "android/os/LegacyMessageQueue/MessageQueue.java", + "android/os/ConcurrentMessageQueue/MessageQueue.java", + "android/os/SemiConcurrentMessageQueue/MessageQueue.java", ], visibility: ["//frameworks/base"], } +// Add selected MessageQueue.java implementation to srcs +soong_config_module_type { + name: "release_package_messagequeue_implementation_srcs", + module_type: "genrule", + config_namespace: "messagequeue", + value_variables: ["release_package_messagequeue_implementation"], + properties: [ + "srcs", + ], +} + +// Output the selected android/os/MessageQueue.java implementation +release_package_messagequeue_implementation_srcs { + name: "messagequeue-gen", + soong_config_variables: { + release_package_messagequeue_implementation: { + srcs: ["android/os/%s"], + conditions_default: { + srcs: ["android/os/LegacyMessageQueue/MessageQueue.java"], + }, + }, + }, + cmd: "mkdir -p android/os/; cp $(in) $(out);", + out: ["android/os/MessageQueue.java"], +} + aidl_library { name: "IDropBoxManagerService_aidl", srcs: [ diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 2313fa27afe1..5214d2c9c02a 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -2981,9 +2981,7 @@ public class AppOpsManager { new AppOpInfo.Builder(OP_ESTABLISH_VPN_MANAGER, OPSTR_ESTABLISH_VPN_MANAGER, "ESTABLISH_VPN_MANAGER").setDefaultMode(AppOpsManager.MODE_ALLOWED).build(), new AppOpInfo.Builder(OP_ACCESS_RESTRICTED_SETTINGS, OPSTR_ACCESS_RESTRICTED_SETTINGS, - "ACCESS_RESTRICTED_SETTINGS").setDefaultMode( - android.permission.flags.Flags.enhancedConfirmationModeApisEnabled() - ? MODE_DEFAULT : MODE_ALLOWED) + "ACCESS_RESTRICTED_SETTINGS").setDefaultMode(AppOpsManager.MODE_ALLOWED) .setDisableReset(true).setRestrictRead(true).build(), new AppOpInfo.Builder(OP_RECEIVE_AMBIENT_TRIGGER_AUDIO, OPSTR_RECEIVE_AMBIENT_TRIGGER_AUDIO, "RECEIVE_SOUNDTRIGGER_AUDIO").setDefaultMode(AppOpsManager.MODE_ALLOWED) diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index bd80dc1e3c60..69b5222aa314 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -499,32 +499,31 @@ public class NotificationManager { /** * Activity Action: Launch an Automatic Zen Rule configuration screen - * <p> - * Input: Optionally, {@link #EXTRA_AUTOMATIC_RULE_ID}, if the configuration screen for an + * + * <p> Input: Optionally, {@link #EXTRA_AUTOMATIC_RULE_ID}, if the configuration screen for an * existing rule should be displayed. If the rule id is missing or null, apps should display * a configuration screen where users can create a new instance of the rule. - * <p> - * Output: Nothing - * <p> - * You can have multiple activities handling this intent, if you support multiple - * {@link AutomaticZenRule rules}. In order for the system to properly display all of your - * rule types so that users can create new instances or configure existing ones, you need - * to add some extra metadata ({@link #META_DATA_AUTOMATIC_RULE_TYPE}) - * to your activity tag in your manifest. If you'd like to limit the number of rules a user - * can create from this flow, you can additionally optionally include - * {@link #META_DATA_RULE_INSTANCE_LIMIT}. - * - * For example, - * <meta-data - * android:name="android.app.zen.automatic.ruleType" - * android:value="@string/my_condition_rule"> - * </meta-data> - * <meta-data - * android:name="android.app.zen.automatic.ruleInstanceLimit" - * android:value="1"> - * </meta-data> - * </p> - * </p> + * + * <p> Output: Nothing + * + * <p> You can have multiple activities handling this intent, if you support multiple + * {@link AutomaticZenRule rules}. In order for the system to properly display all of your + * rule types so that users can create new instances or configure existing ones, you need + * to add some extra metadata ({@link #META_DATA_AUTOMATIC_RULE_TYPE}) + * to your activity tag in your manifest. If you'd like to limit the number of rules a user + * can create from this flow, you can additionally optionally include + * {@link #META_DATA_RULE_INSTANCE_LIMIT}. For example, + * + * <pre> + * {@code + * <meta-data + * android:name="android.service.zen.automatic.ruleType" + * android:value="@string/my_condition_rule" /> + * <meta-data + * android:name="android.service.zen.automatic.ruleInstanceLimit" + * android:value="1" /> + * } + * </pre> * * @see #addAutomaticZenRule(AutomaticZenRule) */ diff --git a/core/java/android/companion/virtual/VirtualDeviceParams.java b/core/java/android/companion/virtual/VirtualDeviceParams.java index f9a9da1ae448..f7f842f58511 100644 --- a/core/java/android/companion/virtual/VirtualDeviceParams.java +++ b/core/java/android/companion/virtual/VirtualDeviceParams.java @@ -171,7 +171,7 @@ public final class VirtualDeviceParams implements Parcelable { * @hide */ @IntDef(prefix = "POLICY_TYPE_", value = {POLICY_TYPE_RECENTS, POLICY_TYPE_ACTIVITY, - POLICY_TYPE_CLIPBOARD}) + POLICY_TYPE_CLIPBOARD, POLICY_TYPE_BLOCKED_ACTIVITY_BEHAVIOR}) @Retention(RetentionPolicy.SOURCE) @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE}) public @interface DynamicPolicyType {} @@ -263,6 +263,22 @@ public final class VirtualDeviceParams implements Parcelable { @FlaggedApi(Flags.FLAG_VIRTUAL_CAMERA) public static final int POLICY_TYPE_CAMERA = 5; + /** + * Tells the virtual device framework how to handle activity launches that were blocked due to + * the current activity policy. + * + * <ul> + * <li>{@link #DEVICE_POLICY_DEFAULT}: Show UI informing the user of the blocked activity + * launch on the virtual display that the activity was originally launched on. + * <li>{@link #DEVICE_POLICY_CUSTOM}: Does not inform the user of the blocked activity + * launch. The virtual device owner can use this policy together with + * {@link VirtualDeviceManager.ActivityListener#onActivityLaunchBlocked} to provide custom + * experience on the virtual device. + * </ul> + */ + @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_ACTIVITY_CONTROL_API) + public static final int POLICY_TYPE_BLOCKED_ACTIVITY_BEHAVIOR = 6; + private final int mLockState; @NonNull private final ArraySet<UserHandle> mUsersWithMatchingAccounts; @NavigationPolicy @@ -1174,6 +1190,10 @@ public final class VirtualDeviceParams implements Parcelable { mDevicePolicies.delete(POLICY_TYPE_CAMERA); } + if (!android.companion.virtualdevice.flags.Flags.activityControlApi()) { + mDevicePolicies.delete(POLICY_TYPE_BLOCKED_ACTIVITY_BEHAVIOR); + } + if ((mAudioPlaybackSessionId != AUDIO_SESSION_ID_GENERATE || mAudioRecordingSessionId != AUDIO_SESSION_ID_GENERATE) && mDevicePolicies.get(POLICY_TYPE_AUDIO, DEVICE_POLICY_DEFAULT) diff --git a/core/java/android/hardware/devicestate/feature/flags.aconfig b/core/java/android/hardware/devicestate/feature/flags.aconfig index 12d3f94ec982..a09c84dcedcb 100644 --- a/core/java/android/hardware/devicestate/feature/flags.aconfig +++ b/core/java/android/hardware/devicestate/feature/flags.aconfig @@ -8,4 +8,13 @@ flag { description: "Updated DeviceState hasProperty API" bug: "293636629" is_fixed_read_only: true +} + +flag { + name: "device_state_property_migration" + is_exported: true + namespace: "windowing_sdk" + description: "Client migration to updated DeviceStateManager API's" + bug: "336640888" + is_fixed_read_only: true }
\ No newline at end of file diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index 065b3d6c7a8a..b2f333ae58e8 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -9025,6 +9025,10 @@ public abstract class BatteryStats { final int uid = consumer.getUid(); final Uid u = uidStats.get(uid); + if (u == null) { + continue; + } + final long rxPackets = u.getNetworkActivityPackets( BatteryStats.NETWORK_MOBILE_RX_DATA, STATS_SINCE_CHARGED); final long txPackets = u.getNetworkActivityPackets( diff --git a/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java b/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java new file mode 100644 index 000000000000..72b5cf79133d --- /dev/null +++ b/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java @@ -0,0 +1,1648 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.TestApi; +import android.os.Handler; +import android.os.Trace; +import android.util.Log; +import android.util.Printer; +import android.util.SparseArray; +import android.util.proto.ProtoOutputStream; + +import com.android.internal.annotations.GuardedBy; + +import java.io.FileDescriptor; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.concurrent.ConcurrentSkipListSet; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.ReentrantLock; + +/** + * Low-level class holding the list of messages to be dispatched by a + * {@link Looper}. Messages are not added directly to a MessageQueue, + * but rather through {@link Handler} objects associated with the Looper. + * + * <p>You can retrieve the MessageQueue for the current thread with + * {@link Looper#myQueue() Looper.myQueue()}. + */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass +@android.ravenwood.annotation.RavenwoodNativeSubstitutionClass( + "com.android.platform.test.ravenwood.nativesubstitution.MessageQueue_host") +public final class MessageQueue { + private static final String TAG = "ConcurrentMessageQueue"; + private static final boolean DEBUG = false; + private static final boolean TRACE = false; + + // True if the message queue can be quit. + private final boolean mQuitAllowed; + + @SuppressWarnings("unused") + private long mPtr; // used by native code + + @IntDef(value = { + STACK_NODE_MESSAGE, + STACK_NODE_ACTIVE, + STACK_NODE_PARKED, + STACK_NODE_TIMEDPARK}) + @Retention(RetentionPolicy.SOURCE) + private @interface StackNodeType {} + + /* + * Stack node types. STACK_NODE_MESSAGE indicates a node containing a message. + * The other types indicate what state our Looper thread is in. The bottom of + * the stack is always a single state node. Message nodes are added on top. + */ + private static final int STACK_NODE_MESSAGE = 0; + /* + * Active state indicates that next() is processing messages + */ + private static final int STACK_NODE_ACTIVE = 1; + /* + * Parked state indicates that the Looper thread is sleeping indefinitely (nothing to deliver) + */ + private static final int STACK_NODE_PARKED = 2; + /* + * Timed Park state indicates that the Looper thread is sleeping, waiting for a message + * deadline + */ + private static final int STACK_NODE_TIMEDPARK = 3; + + /* Describes a node in the Treiber stack */ + static class StackNode { + @StackNodeType + private final int mType; + + StackNode(@StackNodeType int type) { + mType = type; + } + + @StackNodeType + final int getNodeType() { + return mType; + } + + final boolean isMessageNode() { + return mType == STACK_NODE_MESSAGE; + } + } + + static final class MessageNode extends StackNode implements Comparable<MessageNode> { + private final Message mMessage; + volatile StackNode mNext; + StateNode mBottomOfStack; + boolean mWokeUp; + final long mInsertSeq; + private static final VarHandle sRemovedFromStack; + private volatile boolean mRemovedFromStackValue; + static { + try { + MethodHandles.Lookup l = MethodHandles.lookup(); + sRemovedFromStack = l.findVarHandle(MessageQueue.MessageNode.class, + "mRemovedFromStackValue", boolean.class); + } catch (Exception e) { + Log.wtf(TAG, "VarHandle lookup failed with exception: " + e); + throw new ExceptionInInitializerError(e); + } + } + + MessageNode(@NonNull Message message, long insertSeq) { + super(STACK_NODE_MESSAGE); + mMessage = message; + mInsertSeq = insertSeq; + } + + long getWhen() { + return mMessage.when; + } + + boolean isRemovedFromStack() { + return mRemovedFromStackValue; + } + + boolean removeFromStack() { + return sRemovedFromStack.compareAndSet(this, false, true); + } + + boolean isAsync() { + return mMessage.isAsynchronous(); + } + + boolean isBarrier() { + return mMessage.target == null; + } + + @Override + public int compareTo(@NonNull MessageNode messageNode) { + Message other = messageNode.mMessage; + + int compared = Long.compare(mMessage.when, other.when); + if (compared == 0) { + compared = Long.compare(mInsertSeq, messageNode.mInsertSeq); + } + return compared; + } + } + + static class StateNode extends StackNode { + StateNode(int type) { + super(type); + } + } + + static final class TimedParkStateNode extends StateNode { + long mWhenToWake; + + TimedParkStateNode() { + super(STACK_NODE_TIMEDPARK); + } + } + + private static final StateNode sStackStateActive = new StateNode(STACK_NODE_ACTIVE); + private static final StateNode sStackStateParked = new StateNode(STACK_NODE_PARKED); + private final TimedParkStateNode mStackStateTimedPark = new TimedParkStateNode(); + + /* This is the top of our treiber stack. */ + private static final VarHandle sState; + static { + try { + MethodHandles.Lookup l = MethodHandles.lookup(); + sState = l.findVarHandle(MessageQueue.class, "mStateValue", + MessageQueue.StackNode.class); + } catch (Exception e) { + Log.wtf(TAG, "VarHandle lookup failed with exception: " + e); + throw new ExceptionInInitializerError(e); + } + } + + private volatile StackNode mStateValue = sStackStateParked; + private final ConcurrentSkipListSet<MessageNode> mPriorityQueue = + new ConcurrentSkipListSet<MessageNode>(); + private final ConcurrentSkipListSet<MessageNode> mAsyncPriorityQueue = + new ConcurrentSkipListSet<MessageNode>(); + + /* + * This helps us ensure that messages with the same timestamp are inserted in FIFO order. + * Increments on each insert, starting at 0. MessageNode.compareTo() will compare sequences + * when delivery timestamps are identical. + */ + private static final VarHandle sNextInsertSeq; + private volatile long mNextInsertSeqValue = 0; + /* + * The exception to the FIFO order rule is sendMessageAtFrontOfQueue(). + * Those messages must be in LIFO order - SIGH. + * Decrements on each front of queue insert. + */ + private static final VarHandle sNextFrontInsertSeq; + private volatile long mNextFrontInsertSeqValue = -1; + static { + try { + MethodHandles.Lookup l = MethodHandles.lookup(); + sNextInsertSeq = l.findVarHandle(MessageQueue.class, "mNextInsertSeqValue", + long.class); + sNextFrontInsertSeq = l.findVarHandle(MessageQueue.class, "mNextFrontInsertSeqValue", + long.class); + } catch (Exception e) { + Log.wtf(TAG, "VarHandle lookup failed with exception: " + e); + throw new ExceptionInInitializerError(e); + } + + } + + /* + * Tracks the number of queued and cancelled messages in our stack. + * + * On item cancellation, determine whether to wake next() to flush tombstoned messages. + * We track queued and cancelled counts as two ints packed into a single long. + */ + private static final class MessageCounts { + private static VarHandle sCounts; + private volatile long mCountsValue = 0; + static { + try { + MethodHandles.Lookup l = MethodHandles.lookup(); + sCounts = l.findVarHandle(MessageQueue.MessageCounts.class, "mCountsValue", + long.class); + } catch (Exception e) { + Log.wtf(TAG, "VarHandle lookup failed with exception: " + e); + throw new ExceptionInInitializerError(e); + } + } + + /* We use a special value to indicate when next() has been woken for flush. */ + private static final long AWAKE = Long.MAX_VALUE; + /* + * Minimum number of messages in the stack which we need before we consider flushing + * tombstoned items. + */ + private static final int MESSAGE_FLUSH_THRESHOLD = 10; + + private static int numQueued(long val) { + return (int) (val >>> Integer.SIZE); + } + + private static int numCancelled(long val) { + return (int) val; + } + + private static long combineCounts(int queued, int cancelled) { + return ((long) queued << Integer.SIZE) | (long) cancelled; + } + + public void incrementQueued() { + while (true) { + long oldVal = mCountsValue; + int queued = numQueued(oldVal); + int cancelled = numCancelled(oldVal); + /* Use Math.max() to avoid overflow of queued count */ + long newVal = combineCounts(Math.max(queued + 1, queued), cancelled); + + /* Don't overwrite 'AWAKE' state */ + if (oldVal == AWAKE || sCounts.compareAndSet(this, oldVal, newVal)) { + break; + } + } + } + + public boolean incrementCancelled() { + while (true) { + long oldVal = mCountsValue; + if (oldVal == AWAKE) { + return false; + } + int queued = numQueued(oldVal); + int cancelled = numCancelled(oldVal); + boolean needsPurge = queued > MESSAGE_FLUSH_THRESHOLD + && (queued >> 1) < cancelled; + long newVal; + if (needsPurge) { + newVal = AWAKE; + } else { + newVal = combineCounts(queued, + Math.max(cancelled + 1, cancelled)); + } + + if (sCounts.compareAndSet(this, oldVal, newVal)) { + return needsPurge; + } + } + } + + public void clearCounts() { + mCountsValue = 0; + } + } + + private final MessageCounts mMessageCounts = new MessageCounts(); + + private final Object mIdleHandlersLock = new Object(); + @GuardedBy("mIdleHandlersLock") + private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>(); + private IdleHandler[] mPendingIdleHandlers; + + private final Object mFileDescriptorRecordsLock = new Object(); + @GuardedBy("mFileDescriptorRecordsLock") + private SparseArray<FileDescriptorRecord> mFileDescriptorRecords; + + private static final VarHandle sQuitting; + private boolean mQuittingValue = false; + static { + try { + MethodHandles.Lookup l = MethodHandles.lookup(); + sQuitting = l.findVarHandle(MessageQueue.class, "mQuittingValue", boolean.class); + } catch (Exception e) { + Log.wtf(TAG, "VarHandle lookup failed with exception: " + e); + throw new ExceptionInInitializerError(e); + } + } + + // The next barrier token. + // Barriers are indicated by messages with a null target whose arg1 field carries the token. + private final AtomicInteger mNextBarrierToken = new AtomicInteger(1); + + private static native long nativeInit(); + private static native void nativeDestroy(long ptr); + private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/ + private static native void nativeWake(long ptr); + private static native boolean nativeIsPolling(long ptr); + private static native void nativeSetFileDescriptorEvents(long ptr, int fd, int events); + + MessageQueue(boolean quitAllowed) { + mQuitAllowed = quitAllowed; + mPtr = nativeInit(); + } + + @Override + protected void finalize() throws Throwable { + try { + dispose(); + } finally { + super.finalize(); + } + } + + // Disposes of the underlying message queue. + // Must only be called on the looper thread or the finalizer. + private void dispose() { + if (mPtr != 0) { + nativeDestroy(mPtr); + mPtr = 0; + } + } + + /** + * Returns true if the looper has no pending messages which are due to be processed. + * + * <p>This method is safe to call from any thread. + * + * @return True if the looper is idle. + */ + public boolean isIdle() { + MessageNode msgNode = null; + MessageNode asyncMsgNode = null; + + if (!mPriorityQueue.isEmpty()) { + try { + msgNode = mPriorityQueue.first(); + } catch (NoSuchElementException e) { } + } + + if (!mAsyncPriorityQueue.isEmpty()) { + try { + asyncMsgNode = mAsyncPriorityQueue.first(); + } catch (NoSuchElementException e) { } + } + + final long now = SystemClock.uptimeMillis(); + if ((msgNode != null && msgNode.getWhen() <= now) + || (asyncMsgNode != null && asyncMsgNode.getWhen() <= now)) { + return false; + } + + return true; + } + + /* Protects mNextIsDrainingStack */ + private final ReentrantLock mDrainingLock = new ReentrantLock(); + private boolean mNextIsDrainingStack = false; + private final Condition mDrainCompleted = mDrainingLock.newCondition(); + + /** + * Add a new {@link IdleHandler} to this message queue. This may be + * removed automatically for you by returning false from + * {@link IdleHandler#queueIdle IdleHandler.queueIdle()} when it is + * invoked, or explicitly removing it with {@link #removeIdleHandler}. + * + * <p>This method is safe to call from any thread. + * + * @param handler The IdleHandler to be added. + */ + public void addIdleHandler(@NonNull IdleHandler handler) { + if (handler == null) { + throw new NullPointerException("Can't add a null IdleHandler"); + } + synchronized (mIdleHandlersLock) { + mIdleHandlers.add(handler); + } + } + + /** + * Remove an {@link IdleHandler} from the queue that was previously added + * with {@link #addIdleHandler}. If the given object is not currently + * in the idle list, nothing is done. + * + * <p>This method is safe to call from any thread. + * + * @param handler The IdleHandler to be removed. + */ + public void removeIdleHandler(@NonNull IdleHandler handler) { + synchronized (mIdleHandlersLock) { + mIdleHandlers.remove(handler); + } + } + + /** + * Returns whether this looper's thread is currently polling for more work to do. + * This is a good signal that the loop is still alive rather than being stuck + * handling a callback. Note that this method is intrinsically racy, since the + * state of the loop can change before you get the result back. + * + * <p>This method is safe to call from any thread. + * + * @return True if the looper is currently polling for events. + * @hide + */ + public boolean isPolling() { + // If the loop is quitting then it must not be idling. + // We can assume mPtr != 0 when sQuitting is false. + return !((boolean) sQuitting.getVolatile(this)) && nativeIsPolling(mPtr); + } + + /* Helper to choose the correct queue to insert into. */ + private void insertIntoPriorityQueue(MessageNode msgNode) { + if (msgNode.isAsync()) { + mAsyncPriorityQueue.add(msgNode); + } else { + mPriorityQueue.add(msgNode); + } + } + + private boolean removeFromPriorityQueue(MessageNode msgNode) { + if (msgNode.isAsync()) { + return mAsyncPriorityQueue.remove(msgNode); + } else { + return mPriorityQueue.remove(msgNode); + } + } + + private MessageNode pickEarliestNode(MessageNode nodeA, MessageNode nodeB) { + if (nodeA != null && nodeB != null) { + if (nodeA.compareTo(nodeB) < 0) { + return nodeA; + } + return nodeB; + } + + return nodeA != null ? nodeA : nodeB; + } + + private MessageNode iterateNext(Iterator<MessageNode> iter) { + if (iter.hasNext()) { + try { + return iter.next(); + } catch (NoSuchElementException e) { + /* The queue is empty - this can happen if we race with remove */ + } + } + return null; + } + + /* Move any non-cancelled messages into the priority queue */ + private void drainStack(StackNode oldTop) { + while (oldTop.isMessageNode()) { + MessageNode oldTopMessageNode = (MessageNode) oldTop; + if (oldTopMessageNode.removeFromStack()) { + insertIntoPriorityQueue(oldTopMessageNode); + } + MessageNode inserted = oldTopMessageNode; + oldTop = oldTopMessageNode.mNext; + /* + * removeMessages can walk this list while we are consuming it. + * Set our next pointer to null *after* we add the message to our + * priority queue. This way removeMessages() will always find the + * message, either in our list or in the priority queue. + */ + inserted.mNext = null; + } + } + + /* Set the stack state to Active, return a list of nodes to walk. */ + private StackNode swapAndSetStackStateActive() { + while (true) { + /* Set stack state to Active, get node list to walk later */ + StackNode current = (StackNode) sState.getVolatile(this); + if (current == sStackStateActive + || sState.compareAndSet(this, current, sStackStateActive)) { + return current; + } + } + } + + /* This is only read/written from the Looper thread */ + private int mNextPollTimeoutMillis; + private static final AtomicLong mMessagesDelivered = new AtomicLong(); + + private Message nextMessage() { + int i = 0; + + while (true) { + if (DEBUG) { + Log.d(TAG, "nextMessage loop #" + i); + i++; + } + + mDrainingLock.lock(); + mNextIsDrainingStack = true; + mDrainingLock.unlock(); + + /* + * Set our state to active, drain any items from the stack into our priority queues + */ + StackNode oldTop; + oldTop = swapAndSetStackStateActive(); + drainStack(oldTop); + + mDrainingLock.lock(); + mNextIsDrainingStack = false; + mDrainCompleted.signalAll(); + mDrainingLock.unlock(); + + /* + * The objective of this next block of code is to: + * - find a message to return (if any is ready) + * - find a next message we would like to return, after scheduling. + * - we make our scheduling decision based on this next message (if it exists). + * + * We have two queues to juggle and the presence of barriers throws an additional + * wrench into our plans. + * + * The last wrinkle is that remove() may delete items from underneath us. If we hit + * that case, we simply restart the loop. + */ + + /* Get the first node from each queue */ + Iterator<MessageNode> queueIter = mPriorityQueue.iterator(); + MessageNode msgNode = iterateNext(queueIter); + Iterator<MessageNode> asyncQueueIter = mAsyncPriorityQueue.iterator(); + MessageNode asyncMsgNode = iterateNext(asyncQueueIter); + + if (DEBUG) { + if (msgNode != null) { + Message msg = msgNode.mMessage; + Log.d(TAG, "Next found node what: " + msg.what + " when: " + msg.when + + " seq: " + msgNode.mInsertSeq + "barrier: " + + msgNode.isBarrier() + " now: " + SystemClock.uptimeMillis()); + } + if (asyncMsgNode != null) { + Message msg = asyncMsgNode.mMessage; + Log.d(TAG, "Next found async node what: " + msg.what + " when: " + msg.when + + " seq: " + asyncMsgNode.mInsertSeq + "barrier: " + + asyncMsgNode.isBarrier() + " now: " + + SystemClock.uptimeMillis()); + } + } + + /* + * the node which we will return, null if none are ready + */ + MessageNode found = null; + /* + * The node from which we will determine our next wakeup time. + * Null indicates there is no next message ready. If we found a node, + * we can leave this null as Looper will call us again after delivering + * the message. + */ + MessageNode next = null; + + long now = SystemClock.uptimeMillis(); + /* + * If we have a barrier we should return the async node (if it exists and is ready) + */ + if (msgNode != null && msgNode.isBarrier()) { + if (asyncMsgNode != null && now >= asyncMsgNode.getWhen()) { + found = asyncMsgNode; + } else { + next = asyncMsgNode; + } + } else { /* No barrier. */ + MessageNode earliest; + /* + * If we have two messages, pick the earliest option from either queue. + * Otherwise grab whichever node is non-null. If both are null we'll fall through. + */ + earliest = pickEarliestNode(msgNode, asyncMsgNode); + + if (earliest != null) { + if (now >= earliest.getWhen()) { + found = earliest; + } else { + next = earliest; + } + } + } + + if (DEBUG) { + if (found != null) { + Message msg = found.mMessage; + Log.d(TAG, "Will deliver node what: " + msg.what + " when: " + msg.when + + " seq: " + found.mInsertSeq + " barrier: " + found.isBarrier() + + " async: " + found.isAsync() + " now: " + + SystemClock.uptimeMillis()); + } else { + Log.d(TAG, "No node to deliver"); + } + if (next != null) { + Message msg = next.mMessage; + Log.d(TAG, "Next node what: " + msg.what + " when: " + msg.when + " seq: " + + next.mInsertSeq + " barrier: " + next.isBarrier() + " async: " + + next.isAsync() + + " now: " + SystemClock.uptimeMillis()); + } else { + Log.d(TAG, "No next node"); + } + } + + /* + * If we have a found message, we will get called again so there's no need to set state. + * In that case we can leave our state as ACTIVE. + * + * Otherwise we should determine how to park the thread. + */ + StateNode nextOp = sStackStateActive; + if (found == null) { + if (next == null) { + /* No message to deliver, sleep indefinitely */ + mNextPollTimeoutMillis = -1; + nextOp = sStackStateParked; + if (DEBUG) { + Log.d(TAG, "nextMessage next state is StackStateParked"); + } + } else { + /* Message not ready, or we found one to deliver already, set a timeout */ + long nextMessageWhen = next.getWhen(); + if (nextMessageWhen > now) { + mNextPollTimeoutMillis = (int) Math.min(nextMessageWhen - now, + Integer.MAX_VALUE); + } else { + mNextPollTimeoutMillis = 0; + } + + mStackStateTimedPark.mWhenToWake = now + mNextPollTimeoutMillis; + nextOp = mStackStateTimedPark; + if (DEBUG) { + Log.d(TAG, "nextMessage next state is StackStateTimedParked timeout ms " + + mNextPollTimeoutMillis + " mWhenToWake: " + + mStackStateTimedPark.mWhenToWake + " now " + now); + } + } + } + + /* + * Try to swap our state from Active back to Park or TimedPark. If we raced with + * enqueue, loop back around to pick up any new items. + */ + if (sState.compareAndSet(this, sStackStateActive, nextOp)) { + mMessageCounts.clearCounts(); + if (found != null) { + if (!removeFromPriorityQueue(found)) { + /* + * RemoveMessages() might be able to pull messages out from under us + * However we can detect that here and just loop around if it happens. + */ + continue; + } + + if (TRACE) { + Trace.setCounter("MQ.Delivered", mMessagesDelivered.incrementAndGet()); + } + return found.mMessage; + } + return null; + } + } + } + + Message next() { + final long ptr = mPtr; + if (ptr == 0) { + return null; + } + + mNextPollTimeoutMillis = 0; + int pendingIdleHandlerCount = -1; // -1 only during first iteration + while (true) { + if (mNextPollTimeoutMillis != 0) { + Binder.flushPendingCommands(); + } + + nativePollOnce(ptr, mNextPollTimeoutMillis); + + Message msg = nextMessage(); + if (msg != null) { + msg.markInUse(); + return msg; + } + + if ((boolean) sQuitting.getVolatile(this)) { + return null; + } + + synchronized (mIdleHandlersLock) { + // If first time idle, then get the number of idlers to run. + // Idle handles only run if the queue is empty or if the first message + // in the queue (possibly a barrier) is due to be handled in the future. + if (pendingIdleHandlerCount < 0 + && mNextPollTimeoutMillis != 0) { + pendingIdleHandlerCount = mIdleHandlers.size(); + } + if (pendingIdleHandlerCount <= 0) { + // No idle handlers to run. Loop and wait some more. + continue; + } + + if (mPendingIdleHandlers == null) { + mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)]; + } + mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers); + } + + // Run the idle handlers. + // We only ever reach this code block during the first iteration. + for (int i = 0; i < pendingIdleHandlerCount; i++) { + final IdleHandler idler = mPendingIdleHandlers[i]; + mPendingIdleHandlers[i] = null; // release the reference to the handler + + boolean keep = false; + try { + keep = idler.queueIdle(); + } catch (Throwable t) { + Log.wtf(TAG, "IdleHandler threw exception", t); + } + + if (!keep) { + synchronized (mIdleHandlersLock) { + mIdleHandlers.remove(idler); + } + } + } + + // Reset the idle handler count to 0 so we do not run them again. + pendingIdleHandlerCount = 0; + + // While calling an idle handler, a new message could have been delivered + // so go back and look again for a pending message without waiting. + mNextPollTimeoutMillis = 0; + } + } + + void quit(boolean safe) { + if (!mQuitAllowed) { + throw new IllegalStateException("Main thread not allowed to quit."); + } + synchronized (mIdleHandlersLock) { + if (sQuitting.compareAndSet(this, false, true)) { + if (safe) { + removeAllFutureMessages(); + } else { + removeAllMessages(); + } + + // We can assume mPtr != 0 because sQuitting was previously false. + nativeWake(mPtr); + } + } + } + + boolean enqueueMessage(@NonNull Message msg, long when) { + if (msg.target == null) { + throw new IllegalArgumentException("Message must have a target."); + } + + if (msg.isInUse()) { + throw new IllegalStateException(msg + " This message is already in use."); + } + + return enqueueMessageUnchecked(msg, when); + } + + private boolean enqueueMessageUnchecked(@NonNull Message msg, long when) { + if ((boolean) sQuitting.getVolatile(this)) { + IllegalStateException e = new IllegalStateException( + msg.target + " sending message to a Handler on a dead thread"); + Log.w(TAG, e.getMessage(), e); + msg.recycleUnchecked(); + return false; + } + + long seq = when != 0 ? ((long)sNextInsertSeq.getAndAdd(this, 1L) + 1L) + : ((long)sNextFrontInsertSeq.getAndAdd(this, -1L) - 1L); + /* TODO: Add a MessageNode member to Message so we can avoid this allocation */ + MessageNode node = new MessageNode(msg, seq); + msg.when = when; + msg.markInUse(); + + if (DEBUG) { + Log.d(TAG, "Insert message what: " + msg.what + " when: " + msg.when + " seq: " + + node.mInsertSeq + " barrier: " + node.isBarrier() + " async: " + + node.isAsync() + " now: " + SystemClock.uptimeMillis()); + } + + while (true) { + StackNode old = (StackNode) sState.getVolatile(this); + boolean wakeNeeded; + boolean inactive; + + node.mNext = old; + switch (old.getNodeType()) { + case STACK_NODE_ACTIVE: + /* + * The worker thread is currently active and will process any elements added to + * the stack before parking again. + */ + node.mBottomOfStack = (StateNode) old; + inactive = false; + node.mWokeUp = true; + wakeNeeded = false; + break; + + case STACK_NODE_PARKED: + node.mBottomOfStack = (StateNode) old; + inactive = true; + node.mWokeUp = true; + wakeNeeded = true; + break; + + case STACK_NODE_TIMEDPARK: + node.mBottomOfStack = (StateNode) old; + inactive = true; + wakeNeeded = mStackStateTimedPark.mWhenToWake >= node.getWhen(); + node.mWokeUp = wakeNeeded; + break; + + default: + MessageNode oldMessage = (MessageNode) old; + + node.mBottomOfStack = oldMessage.mBottomOfStack; + int bottomType = node.mBottomOfStack.getNodeType(); + inactive = bottomType >= STACK_NODE_PARKED; + wakeNeeded = (bottomType == STACK_NODE_TIMEDPARK + && mStackStateTimedPark.mWhenToWake >= node.getWhen() + && !oldMessage.mWokeUp); + node.mWokeUp = oldMessage.mWokeUp || wakeNeeded; + break; + } + if (sState.compareAndSet(this, old, node)) { + if (inactive) { + if (wakeNeeded) { + nativeWake(mPtr); + } else { + mMessageCounts.incrementQueued(); + } + } + return true; + } + } + } + + /** + * Posts a synchronization barrier to the Looper's message queue. + * + * Message processing occurs as usual until the message queue encounters the + * synchronization barrier that has been posted. When the barrier is encountered, + * later synchronous messages in the queue are stalled (prevented from being executed) + * until the barrier is released by calling {@link #removeSyncBarrier} and specifying + * the token that identifies the synchronization barrier. + * + * This method is used to immediately postpone execution of all subsequently posted + * synchronous messages until a condition is met that releases the barrier. + * Asynchronous messages (see {@link Message#isAsynchronous} are exempt from the barrier + * and continue to be processed as usual. + * + * This call must be always matched by a call to {@link #removeSyncBarrier} with + * the same token to ensure that the message queue resumes normal operation. + * Otherwise the application will probably hang! + * + * @return A token that uniquely identifies the barrier. This token must be + * passed to {@link #removeSyncBarrier} to release the barrier. + * + * @hide + */ + @TestApi + public int postSyncBarrier() { + return postSyncBarrier(SystemClock.uptimeMillis()); + } + + private int postSyncBarrier(long when) { + final int token = mNextBarrierToken.getAndIncrement(); + final Message msg = Message.obtain(); + + msg.markInUse(); + msg.arg1 = token; + + if (!enqueueMessageUnchecked(msg, when)) { + Log.wtf(TAG, "Unexpected error while adding sync barrier!"); + return -1; + } + + return token; + } + + private class MatchBarrierToken extends MessageCompare { + int mBarrierToken; + + MatchBarrierToken(int token) { + super(); + mBarrierToken = token; + } + + @Override + public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r, + long when) { + if (m.target == null && m.arg1 == mBarrierToken) { + return true; + } + return false; + } + } + + /** + * Removes a synchronization barrier. + * + * @param token The synchronization barrier token that was returned by + * {@link #postSyncBarrier}. + * + * @throws IllegalStateException if the barrier was not found. + * + * @hide + */ + @TestApi + public void removeSyncBarrier(int token) { + boolean removed; + MessageNode first; + final MatchBarrierToken matchBarrierToken = new MatchBarrierToken(token); + + try { + /* Retain the first element to see if we are currently stuck on a barrier. */ + first = mPriorityQueue.first(); + } catch (NoSuchElementException e) { + /* The queue is empty */ + first = null; + } + + removed = findOrRemoveMessages(null, 0, null, null, 0, matchBarrierToken, true); + if (removed && first != null) { + Message m = first.mMessage; + if (m.target == null && m.arg1 == token) { + /* Wake up next() in case it was sleeping on this barrier. */ + nativeWake(mPtr); + } + } else if (!removed) { + throw new IllegalStateException("The specified message queue synchronization " + + " barrier token has not been posted or has already been removed."); + } + } + + private StateNode getStateNode(StackNode node) { + if (node.isMessageNode()) { + return ((MessageNode) node).mBottomOfStack; + } + return (StateNode) node; + } + + private void waitForDrainCompleted() { + mDrainingLock.lock(); + while (mNextIsDrainingStack) { + mDrainCompleted.awaitUninterruptibly(); + } + mDrainingLock.unlock(); + } + + /* + * This class is used to find matches for hasMessages() and removeMessages() + */ + private abstract static class MessageCompare { + public abstract boolean compareMessage(Message m, Handler h, int what, Object object, + Runnable r, long when); + } + + private boolean stackHasMessages(Handler h, int what, Object object, Runnable r, long when, + MessageCompare compare, boolean removeMatches) { + boolean found = false; + StackNode top = (StackNode) sState.getVolatile(this); + StateNode bottom = getStateNode(top); + + /* + * If the top node is a state node, there are no reachable messages. + * If it's anything other than Active, we can quit as we know that next() is not + * consuming items. + * If the top node is Active then we know that next() is currently consuming items. + * In that case we should wait next() has drained the stack. + */ + if (top == bottom) { + if (bottom != sStackStateActive) { + return false; + } + waitForDrainCompleted(); + return false; + } + + /* + * We have messages that we may tombstone. Walk the stack until we hit the bottom or we + * hit a null pointer. + * If we hit the bottom, we are done. + * If we hit a null pointer, then the stack is being consumed by next() and we must cycle + * until the stack has been drained. + */ + MessageNode p = (MessageNode) top; + + while (true) { + if (compare.compareMessage(p.mMessage, h, what, object, r, when)) { + found = true; + if (DEBUG) { + Log.w(TAG, "stackHasMessages node matches"); + } + if (removeMatches) { + if (p.removeFromStack()) { + p.mMessage.recycleUnchecked(); + if (mMessageCounts.incrementCancelled()) { + nativeWake(mPtr); + } + } + } else { + return true; + } + } + + StackNode n = p.mNext; + if (n == null) { + /* Next() is walking the stack, we must re-sample */ + if (DEBUG) { + Log.d(TAG, "stackHasMessages next() is walking the stack, we must re-sample"); + } + waitForDrainCompleted(); + break; + } + if (!n.isMessageNode()) { + /* We reached the end of the stack */ + return found; + } + p = (MessageNode) n; + } + + return found; + } + + private boolean priorityQueueHasMessage(ConcurrentSkipListSet<MessageNode> queue, Handler h, + int what, Object object, Runnable r, long when, MessageCompare compare, + boolean removeMatches) { + Iterator<MessageNode> iterator = queue.iterator(); + boolean found = false; + + while (iterator.hasNext()) { + MessageNode msg = iterator.next(); + + if (compare.compareMessage(msg.mMessage, h, what, object, r, when)) { + if (removeMatches) { + found = true; + if (queue.remove(msg)) { + msg.mMessage.recycleUnchecked(); + } + } else { + return true; + } + } + } + return found; + } + + private boolean findOrRemoveMessages(Handler h, int what, Object object, Runnable r, long when, + MessageCompare compare, boolean removeMatches) { + boolean foundInStack, foundInQueue; + + foundInStack = stackHasMessages(h, what, object, r, when, compare, removeMatches); + foundInQueue = priorityQueueHasMessage(mPriorityQueue, h, what, object, r, when, compare, + removeMatches); + foundInQueue |= priorityQueueHasMessage(mAsyncPriorityQueue, h, what, object, r, when, + compare, removeMatches); + + return foundInStack || foundInQueue; + } + + private static class MatchHandlerWhatAndObject extends MessageCompare { + @Override + public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r, + long when) { + if (m.target == h && m.what == what && (object == null || m.obj == object)) { + return true; + } + return false; + } + } + private final MatchHandlerWhatAndObject mMatchHandlerWhatAndObject = + new MatchHandlerWhatAndObject(); + boolean hasMessages(Handler h, int what, Object object) { + if (h == null) { + return false; + } + + return findOrRemoveMessages(h, what, object, null, 0, mMatchHandlerWhatAndObject, false); + } + + private static class MatchHandlerWhatAndObjectEquals extends MessageCompare { + @Override + public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r, + long when) { + if (m.target == h && m.what == what && (object == null || object.equals(m.obj))) { + return true; + } + return false; + } + } + private final MatchHandlerWhatAndObjectEquals mMatchHandlerWhatAndObjectEquals = + new MatchHandlerWhatAndObjectEquals(); + boolean hasEqualMessages(Handler h, int what, Object object) { + if (h == null) { + return false; + } + + return findOrRemoveMessages(h, what, object, null, 0, mMatchHandlerWhatAndObjectEquals, + false); + } + + private static class MatchHandlerRunnableAndObject extends MessageCompare { + @Override + public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r, + long when) { + if (m.target == h && m.callback == r && (object == null || m.obj == object)) { + return true; + } + return false; + } + } + private final MatchHandlerRunnableAndObject mMatchHandlerRunnableAndObject = + new MatchHandlerRunnableAndObject(); + + boolean hasMessages(Handler h, Runnable r, Object object) { + if (h == null) { + return false; + } + + return findOrRemoveMessages(h, -1, object, r, 0, mMatchHandlerRunnableAndObject, false); + } + + private static class MatchHandler extends MessageCompare { + @Override + public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r, + long when) { + if (m.target == h) { + return true; + } + return false; + } + } + private final MatchHandler mMatchHandler = new MatchHandler(); + boolean hasMessages(Handler h) { + if (h == null) { + return false; + } + return findOrRemoveMessages(h, -1, null, null, 0, mMatchHandler, false); + } + + void removeMessages(Handler h, int what, Object object) { + if (h == null) { + return; + } + findOrRemoveMessages(h, what, object, null, 0, mMatchHandlerWhatAndObject, true); + } + + void removeEqualMessages(Handler h, int what, Object object) { + if (h == null) { + return; + } + findOrRemoveMessages(h, what, object, null, 0, mMatchHandlerWhatAndObjectEquals, true); + } + + void removeMessages(Handler h, Runnable r, Object object) { + if (h == null || r == null) { + return; + } + findOrRemoveMessages(h, -1, object, r, 0, mMatchHandlerRunnableAndObject, true); + } + + private static class MatchHandlerRunnableAndObjectEquals extends MessageCompare { + @Override + public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r, + long when) { + if (m.target == h && m.callback == r && (object == null || object.equals(m.obj))) { + return true; + } + return false; + } + } + private final MatchHandlerRunnableAndObjectEquals mMatchHandlerRunnableAndObjectEquals = + new MatchHandlerRunnableAndObjectEquals(); + void removeEqualMessages(Handler h, Runnable r, Object object) { + if (h == null || r == null) { + return; + } + findOrRemoveMessages(h, -1, object, r, 0, mMatchHandlerRunnableAndObjectEquals, true); + } + + private static class MatchHandlerAndObject extends MessageCompare { + @Override + public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r, + long when) { + if (m.target == h && (object == null || m.obj == object)) { + return true; + } + return false; + } + } + private final MatchHandlerAndObject mMatchHandlerAndObject = new MatchHandlerAndObject(); + void removeCallbacksAndMessages(Handler h, Object object) { + if (h == null) { + return; + } + findOrRemoveMessages(h, -1, object, null, 0, mMatchHandlerAndObject, true); + } + + private static class MatchHandlerAndObjectEquals extends MessageCompare { + @Override + public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r, + long when) { + if (m.target == h && (object == null || object.equals(m.obj))) { + return true; + } + return false; + } + } + private final MatchHandlerAndObjectEquals mMatchHandlerAndObjectEquals = + new MatchHandlerAndObjectEquals(); + void removeCallbacksAndEqualMessages(Handler h, Object object) { + if (h == null) { + return; + } + findOrRemoveMessages(h, -1, object, null, 0, mMatchHandlerAndObjectEquals, true); + } + + private static class MatchAllMessages extends MessageCompare { + @Override + public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r, + long when) { + return true; + } + } + private final MatchAllMessages mMatchAllMessages = new MatchAllMessages(); + private void removeAllMessages() { + findOrRemoveMessages(null, -1, null, null, 0, mMatchAllMessages, true); + } + + private static class MatchAllFutureMessages extends MessageCompare { + @Override + public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r, + long when) { + if (m.when > when) { + return true; + } + return false; + } + } + private final MatchAllFutureMessages mMatchAllFutureMessages = new MatchAllFutureMessages(); + private void removeAllFutureMessages() { + findOrRemoveMessages(null, -1, null, null, SystemClock.uptimeMillis(), + mMatchAllFutureMessages, true); + } + + private void printPriorityQueueNodes() { + Iterator<MessageNode> iterator = mPriorityQueue.iterator(); + + Log.d(TAG, "* Dump priority queue"); + while (iterator.hasNext()) { + MessageNode msgNode = iterator.next(); + Log.d(TAG, "** MessageNode what: " + msgNode.mMessage.what + " when " + + msgNode.mMessage.when + " seq: " + msgNode.mInsertSeq); + } + } + + private int dumpPriorityQueue(ConcurrentSkipListSet<MessageNode> queue, Printer pw, + String prefix, Handler h, int n) { + int count = 0; + long now = SystemClock.uptimeMillis(); + + for (MessageNode msgNode : queue) { + Message msg = msgNode.mMessage; + if (h == null || h == msg.target) { + pw.println(prefix + "Message " + (n + count) + ": " + msg.toString(now)); + } + count++; + } + return count; + } + + void dump(Printer pw, String prefix, Handler h) { + long now = SystemClock.uptimeMillis(); + int n = 0; + + pw.println(prefix + "(MessageQueue is using Concurrent implementation)"); + + StackNode node = (StackNode) sState.getVolatile(this); + while (node != null) { + if (node.isMessageNode()) { + Message msg = ((MessageNode) node).mMessage; + if (h == null || h == msg.target) { + pw.println(prefix + "Message " + n + ": " + msg.toString(now)); + } + node = ((MessageNode) node).mNext; + } else { + pw.println(prefix + "State: " + node); + node = null; + } + n++; + } + + pw.println(prefix + "PriorityQueue Messages: "); + n += dumpPriorityQueue(mPriorityQueue, pw, prefix, h, n); + pw.println(prefix + "AsyncPriorityQueue Messages: "); + n += dumpPriorityQueue(mAsyncPriorityQueue, pw, prefix, h, n); + + pw.println(prefix + "(Total messages: " + n + ", polling=" + isPolling() + + ", quitting=" + (boolean) sQuitting.getVolatile(this) + ")"); + } + + private int dumpPriorityQueue(ConcurrentSkipListSet<MessageNode> queue, + ProtoOutputStream proto) { + int count = 0; + + for (MessageNode msgNode : queue) { + Message msg = msgNode.mMessage; + msg.dumpDebug(proto, MessageQueueProto.MESSAGES); + count++; + } + return count; + } + + void dumpDebug(ProtoOutputStream proto, long fieldId) { + final long messageQueueToken = proto.start(fieldId); + + StackNode node = (StackNode) sState.getVolatile(this); + while (node.isMessageNode()) { + Message msg = ((MessageNode) node).mMessage; + msg.dumpDebug(proto, MessageQueueProto.MESSAGES); + node = ((MessageNode) node).mNext; + } + + dumpPriorityQueue(mPriorityQueue, proto); + dumpPriorityQueue(mAsyncPriorityQueue, proto); + + proto.write(MessageQueueProto.IS_POLLING_LOCKED, isPolling()); + proto.write(MessageQueueProto.IS_QUITTING, (boolean) sQuitting.getVolatile(this)); + proto.end(messageQueueToken); + } + + /** + * Adds a file descriptor listener to receive notification when file descriptor + * related events occur. + * <p> + * If the file descriptor has already been registered, the specified events + * and listener will replace any that were previously associated with it. + * It is not possible to set more than one listener per file descriptor. + * </p><p> + * It is important to always unregister the listener when the file descriptor + * is no longer of use. + * </p> + * + * @param fd The file descriptor for which a listener will be registered. + * @param events The set of events to receive: a combination of the + * {@link OnFileDescriptorEventListener#EVENT_INPUT}, + * {@link OnFileDescriptorEventListener#EVENT_OUTPUT}, and + * {@link OnFileDescriptorEventListener#EVENT_ERROR} event masks. If the requested + * set of events is zero, then the listener is unregistered. + * @param listener The listener to invoke when file descriptor events occur. + * + * @see OnFileDescriptorEventListener + * @see #removeOnFileDescriptorEventListener + */ + @android.ravenwood.annotation.RavenwoodThrow(blockedBy = android.os.ParcelFileDescriptor.class) + public void addOnFileDescriptorEventListener(@NonNull FileDescriptor fd, + @OnFileDescriptorEventListener.Events int events, + @NonNull OnFileDescriptorEventListener listener) { + if (fd == null) { + throw new IllegalArgumentException("fd must not be null"); + } + if (listener == null) { + throw new IllegalArgumentException("listener must not be null"); + } + + synchronized (mFileDescriptorRecordsLock) { + updateOnFileDescriptorEventListenerLocked(fd, events, listener); + } + } + + /** + * Removes a file descriptor listener. + * <p> + * This method does nothing if no listener has been registered for the + * specified file descriptor. + * </p> + * + * @param fd The file descriptor whose listener will be unregistered. + * + * @see OnFileDescriptorEventListener + * @see #addOnFileDescriptorEventListener + */ + @android.ravenwood.annotation.RavenwoodThrow(blockedBy = android.os.ParcelFileDescriptor.class) + public void removeOnFileDescriptorEventListener(@NonNull FileDescriptor fd) { + if (fd == null) { + throw new IllegalArgumentException("fd must not be null"); + } + + synchronized (mFileDescriptorRecordsLock) { + updateOnFileDescriptorEventListenerLocked(fd, 0, null); + } + } + + @android.ravenwood.annotation.RavenwoodThrow(blockedBy = android.os.ParcelFileDescriptor.class) + private void updateOnFileDescriptorEventListenerLocked(FileDescriptor fd, int events, + OnFileDescriptorEventListener listener) { + final int fdNum = fd.getInt$(); + + int index = -1; + FileDescriptorRecord record = null; + if (mFileDescriptorRecords != null) { + index = mFileDescriptorRecords.indexOfKey(fdNum); + if (index >= 0) { + record = mFileDescriptorRecords.valueAt(index); + if (record != null && record.mEvents == events) { + return; + } + } + } + + if (events != 0) { + events |= OnFileDescriptorEventListener.EVENT_ERROR; + if (record == null) { + if (mFileDescriptorRecords == null) { + mFileDescriptorRecords = new SparseArray<FileDescriptorRecord>(); + } + record = new FileDescriptorRecord(fd, events, listener); + mFileDescriptorRecords.put(fdNum, record); + } else { + record.mListener = listener; + record.mEvents = events; + record.mSeq += 1; + } + nativeSetFileDescriptorEvents(mPtr, fdNum, events); + } else if (record != null) { + record.mEvents = 0; + mFileDescriptorRecords.removeAt(index); + nativeSetFileDescriptorEvents(mPtr, fdNum, 0); + } + } + + // Called from native code. + private int dispatchEvents(int fd, int events) { + // Get the file descriptor record and any state that might change. + final FileDescriptorRecord record; + final int oldWatchedEvents; + final OnFileDescriptorEventListener listener; + final int seq; + synchronized (mFileDescriptorRecordsLock) { + record = mFileDescriptorRecords.get(fd); + if (record == null) { + return 0; // spurious, no listener registered + } + + oldWatchedEvents = record.mEvents; + events &= oldWatchedEvents; // filter events based on current watched set + if (events == 0) { + return oldWatchedEvents; // spurious, watched events changed + } + + listener = record.mListener; + seq = record.mSeq; + } + + // Invoke the listener outside of the lock. + int newWatchedEvents = listener.onFileDescriptorEvents( + record.mDescriptor, events); + if (newWatchedEvents != 0) { + newWatchedEvents |= OnFileDescriptorEventListener.EVENT_ERROR; + } + + // Update the file descriptor record if the listener changed the set of + // events to watch and the listener itself hasn't been updated since. + if (newWatchedEvents != oldWatchedEvents) { + synchronized (mFileDescriptorRecordsLock) { + int index = mFileDescriptorRecords.indexOfKey(fd); + if (index >= 0 && mFileDescriptorRecords.valueAt(index) == record + && record.mSeq == seq) { + record.mEvents = newWatchedEvents; + if (newWatchedEvents == 0) { + mFileDescriptorRecords.removeAt(index); + } + } + } + } + + // Return the new set of events to watch for native code to take care of. + return newWatchedEvents; + } + + /** + * Callback interface for discovering when a thread is going to block + * waiting for more messages. + */ + public static interface IdleHandler { + /** + * Called when the message queue has run out of messages and will now + * wait for more. Return true to keep your idle handler active, false + * to have it removed. This may be called if there are still messages + * pending in the queue, but they are all scheduled to be dispatched + * after the current time. + */ + boolean queueIdle(); + } + + /** + * A listener which is invoked when file descriptor related events occur. + */ + public interface OnFileDescriptorEventListener { + /** + * File descriptor event: Indicates that the file descriptor is ready for input + * operations, such as reading. + * <p> + * The listener should read all available data from the file descriptor + * then return <code>true</code> to keep the listener active or <code>false</code> + * to remove the listener. + * </p><p> + * In the case of a socket, this event may be generated to indicate + * that there is at least one incoming connection that the listener + * should accept. + * </p><p> + * This event will only be generated if the {@link #EVENT_INPUT} event mask was + * specified when the listener was added. + * </p> + */ + public static final int EVENT_INPUT = 1 << 0; + + /** + * File descriptor event: Indicates that the file descriptor is ready for output + * operations, such as writing. + * <p> + * The listener should write as much data as it needs. If it could not + * write everything at once, then it should return <code>true</code> to + * keep the listener active. Otherwise, it should return <code>false</code> + * to remove the listener then re-register it later when it needs to write + * something else. + * </p><p> + * This event will only be generated if the {@link #EVENT_OUTPUT} event mask was + * specified when the listener was added. + * </p> + */ + public static final int EVENT_OUTPUT = 1 << 1; + + /** + * File descriptor event: Indicates that the file descriptor encountered a + * fatal error. + * <p> + * File descriptor errors can occur for various reasons. One common error + * is when the remote peer of a socket or pipe closes its end of the connection. + * </p><p> + * This event may be generated at any time regardless of whether the + * {@link #EVENT_ERROR} event mask was specified when the listener was added. + * </p> + */ + public static final int EVENT_ERROR = 1 << 2; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag = true, prefix = { "EVENT_" }, value = { + EVENT_INPUT, + EVENT_OUTPUT, + EVENT_ERROR + }) + public @interface Events {} + + /** + * Called when a file descriptor receives events. + * + * @param fd The file descriptor. + * @param events The set of events that occurred: a combination of the + * {@link #EVENT_INPUT}, {@link #EVENT_OUTPUT}, and {@link #EVENT_ERROR} event masks. + * @return The new set of events to watch, or 0 to unregister the listener. + * + * @see #EVENT_INPUT + * @see #EVENT_OUTPUT + * @see #EVENT_ERROR + */ + @Events int onFileDescriptorEvents(@NonNull FileDescriptor fd, @Events int events); + } + + static final class FileDescriptorRecord { + public final FileDescriptor mDescriptor; + public int mEvents; + public OnFileDescriptorEventListener mListener; + public int mSeq; + + public FileDescriptorRecord(FileDescriptor descriptor, + int events, OnFileDescriptorEventListener listener) { + mDescriptor = descriptor; + mEvents = events; + mListener = listener; + } + } +} diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java index a459aaa42930..2c7b9c02330e 100644 --- a/core/java/android/os/Environment.java +++ b/core/java/android/os/Environment.java @@ -159,6 +159,8 @@ public class Environment { @UnsupportedAppUsage private static UserEnvironment sCurrentUser; private static boolean sUserRequired; + private static Boolean sLegacyStorageAppOp; + private static Boolean sNoIsolatedStorageAppOp; static { initForCurrentUser(); @@ -1459,15 +1461,23 @@ public class Environment { final AppOpsManager appOps = context.getSystemService(AppOpsManager.class); final String opPackageName = context.getOpPackageName(); - if (appOps.noteOpNoThrow(AppOpsManager.OP_LEGACY_STORAGE, uid, - opPackageName) == AppOpsManager.MODE_ALLOWED) { - return true; + if (sLegacyStorageAppOp == null) { + sLegacyStorageAppOp = + appOps.checkOpNoThrow(AppOpsManager.OP_LEGACY_STORAGE, uid, opPackageName) == + AppOpsManager.MODE_ALLOWED; + } + if (sLegacyStorageAppOp) { + return sLegacyStorageAppOp; } // Legacy external storage access is granted to instrumentations invoked with // "--no-isolated-storage" flag. - return appOps.noteOpNoThrow(AppOpsManager.OP_NO_ISOLATED_STORAGE, uid, - opPackageName) == AppOpsManager.MODE_ALLOWED; + if (sNoIsolatedStorageAppOp == null) { + sNoIsolatedStorageAppOp = + appOps.checkOpNoThrow(AppOpsManager.OP_NO_ISOLATED_STORAGE, uid, + opPackageName) == AppOpsManager.MODE_ALLOWED; + } + return sNoIsolatedStorageAppOp; } private static boolean isScopedStorageEnforced(boolean defaultScopedStorage, diff --git a/core/java/android/os/MessageQueue.java b/core/java/android/os/LegacyMessageQueue/MessageQueue.java index 5b711c9d8401..6b9b3496d1c0 100644 --- a/core/java/android/os/MessageQueue.java +++ b/core/java/android/os/LegacyMessageQueue/MessageQueue.java @@ -20,6 +20,9 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; +import android.os.Handler; +import android.os.Process; +import android.os.Trace; import android.util.Log; import android.util.Printer; import android.util.SparseArray; @@ -29,6 +32,7 @@ import java.io.FileDescriptor; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; +import java.util.concurrent.atomic.AtomicLong; /** * Low-level class holding the list of messages to be dispatched by a @@ -44,6 +48,7 @@ import java.util.ArrayList; public final class MessageQueue { private static final String TAG = "MessageQueue"; private static final boolean DEBUG = false; + private static final boolean TRACE = false; // True if the message queue can be quit. @UnsupportedAppUsage @@ -326,6 +331,8 @@ public final class MessageQueue { return newWatchedEvents; } + private static final AtomicLong mMessagesDelivered = new AtomicLong(); + @UnsupportedAppUsage Message next() { // Return here if the message loop has already quit and been disposed. @@ -381,6 +388,9 @@ public final class MessageQueue { if (msg.isAsynchronous()) { mAsyncMessageCount--; } + if (TRACE) { + Trace.setCounter("MQ.Delivered", mMessagesDelivered.incrementAndGet()); + } return msg; } } else { @@ -794,7 +804,7 @@ public final class MessageQueue { Message n = p.next; if (n != null) { if (n.target == h && n.what == what - && (object == null || n.obj == object)) { + && (object == null || n.obj == object)) { Message nn = n.next; if (n.isAsynchronous()) { mAsyncMessageCount--; @@ -841,7 +851,7 @@ public final class MessageQueue { Message n = p.next; if (n != null) { if (n.target == h && n.what == what - && (object == null || object.equals(n.obj))) { + && (object == null || object.equals(n.obj))) { Message nn = n.next; if (n.isAsynchronous()) { mAsyncMessageCount--; @@ -888,7 +898,7 @@ public final class MessageQueue { Message n = p.next; if (n != null) { if (n.target == h && n.callback == r - && (object == null || n.obj == object)) { + && (object == null || n.obj == object)) { Message nn = n.next; if (n.isAsynchronous()) { mAsyncMessageCount--; @@ -935,7 +945,7 @@ public final class MessageQueue { Message n = p.next; if (n != null) { if (n.target == h && n.callback == r - && (object == null || object.equals(n.obj))) { + && (object == null || object.equals(n.obj))) { Message nn = n.next; if (n.isAsynchronous()) { mAsyncMessageCount--; @@ -1093,6 +1103,7 @@ public final class MessageQueue { void dump(Printer pw, String prefix, Handler h) { synchronized (this) { + pw.println(prefix + "(MessageQueue is using Legacy implementation)"); long now = SystemClock.uptimeMillis(); int n = 0; for (Message msg = mMessages; msg != null; msg = msg.next) { diff --git a/core/java/android/os/SemiConcurrentMessageQueue/MessageQueue.java b/core/java/android/os/SemiConcurrentMessageQueue/MessageQueue.java new file mode 100644 index 000000000000..967332fcf80c --- /dev/null +++ b/core/java/android/os/SemiConcurrentMessageQueue/MessageQueue.java @@ -0,0 +1,1589 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.TestApi; +import android.os.Handler; +import android.os.Trace; +import android.util.Log; +import android.util.Printer; +import android.util.SparseArray; +import android.util.proto.ProtoOutputStream; + +import com.android.internal.annotations.GuardedBy; + +import java.io.FileDescriptor; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.PriorityQueue; +import java.util.PriorityQueue; +import java.util.PriorityQueue; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; + +/** + * Low-level class holding the list of messages to be dispatched by a + * {@link Looper}. Messages are not added directly to a MessageQueue, + * but rather through {@link Handler} objects associated with the Looper. + * + * <p>You can retrieve the MessageQueue for the current thread with + * {@link Looper#myQueue() Looper.myQueue()}. + */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass +@android.ravenwood.annotation.RavenwoodNativeSubstitutionClass( + "com.android.platform.test.ravenwood.nativesubstitution.MessageQueue_host") +public final class MessageQueue { + private static final String TAG = "SemiConcurrentMessageQueue"; + private static final boolean DEBUG = false; + private static final boolean TRACE = false; + + // True if the message queue can be quit. + private final boolean mQuitAllowed; + + @SuppressWarnings("unused") + private long mPtr; // used by native code + + @IntDef(value = { + STACK_NODE_MESSAGE, + STACK_NODE_ACTIVE, + STACK_NODE_PARKED, + STACK_NODE_TIMEDPARK}) + @Retention(RetentionPolicy.SOURCE) + private @interface StackNodeType {} + + /* + * Stack node types. STACK_NODE_MESSAGE indicates a node containing a message. + * The other types indicate what state our Looper thread is in. The bottom of + * the stack is always a single state node. Message nodes are added on top. + */ + private static final int STACK_NODE_MESSAGE = 0; + /* + * Active state indicates that next() is processing messages + */ + private static final int STACK_NODE_ACTIVE = 1; + /* + * Parked state indicates that the Looper thread is sleeping indefinitely (nothing to deliver) + */ + private static final int STACK_NODE_PARKED = 2; + /* + * Timed Park state indicates that the Looper thread is sleeping, waiting for a message + * deadline + */ + private static final int STACK_NODE_TIMEDPARK = 3; + + /* Describes a node in the Treiber stack */ + static class StackNode { + @StackNodeType + private final int mType; + + StackNode(@StackNodeType int type) { + mType = type; + } + + @StackNodeType + final int getNodeType() { + return mType; + } + + final boolean isMessageNode() { + return mType == STACK_NODE_MESSAGE; + } + } + + static final class MessageNode extends StackNode implements Comparable<MessageNode> { + private final Message mMessage; + volatile StackNode mNext; + StateNode mBottomOfStack; + boolean mWokeUp; + boolean mRemovedFromStack = false; + final long mInsertSeq; + + MessageNode(@NonNull Message message, long insertSeq) { + super(STACK_NODE_MESSAGE); + mMessage = message; + mInsertSeq = insertSeq; + } + + long getWhen() { + return mMessage.when; + } + + boolean isRemovedFromStack() { + return mRemovedFromStack; + } + + boolean removeFromStack() { + if (!mRemovedFromStack) { + mRemovedFromStack = true; + return true; + } + return false; + } + + boolean isAsync() { + return mMessage.isAsynchronous(); + } + + boolean isBarrier() { + return mMessage.target == null; + } + + @Override + public int compareTo(@NonNull MessageNode messageNode) { + Message other = messageNode.mMessage; + + int compared = Long.compare(mMessage.when, other.when); + if (compared == 0) { + compared = Long.compare(mInsertSeq, messageNode.mInsertSeq); + } + return compared; + } + } + + static class StateNode extends StackNode { + StateNode(int type) { + super(type); + } + } + + static final class TimedParkStateNode extends StateNode { + long mWhenToWake; + + TimedParkStateNode() { + super(STACK_NODE_TIMEDPARK); + } + } + + private static final StateNode sStackStateActive = new StateNode(STACK_NODE_ACTIVE); + private static final StateNode sStackStateParked = new StateNode(STACK_NODE_PARKED); + private final TimedParkStateNode mStackStateTimedPark = new TimedParkStateNode(); + + /* This is the top of our treiber stack. */ + private static final VarHandle sState; + static { + try { + MethodHandles.Lookup l = MethodHandles.lookup(); + sState = l.findVarHandle(MessageQueue.class, "mStateValue", + MessageQueue.StackNode.class); + } catch (Exception e) { + Log.wtf(TAG, "VarHandle lookup failed with exception: " + e); + throw new ExceptionInInitializerError(e); + } + } + + private volatile StackNode mStateValue = sStackStateParked; + @GuardedBy("mPriorityQueue") + private final PriorityQueue<MessageNode> mPriorityQueue = + new PriorityQueue<MessageNode>(); + @GuardedBy("mPriorityQueue") + private final PriorityQueue<MessageNode> mAsyncPriorityQueue = + new PriorityQueue<MessageNode>(); + + /* + * This helps us ensure that messages with the same timestamp are inserted in FIFO order. + * Increments on each insert, starting at 0. MessageNode.compareTo() will compare sequences + * when delivery timestamps are identical. + */ + private static final VarHandle sNextInsertSeq; + private volatile long mNextInsertSeqValue = 0; + /* + * The exception to the FIFO order rule is sendMessageAtFrontOfQueue(). + * Those messages must be in LIFO order - SIGH. + * Decrements on each front of queue insert. + */ + private static final VarHandle sNextFrontInsertSeq; + private volatile long mNextFrontInsertSeqValue = -1; + static { + try { + MethodHandles.Lookup l = MethodHandles.lookup(); + sNextInsertSeq = l.findVarHandle(MessageQueue.class, "mNextInsertSeqValue", + long.class); + sNextFrontInsertSeq = l.findVarHandle(MessageQueue.class, "mNextFrontInsertSeqValue", + long.class); + } catch (Exception e) { + Log.wtf(TAG, "VarHandle lookup failed with exception: " + e); + throw new ExceptionInInitializerError(e); + } + + } + + /* + * Tracks the number of queued and cancelled messages in our stack. + * + * On item cancellation, determine whether to wake next() to flush tombstoned messages. + * We track queued and cancelled counts as two ints packed into a single long. + */ + private static final class MessageCounts { + private static VarHandle sCounts; + private volatile long mCountsValue = 0; + static { + try { + MethodHandles.Lookup l = MethodHandles.lookup(); + sCounts = l.findVarHandle(MessageQueue.MessageCounts.class, "mCountsValue", + long.class); + } catch (Exception e) { + Log.wtf(TAG, "VarHandle lookup failed with exception: " + e); + throw new ExceptionInInitializerError(e); + } + } + /* We use a special value to indicate when next() has been woken for flush. */ + private static final long AWAKE = Long.MAX_VALUE; + /* + * Minimum number of messages in the stack which we need before we consider flushing + * tombstoned items. + */ + private static final int MESSAGE_FLUSH_THRESHOLD = 10; + + private static int numQueued(long val) { + return (int) (val >>> Integer.SIZE); + } + + private static int numCancelled(long val) { + return (int) val; + } + + private static long combineCounts(int queued, int cancelled) { + return ((long) queued << Integer.SIZE) | (long) cancelled; + } + + public void incrementQueued() { + while (true) { + long oldVal = mCountsValue; + int queued = numQueued(oldVal); + int cancelled = numCancelled(oldVal); + /* Use Math.max() to avoid overflow of queued count */ + long newVal = combineCounts(Math.max(queued + 1, queued), cancelled); + + /* Don't overwrite 'AWAKE' state */ + if (oldVal == AWAKE || sCounts.compareAndSet(this, oldVal, newVal)) { + break; + } + } + } + + public boolean incrementCancelled() { + while (true) { + long oldVal = mCountsValue; + if (oldVal == AWAKE) { + return false; + } + int queued = numQueued(oldVal); + int cancelled = numCancelled(oldVal); + boolean needsPurge = queued > MESSAGE_FLUSH_THRESHOLD + && (queued >> 1) < cancelled; + long newVal; + if (needsPurge) { + newVal = AWAKE; + } else { + newVal = combineCounts(queued, + Math.max(cancelled + 1, cancelled)); + } + + if (sCounts.compareAndSet(this, oldVal, newVal)) { + return needsPurge; + } + } + } + + public void clearCounts() { + mCountsValue = 0; + } + } + + private final MessageCounts mMessageCounts = new MessageCounts(); + + private final Object mIdleHandlersLock = new Object(); + @GuardedBy("mIdleHandlersLock") + private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>(); + private IdleHandler[] mPendingIdleHandlers; + + private final Object mFileDescriptorRecordsLock = new Object(); + @GuardedBy("mFileDescriptorRecordsLock") + private SparseArray<FileDescriptorRecord> mFileDescriptorRecords; + + private static final VarHandle sQuitting; + private boolean mQuittingValue = false; + static { + try { + MethodHandles.Lookup l = MethodHandles.lookup(); + sQuitting = l.findVarHandle(MessageQueue.class, "mQuittingValue", boolean.class); + } catch (Exception e) { + Log.wtf(TAG, "VarHandle lookup failed with exception: " + e); + throw new ExceptionInInitializerError(e); + } + } + + // The next barrier token. + // Barriers are indicated by messages with a null target whose arg1 field carries the token. + private final AtomicInteger mNextBarrierToken = new AtomicInteger(1); + + private static native long nativeInit(); + private static native void nativeDestroy(long ptr); + private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/ + private static native void nativeWake(long ptr); + private static native boolean nativeIsPolling(long ptr); + private static native void nativeSetFileDescriptorEvents(long ptr, int fd, int events); + + MessageQueue(boolean quitAllowed) { + mQuitAllowed = quitAllowed; + mPtr = nativeInit(); + } + + @Override + protected void finalize() throws Throwable { + try { + dispose(); + } finally { + super.finalize(); + } + } + + // Disposes of the underlying message queue. + // Must only be called on the looper thread or the finalizer. + private void dispose() { + if (mPtr != 0) { + nativeDestroy(mPtr); + mPtr = 0; + } + } + + /** + * Returns true if the looper has no pending messages which are due to be processed. + * + * <p>This method is safe to call from any thread. + * + * @return True if the looper is idle. + */ + public boolean isIdle() { + MessageNode msgNode = null; + MessageNode asyncMsgNode = null; + + synchronized (mPriorityQueue) { + msgNode = mPriorityQueue.peek(); + asyncMsgNode = mAsyncPriorityQueue.peek(); + + final long now = SystemClock.uptimeMillis(); + if ((msgNode != null && msgNode.getWhen() <= now) + || (asyncMsgNode != null && asyncMsgNode.getWhen() <= now)) { + return false; + } + } + + return true; + } + + /** + * Add a new {@link IdleHandler} to this message queue. This may be + * removed automatically for you by returning false from + * {@link IdleHandler#queueIdle IdleHandler.queueIdle()} when it is + * invoked, or explicitly removing it with {@link #removeIdleHandler}. + * + * <p>This method is safe to call from any thread. + * + * @param handler The IdleHandler to be added. + */ + public void addIdleHandler(@NonNull IdleHandler handler) { + if (handler == null) { + throw new NullPointerException("Can't add a null IdleHandler"); + } + synchronized (mIdleHandlersLock) { + mIdleHandlers.add(handler); + } + } + + /** + * Remove an {@link IdleHandler} from the queue that was previously added + * with {@link #addIdleHandler}. If the given object is not currently + * in the idle list, nothing is done. + * + * <p>This method is safe to call from any thread. + * + * @param handler The IdleHandler to be removed. + */ + public void removeIdleHandler(@NonNull IdleHandler handler) { + synchronized (mIdleHandlersLock) { + mIdleHandlers.remove(handler); + } + } + + /** + * Returns whether this looper's thread is currently polling for more work to do. + * This is a good signal that the loop is still alive rather than being stuck + * handling a callback. Note that this method is intrinsically racy, since the + * state of the loop can change before you get the result back. + * + * <p>This method is safe to call from any thread. + * + * @return True if the looper is currently polling for events. + * @hide + */ + public boolean isPolling() { + // If the loop is quitting then it must not be idling. + // We can assume mPtr != 0 when sQuitting is false. + return !((boolean) sQuitting.getVolatile(this)) && nativeIsPolling(mPtr); + } + + /* Helper to choose the correct queue to insert into. */ + @GuardedBy("mPriorityQueue") + private void insertIntoPriorityQueue(MessageNode msgNode) { + if (msgNode.isAsync()) { + mAsyncPriorityQueue.offer(msgNode); + } else { + mPriorityQueue.offer(msgNode); + } + } + + @GuardedBy("mPriorityQueue") + private boolean removeFromPriorityQueue(MessageNode msgNode) { + if (msgNode.isAsync()) { + return mAsyncPriorityQueue.remove(msgNode); + } else { + return mPriorityQueue.remove(msgNode); + } + } + + private MessageNode pickEarliestNode(MessageNode nodeA, MessageNode nodeB) { + if (nodeA != null && nodeB != null) { + if (nodeA.compareTo(nodeB) < 0) { + return nodeA; + } + return nodeB; + } + + return nodeA != null ? nodeA : nodeB; + } + + /* Move any non-cancelled messages into the priority queue */ + private void drainStack(StackNode oldTop) { + while (oldTop.isMessageNode()) { + MessageNode oldTopMessageNode = (MessageNode) oldTop; + if (oldTopMessageNode.removeFromStack()) { + insertIntoPriorityQueue(oldTopMessageNode); + } + MessageNode inserted = oldTopMessageNode; + oldTop = oldTopMessageNode.mNext; + } + } + + /* Set the stack state to Active, return a list of nodes to walk. */ + private StackNode swapAndSetStackStateActive() { + while (true) { + /* Set stack state to Active, get node list to walk later */ + StackNode current = (StackNode) sState.getVolatile(this); + if (current == sStackStateActive + || sState.compareAndSet(this, current, sStackStateActive)) { + return current; + } + } + } + + /* This is only read/written from the Looper thread */ + private int mNextPollTimeoutMillis; + private static final AtomicLong mMessagesDelivered = new AtomicLong(); + + private Message nextMessage() { + int i = 0; + + while (true) { + if (DEBUG) { + Log.d(TAG, "nextMessage loop #" + i); + i++; + } + + /* This protects us from racing with remove. Enqueue can still add items. */ + synchronized (mPriorityQueue) { + + /* + * Set our state to active, drain any items from the stack into our priority queues + */ + StackNode oldTop; + oldTop = swapAndSetStackStateActive(); + drainStack(oldTop); + + /* + * The objective of this next block of code is to: + * - find a message to return (if any is ready) + * - find a next message we would like to return, after scheduling. + * - we make our scheduling decision based on this next message (if it exists). + * + * We have two queues to juggle and the presence of barriers throws an additional + * wrench into our plans. + */ + + /* Get the first node from each queue */ + MessageNode msgNode = mPriorityQueue.peek(); + MessageNode asyncMsgNode = mAsyncPriorityQueue.peek(); + + if (DEBUG) { + if (msgNode != null) { + Message msg = msgNode.mMessage; + Log.d(TAG, "Next found node what: " + msg.what + " when: " + msg.when + + " seq: " + msgNode.mInsertSeq + "barrier: " + + msgNode.isBarrier() + " now: " + + SystemClock.uptimeMillis()); + } + if (asyncMsgNode != null) { + Message msg = asyncMsgNode.mMessage; + Log.d(TAG, "Next found async node what: " + msg.what + " when: " + msg.when + + " seq: " + asyncMsgNode.mInsertSeq + "barrier: " + + asyncMsgNode.isBarrier() + " now: " + + SystemClock.uptimeMillis()); + } + } + + /* + * the node which we will return, null if none are ready + */ + MessageNode found = null; + /* + * The node from which we will determine our next wakeup time. + * Null indicates there is no next message ready. If we found a node, + * we can leave this null as Looper will call us again after delivering + * the message. + */ + MessageNode next = null; + + long now = SystemClock.uptimeMillis(); + /* + * If we have a barrier we should return the async node if it exists and is + * ready + */ + if (msgNode != null && msgNode.isBarrier()) { + if (asyncMsgNode != null && now >= asyncMsgNode.getWhen()) { + found = asyncMsgNode; + removeFromPriorityQueue(found); + } else { + next = asyncMsgNode; + } + } else { /* No barrier. */ + MessageNode earliest; + /* + * If we have two messages, pick the earliest option from either queue. + * Otherwise grab whichever node is non-null. If both are null we'll fall + * through. + */ + earliest = pickEarliestNode(msgNode, asyncMsgNode); + + if (earliest != null) { + if (now >= earliest.getWhen()) { + found = earliest; + removeFromPriorityQueue(found); + } else { + next = earliest; + } + } + } + + if (DEBUG) { + if (found != null) { + Message msg = found.mMessage; + Log.d(TAG, "Will deliver node what: " + msg.what + " when: " + msg.when + + " seq: " + found.mInsertSeq + " barrier: " + + found.isBarrier() + " async: " + found.isAsync() + + " now: " + SystemClock.uptimeMillis()); + } else { + Log.d(TAG, "No node to deliver"); + } + if (next != null) { + Message msg = next.mMessage; + Log.d(TAG, "Next node what: " + msg.what + " when: " + msg.when + " seq: " + + next.mInsertSeq + " barrier: " + next.isBarrier() + + " async: " + next.isAsync() + + " now: " + SystemClock.uptimeMillis()); + } else { + Log.d(TAG, "No next node"); + } + } + + /* + * If we have a found message, we will get called again so there's no need to set + * state. + * In that case we can leave our state as ACTIVE. + * + * Otherwise we should determine how to park the thread. + */ + StateNode nextOp = sStackStateActive; + if (found == null) { + if (next == null) { + /* No message to deliver, sleep indefinitely */ + mNextPollTimeoutMillis = -1; + nextOp = sStackStateParked; + if (DEBUG) { + Log.d(TAG, "nextMessage next state is StackStateParked"); + } + } else { + /* Message not ready, or we found one to deliver already, set a timeout */ + long nextMessageWhen = next.getWhen(); + if (nextMessageWhen > now) { + mNextPollTimeoutMillis = (int) Math.min(nextMessageWhen - now, + Integer.MAX_VALUE); + } else { + mNextPollTimeoutMillis = 0; + } + + mStackStateTimedPark.mWhenToWake = now + mNextPollTimeoutMillis; + nextOp = mStackStateTimedPark; + if (DEBUG) { + Log.d(TAG, "nextMessage next state is StackStateTimedParked " + + " next timeout ms " + mNextPollTimeoutMillis + + " mWhenToWake: " + mStackStateTimedPark.mWhenToWake + + " now " + now); + } + } + } + + /* + * Try to swap our state from Active back to Park or TimedPark. If we raced with + * enqueue, loop back around to pick up any new items. + */ + if (sState.compareAndSet(this, sStackStateActive, nextOp)) { + mMessageCounts.clearCounts(); + if (found != null) { + if (TRACE) { + Trace.setCounter("MQ.Delivered", mMessagesDelivered.incrementAndGet()); + } + return found.mMessage; + } + return null; + } + if (found != null) { + /* + * Add this node back - we will be adding new nodes into our priority queue, and + * recalculating what to return. + */ + insertIntoPriorityQueue(found); + } + } + } + } + + Message next() { + final long ptr = mPtr; + if (ptr == 0) { + return null; + } + + mNextPollTimeoutMillis = 0; + int pendingIdleHandlerCount = -1; // -1 only during first iteration + while (true) { + if (mNextPollTimeoutMillis != 0) { + Binder.flushPendingCommands(); + } + + nativePollOnce(ptr, mNextPollTimeoutMillis); + + Message msg = nextMessage(); + if (msg != null) { + msg.markInUse(); + return msg; + } + + if ((boolean) sQuitting.getVolatile(this)) { + return null; + } + + synchronized (mIdleHandlersLock) { + // If first time idle, then get the number of idlers to run. + // Idle handles only run if the queue is empty or if the first message + // in the queue (possibly a barrier) is due to be handled in the future. + if (pendingIdleHandlerCount < 0 + && mNextPollTimeoutMillis != 0) { + pendingIdleHandlerCount = mIdleHandlers.size(); + } + if (pendingIdleHandlerCount <= 0) { + // No idle handlers to run. Loop and wait some more. + continue; + } + + if (mPendingIdleHandlers == null) { + mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)]; + } + mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers); + } + + // Run the idle handlers. + // We only ever reach this code block during the first iteration. + for (int i = 0; i < pendingIdleHandlerCount; i++) { + final IdleHandler idler = mPendingIdleHandlers[i]; + mPendingIdleHandlers[i] = null; // release the reference to the handler + + boolean keep = false; + try { + keep = idler.queueIdle(); + } catch (Throwable t) { + Log.wtf(TAG, "IdleHandler threw exception", t); + } + + if (!keep) { + synchronized (mIdleHandlersLock) { + mIdleHandlers.remove(idler); + } + } + } + + // Reset the idle handler count to 0 so we do not run them again. + pendingIdleHandlerCount = 0; + + // While calling an idle handler, a new message could have been delivered + // so go back and look again for a pending message without waiting. + mNextPollTimeoutMillis = 0; + } + } + + void quit(boolean safe) { + if (!mQuitAllowed) { + throw new IllegalStateException("Main thread not allowed to quit."); + } + synchronized (mIdleHandlersLock) { + if (sQuitting.compareAndSet(this, false, true)) { + if (safe) { + removeAllFutureMessages(); + } else { + removeAllMessages(); + } + + // We can assume mPtr != 0 because sQuitting was previously false. + nativeWake(mPtr); + } + } + } + + boolean enqueueMessage(@NonNull Message msg, long when) { + if (msg.target == null) { + throw new IllegalArgumentException("Message must have a target."); + } + + if (msg.isInUse()) { + throw new IllegalStateException(msg + " This message is already in use."); + } + + return enqueueMessageUnchecked(msg, when); + } + + private boolean enqueueMessageUnchecked(@NonNull Message msg, long when) { + if ((boolean) sQuitting.getVolatile(this)) { + IllegalStateException e = new IllegalStateException( + msg.target + " sending message to a Handler on a dead thread"); + Log.w(TAG, e.getMessage(), e); + msg.recycleUnchecked(); + return false; + } + + long seq = when != 0 ? ((long)sNextInsertSeq.getAndAdd(this, 1L) + 1L) + : ((long)sNextFrontInsertSeq.getAndAdd(this, -1L) - 1L); + /* TODO: Add a MessageNode member to Message so we can avoid this allocation */ + MessageNode node = new MessageNode(msg, seq); + msg.when = when; + msg.markInUse(); + + if (DEBUG) { + Log.d(TAG, "Insert message what: " + msg.what + " when: " + msg.when + " seq: " + + node.mInsertSeq + " barrier: " + node.isBarrier() + " async: " + + node.isAsync() + " now: " + SystemClock.uptimeMillis()); + } + + while (true) { + StackNode old = (StackNode) sState.getVolatile(this); + boolean wakeNeeded; + boolean inactive; + + node.mNext = old; + switch (old.getNodeType()) { + case STACK_NODE_ACTIVE: + /* + * The worker thread is currently active and will process any elements added to + * the stack before parking again. + */ + node.mBottomOfStack = (StateNode) old; + inactive = false; + node.mWokeUp = true; + wakeNeeded = false; + break; + + case STACK_NODE_PARKED: + node.mBottomOfStack = (StateNode) old; + inactive = true; + node.mWokeUp = true; + wakeNeeded = true; + break; + + case STACK_NODE_TIMEDPARK: + node.mBottomOfStack = (StateNode) old; + inactive = true; + wakeNeeded = mStackStateTimedPark.mWhenToWake >= node.getWhen(); + node.mWokeUp = wakeNeeded; + break; + + default: + MessageNode oldMessage = (MessageNode) old; + + node.mBottomOfStack = oldMessage.mBottomOfStack; + int bottomType = node.mBottomOfStack.getNodeType(); + inactive = bottomType >= STACK_NODE_PARKED; + wakeNeeded = (bottomType == STACK_NODE_TIMEDPARK + && mStackStateTimedPark.mWhenToWake >= node.getWhen() + && !oldMessage.mWokeUp); + node.mWokeUp = oldMessage.mWokeUp || wakeNeeded; + break; + } + if (sState.compareAndSet(this, old, node)) { + if (inactive) { + if (wakeNeeded) { + nativeWake(mPtr); + } else { + mMessageCounts.incrementQueued(); + } + } + return true; + } + } + } + + /** + * Posts a synchronization barrier to the Looper's message queue. + * + * Message processing occurs as usual until the message queue encounters the + * synchronization barrier that has been posted. When the barrier is encountered, + * later synchronous messages in the queue are stalled (prevented from being executed) + * until the barrier is released by calling {@link #removeSyncBarrier} and specifying + * the token that identifies the synchronization barrier. + * + * This method is used to immediately postpone execution of all subsequently posted + * synchronous messages until a condition is met that releases the barrier. + * Asynchronous messages (see {@link Message#isAsynchronous} are exempt from the barrier + * and continue to be processed as usual. + * + * This call must be always matched by a call to {@link #removeSyncBarrier} with + * the same token to ensure that the message queue resumes normal operation. + * Otherwise the application will probably hang! + * + * @return A token that uniquely identifies the barrier. This token must be + * passed to {@link #removeSyncBarrier} to release the barrier. + * + * @hide + */ + @TestApi + public int postSyncBarrier() { + return postSyncBarrier(SystemClock.uptimeMillis()); + } + + private int postSyncBarrier(long when) { + final int token = mNextBarrierToken.getAndIncrement(); + final Message msg = Message.obtain(); + + msg.markInUse(); + msg.arg1 = token; + + if (!enqueueMessageUnchecked(msg, when)) { + Log.wtf(TAG, "Unexpected error while adding sync barrier!"); + return -1; + } + + return token; + } + + private class MatchBarrierToken extends MessageCompare { + int mBarrierToken; + + MatchBarrierToken(int token) { + super(); + mBarrierToken = token; + } + + @Override + public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r, + long when) { + if (m.target == null && m.arg1 == mBarrierToken) { + return true; + } + return false; + } + } + + /** + * Removes a synchronization barrier. + * + * @param token The synchronization barrier token that was returned by + * {@link #postSyncBarrier}. + * + * @throws IllegalStateException if the barrier was not found. + * + * @hide + */ + @TestApi + public void removeSyncBarrier(int token) { + boolean removed; + MessageNode first; + final MatchBarrierToken matchBarrierToken = new MatchBarrierToken(token); + + synchronized (mPriorityQueue) { + try { + /* Retain the first element to see if we are currently stuck on a barrier. */ + first = mPriorityQueue.peek(); + } catch (NoSuchElementException e) { + /* The queue is empty */ + first = null; + } + + removed = findOrRemoveMessages(null, 0, null, null, 0, matchBarrierToken, true); + if (removed && first != null) { + Message m = first.mMessage; + if (m.target == null && m.arg1 == token) { + /* Wake up next() in case it was sleeping on this barrier. */ + nativeWake(mPtr); + } + } else if (!removed) { + throw new IllegalStateException("The specified message queue synchronization " + + " barrier token has not been posted or has already been removed."); + } + } + } + + private StateNode getStateNode(StackNode node) { + if (node.isMessageNode()) { + return ((MessageNode) node).mBottomOfStack; + } + return (StateNode) node; + } + + /* + * This class is used to find matches for hasMessages() and removeMessages() + */ + private abstract static class MessageCompare { + public abstract boolean compareMessage(Message m, Handler h, int what, Object object, + Runnable r, long when); + } + @GuardedBy("mPriorityQueue") + private boolean stackHasMessages(Handler h, int what, Object object, Runnable r, long when, + MessageCompare compare, boolean removeMatches) { + boolean found = false; + StackNode top = (StackNode) sState.getVolatile(this); + StateNode bottom = getStateNode(top); + + /* No messages to search. */ + if (!top.isMessageNode()) { + return false; + } + + /* + * We have messages that we may tombstone. Walk the stack until we hit the bottom. + * next() will remove them on it's next pass. + */ + if (!(top instanceof MessageNode)) { + Log.wtf(TAG, "Unknown node type found in Trieber stack"); + } + MessageNode p = (MessageNode) top; + + while (true) { + if (compare.compareMessage(p.mMessage, h, what, object, r, when)) { + found = true; + if (DEBUG) { + Log.w(TAG, "stackHasMessages node matches"); + } + if (removeMatches) { + if (p.removeFromStack()) { + p.mMessage.recycleUnchecked(); + if (mMessageCounts.incrementCancelled()) { + nativeWake(mPtr); + } + } + } else { + return true; + } + } + + StackNode n = p.mNext; + if (!n.isMessageNode()) { + /* We reached the end of the stack */ + return found; + } + p = (MessageNode) n; + } + } + + @GuardedBy("mPriorityQueue") + private boolean priorityQueueHasMessage(PriorityQueue queue, Handler h, + int what, Object object, Runnable r, long when, MessageCompare compare, + boolean removeMatches) { + Iterator<MessageNode> iterator = queue.iterator(); + boolean found = false; + + while (iterator.hasNext()) { + MessageNode msg = iterator.next(); + + if (compare.compareMessage(msg.mMessage, h, what, object, r, when)) { + if (removeMatches) { + found = true; + iterator.remove(); + msg.mMessage.recycleUnchecked(); + } else { + return true; + } + } + } + return found; + } + + private boolean findOrRemoveMessages(Handler h, int what, Object object, Runnable r, long when, + MessageCompare compare, boolean removeMatches) { + boolean foundInStack, foundInQueue; + + synchronized (mPriorityQueue) { + foundInStack = stackHasMessages(h, what, object, r, when, compare, removeMatches); + foundInQueue = priorityQueueHasMessage(mPriorityQueue, h, what, object, r, when, + compare, removeMatches); + foundInQueue |= priorityQueueHasMessage(mAsyncPriorityQueue, h, what, object, r, when, + compare, removeMatches); + + return foundInStack || foundInQueue; + } + } + + private static class MatchHandlerWhatAndObject extends MessageCompare { + @Override + public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r, + long when) { + if (m.target == h && m.what == what && (object == null || m.obj == object)) { + return true; + } + return false; + } + } + private final MatchHandlerWhatAndObject mMatchHandlerWhatAndObject = + new MatchHandlerWhatAndObject(); + boolean hasMessages(Handler h, int what, Object object) { + if (h == null) { + return false; + } + + return findOrRemoveMessages(h, what, object, null, 0, mMatchHandlerWhatAndObject, false); + } + + private static class MatchHandlerWhatAndObjectEquals extends MessageCompare { + @Override + public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r, + long when) { + if (m.target == h && m.what == what && (object == null || object.equals(m.obj))) { + return true; + } + return false; + } + } + private final MatchHandlerWhatAndObjectEquals mMatchHandlerWhatAndObjectEquals = + new MatchHandlerWhatAndObjectEquals(); + boolean hasEqualMessages(Handler h, int what, Object object) { + if (h == null) { + return false; + } + + return findOrRemoveMessages(h, what, object, null, 0, mMatchHandlerWhatAndObjectEquals, + false); + } + + private static class MatchHandlerRunnableAndObject extends MessageCompare { + @Override + public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r, + long when) { + if (m.target == h && m.callback == r && (object == null || m.obj == object)) { + return true; + } + return false; + } + } + private final MatchHandlerRunnableAndObject mMatchHandlerRunnableAndObject = + new MatchHandlerRunnableAndObject(); + + boolean hasMessages(Handler h, Runnable r, Object object) { + if (h == null) { + return false; + } + + return findOrRemoveMessages(h, -1, object, r, 0, mMatchHandlerRunnableAndObject, false); + } + + private static class MatchHandler extends MessageCompare { + @Override + public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r, + long when) { + if (m.target == h) { + return true; + } + return false; + } + } + private final MatchHandler mMatchHandler = new MatchHandler(); + boolean hasMessages(Handler h) { + if (h == null) { + return false; + } + return findOrRemoveMessages(h, -1, null, null, 0, mMatchHandler, false); + } + + void removeMessages(Handler h, int what, Object object) { + if (h == null) { + return; + } + findOrRemoveMessages(h, what, object, null, 0, mMatchHandlerWhatAndObject, true); + } + + void removeEqualMessages(Handler h, int what, Object object) { + if (h == null) { + return; + } + findOrRemoveMessages(h, what, object, null, 0, mMatchHandlerWhatAndObjectEquals, true); + } + + void removeMessages(Handler h, Runnable r, Object object) { + if (h == null || r == null) { + return; + } + findOrRemoveMessages(h, -1, object, r, 0, mMatchHandlerRunnableAndObject, true); + } + + private static class MatchHandlerRunnableAndObjectEquals extends MessageCompare { + @Override + public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r, + long when) { + if (m.target == h && m.callback == r && (object == null || object.equals(m.obj))) { + return true; + } + return false; + } + } + private final MatchHandlerRunnableAndObjectEquals mMatchHandlerRunnableAndObjectEquals = + new MatchHandlerRunnableAndObjectEquals(); + void removeEqualMessages(Handler h, Runnable r, Object object) { + if (h == null || r == null) { + return; + } + findOrRemoveMessages(h, -1, object, r, 0, mMatchHandlerRunnableAndObjectEquals, true); + } + + private static class MatchHandlerAndObject extends MessageCompare { + @Override + public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r, + long when) { + if (m.target == h && (object == null || m.obj == object)) { + return true; + } + return false; + } + } + private final MatchHandlerAndObject mMatchHandlerAndObject = new MatchHandlerAndObject(); + void removeCallbacksAndMessages(Handler h, Object object) { + if (h == null) { + return; + } + findOrRemoveMessages(h, -1, object, null, 0, mMatchHandlerAndObject, true); + } + + private static class MatchHandlerAndObjectEquals extends MessageCompare { + @Override + public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r, + long when) { + if (m.target == h && (object == null || object.equals(m.obj))) { + return true; + } + return false; + } + } + private final MatchHandlerAndObjectEquals mMatchHandlerAndObjectEquals = + new MatchHandlerAndObjectEquals(); + void removeCallbacksAndEqualMessages(Handler h, Object object) { + if (h == null) { + return; + } + findOrRemoveMessages(h, -1, object, null, 0, mMatchHandlerAndObjectEquals, true); + } + + private static class MatchAllMessages extends MessageCompare { + @Override + public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r, + long when) { + return true; + } + } + private final MatchAllMessages mMatchAllMessages = new MatchAllMessages(); + private void removeAllMessages() { + findOrRemoveMessages(null, -1, null, null, 0, mMatchAllMessages, true); + } + + private static class MatchAllFutureMessages extends MessageCompare { + @Override + public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r, + long when) { + if (m.when > when) { + return true; + } + return false; + } + } + private final MatchAllFutureMessages mMatchAllFutureMessages = new MatchAllFutureMessages(); + private void removeAllFutureMessages() { + findOrRemoveMessages(null, -1, null, null, SystemClock.uptimeMillis(), + mMatchAllFutureMessages, true); + } + + private void printPriorityQueueNodes() { + Iterator<MessageNode> iterator = mPriorityQueue.iterator(); + + Log.d(TAG, "* Dump priority queue"); + while (iterator.hasNext()) { + MessageNode msgNode = iterator.next(); + Log.d(TAG, "** MessageNode what: " + msgNode.mMessage.what + " when " + + msgNode.mMessage.when + " seq: " + msgNode.mInsertSeq); + } + } + + private int dumpPriorityQueue(PriorityQueue<MessageNode> queue, Printer pw, String prefix, + Handler h, int n) { + int count = 0; + long now = SystemClock.uptimeMillis(); + + for (MessageNode msgNode : queue) { + Message msg = msgNode.mMessage; + if (h == null || h == msg.target) { + pw.println(prefix + "Message " + (n + count) + ": " + msg.toString(now)); + } + count++; + } + return count; + } + + void dump(Printer pw, String prefix, Handler h) { + long now = SystemClock.uptimeMillis(); + int n = 0; + + pw.println(prefix + "(MessageQueue is using SemiConcurrent implementation)"); + + StackNode node = (StackNode) sState.getVolatile(this); + while (node != null) { + if (node.isMessageNode()) { + Message msg = ((MessageNode) node).mMessage; + if (h == null || h == msg.target) { + pw.println(prefix + "Message " + n + ": " + msg.toString(now)); + } + node = ((MessageNode) node).mNext; + } else { + pw.println(prefix + "State: " + node); + node = null; + } + n++; + } + + synchronized (mPriorityQueue) { + pw.println(prefix + "PriorityQueue Messages: "); + n += dumpPriorityQueue(mPriorityQueue, pw, prefix, h, n); + pw.println(prefix + "AsyncPriorityQueue Messages: "); + n += dumpPriorityQueue(mAsyncPriorityQueue, pw, prefix, h, n); + } + + pw.println(prefix + "(Total messages: " + n + ", polling=" + isPolling() + + ", quitting=" + (boolean) sQuitting.getVolatile(this) + ")"); + } + + private int dumpPriorityQueue(PriorityQueue<MessageNode> queue, ProtoOutputStream proto) { + int count = 0; + + for (MessageNode msgNode : queue) { + Message msg = msgNode.mMessage; + msg.dumpDebug(proto, MessageQueueProto.MESSAGES); + count++; + } + return count; + } + + void dumpDebug(ProtoOutputStream proto, long fieldId) { + final long messageQueueToken = proto.start(fieldId); + + StackNode node = (StackNode) sState.getVolatile(this); + while (node.isMessageNode()) { + Message msg = ((MessageNode) node).mMessage; + msg.dumpDebug(proto, MessageQueueProto.MESSAGES); + node = ((MessageNode) node).mNext; + } + + synchronized (mPriorityQueue) { + dumpPriorityQueue(mPriorityQueue, proto); + dumpPriorityQueue(mAsyncPriorityQueue, proto); + } + + proto.write(MessageQueueProto.IS_POLLING_LOCKED, isPolling()); + proto.write(MessageQueueProto.IS_QUITTING, (boolean) sQuitting.getVolatile(this)); + proto.end(messageQueueToken); + } + + /** + * Adds a file descriptor listener to receive notification when file descriptor + * related events occur. + * <p> + * If the file descriptor has already been registered, the specified events + * and listener will replace any that were previously associated with it. + * It is not possible to set more than one listener per file descriptor. + * </p><p> + * It is important to always unregister the listener when the file descriptor + * is no longer of use. + * </p> + * + * @param fd The file descriptor for which a listener will be registered. + * @param events The set of events to receive: a combination of the + * {@link OnFileDescriptorEventListener#EVENT_INPUT}, + * {@link OnFileDescriptorEventListener#EVENT_OUTPUT}, and + * {@link OnFileDescriptorEventListener#EVENT_ERROR} event masks. If the requested + * set of events is zero, then the listener is unregistered. + * @param listener The listener to invoke when file descriptor events occur. + * + * @see OnFileDescriptorEventListener + * @see #removeOnFileDescriptorEventListener + */ + @android.ravenwood.annotation.RavenwoodThrow(blockedBy = android.os.ParcelFileDescriptor.class) + public void addOnFileDescriptorEventListener(@NonNull FileDescriptor fd, + @OnFileDescriptorEventListener.Events int events, + @NonNull OnFileDescriptorEventListener listener) { + if (fd == null) { + throw new IllegalArgumentException("fd must not be null"); + } + if (listener == null) { + throw new IllegalArgumentException("listener must not be null"); + } + + synchronized (mFileDescriptorRecordsLock) { + updateOnFileDescriptorEventListenerLocked(fd, events, listener); + } + } + + /** + * Removes a file descriptor listener. + * <p> + * This method does nothing if no listener has been registered for the + * specified file descriptor. + * </p> + * + * @param fd The file descriptor whose listener will be unregistered. + * + * @see OnFileDescriptorEventListener + * @see #addOnFileDescriptorEventListener + */ + @android.ravenwood.annotation.RavenwoodThrow(blockedBy = android.os.ParcelFileDescriptor.class) + public void removeOnFileDescriptorEventListener(@NonNull FileDescriptor fd) { + if (fd == null) { + throw new IllegalArgumentException("fd must not be null"); + } + + synchronized (mFileDescriptorRecordsLock) { + updateOnFileDescriptorEventListenerLocked(fd, 0, null); + } + } + + @android.ravenwood.annotation.RavenwoodThrow(blockedBy = android.os.ParcelFileDescriptor.class) + private void updateOnFileDescriptorEventListenerLocked(FileDescriptor fd, int events, + OnFileDescriptorEventListener listener) { + final int fdNum = fd.getInt$(); + + int index = -1; + FileDescriptorRecord record = null; + if (mFileDescriptorRecords != null) { + index = mFileDescriptorRecords.indexOfKey(fdNum); + if (index >= 0) { + record = mFileDescriptorRecords.valueAt(index); + if (record != null && record.mEvents == events) { + return; + } + } + } + + if (events != 0) { + events |= OnFileDescriptorEventListener.EVENT_ERROR; + if (record == null) { + if (mFileDescriptorRecords == null) { + mFileDescriptorRecords = new SparseArray<FileDescriptorRecord>(); + } + record = new FileDescriptorRecord(fd, events, listener); + mFileDescriptorRecords.put(fdNum, record); + } else { + record.mListener = listener; + record.mEvents = events; + record.mSeq += 1; + } + nativeSetFileDescriptorEvents(mPtr, fdNum, events); + } else if (record != null) { + record.mEvents = 0; + mFileDescriptorRecords.removeAt(index); + nativeSetFileDescriptorEvents(mPtr, fdNum, 0); + } + } + + // Called from native code. + private int dispatchEvents(int fd, int events) { + // Get the file descriptor record and any state that might change. + final FileDescriptorRecord record; + final int oldWatchedEvents; + final OnFileDescriptorEventListener listener; + final int seq; + synchronized (mFileDescriptorRecordsLock) { + record = mFileDescriptorRecords.get(fd); + if (record == null) { + return 0; // spurious, no listener registered + } + + oldWatchedEvents = record.mEvents; + events &= oldWatchedEvents; // filter events based on current watched set + if (events == 0) { + return oldWatchedEvents; // spurious, watched events changed + } + + listener = record.mListener; + seq = record.mSeq; + } + + // Invoke the listener outside of the lock. + int newWatchedEvents = listener.onFileDescriptorEvents( + record.mDescriptor, events); + if (newWatchedEvents != 0) { + newWatchedEvents |= OnFileDescriptorEventListener.EVENT_ERROR; + } + + // Update the file descriptor record if the listener changed the set of + // events to watch and the listener itself hasn't been updated since. + if (newWatchedEvents != oldWatchedEvents) { + synchronized (mFileDescriptorRecordsLock) { + int index = mFileDescriptorRecords.indexOfKey(fd); + if (index >= 0 && mFileDescriptorRecords.valueAt(index) == record + && record.mSeq == seq) { + record.mEvents = newWatchedEvents; + if (newWatchedEvents == 0) { + mFileDescriptorRecords.removeAt(index); + } + } + } + } + + // Return the new set of events to watch for native code to take care of. + return newWatchedEvents; + } + + /** + * Callback interface for discovering when a thread is going to block + * waiting for more messages. + */ + public static interface IdleHandler { + /** + * Called when the message queue has run out of messages and will now + * wait for more. Return true to keep your idle handler active, false + * to have it removed. This may be called if there are still messages + * pending in the queue, but they are all scheduled to be dispatched + * after the current time. + */ + boolean queueIdle(); + } + + /** + * A listener which is invoked when file descriptor related events occur. + */ + public interface OnFileDescriptorEventListener { + /** + * File descriptor event: Indicates that the file descriptor is ready for input + * operations, such as reading. + * <p> + * The listener should read all available data from the file descriptor + * then return <code>true</code> to keep the listener active or <code>false</code> + * to remove the listener. + * </p><p> + * In the case of a socket, this event may be generated to indicate + * that there is at least one incoming connection that the listener + * should accept. + * </p><p> + * This event will only be generated if the {@link #EVENT_INPUT} event mask was + * specified when the listener was added. + * </p> + */ + public static final int EVENT_INPUT = 1 << 0; + + /** + * File descriptor event: Indicates that the file descriptor is ready for output + * operations, such as writing. + * <p> + * The listener should write as much data as it needs. If it could not + * write everything at once, then it should return <code>true</code> to + * keep the listener active. Otherwise, it should return <code>false</code> + * to remove the listener then re-register it later when it needs to write + * something else. + * </p><p> + * This event will only be generated if the {@link #EVENT_OUTPUT} event mask was + * specified when the listener was added. + * </p> + */ + public static final int EVENT_OUTPUT = 1 << 1; + + /** + * File descriptor event: Indicates that the file descriptor encountered a + * fatal error. + * <p> + * File descriptor errors can occur for various reasons. One common error + * is when the remote peer of a socket or pipe closes its end of the connection. + * </p><p> + * This event may be generated at any time regardless of whether the + * {@link #EVENT_ERROR} event mask was specified when the listener was added. + * </p> + */ + public static final int EVENT_ERROR = 1 << 2; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag = true, prefix = { "EVENT_" }, value = { + EVENT_INPUT, + EVENT_OUTPUT, + EVENT_ERROR + }) + public @interface Events {} + + /** + * Called when a file descriptor receives events. + * + * @param fd The file descriptor. + * @param events The set of events that occurred: a combination of the + * {@link #EVENT_INPUT}, {@link #EVENT_OUTPUT}, and {@link #EVENT_ERROR} event masks. + * @return The new set of events to watch, or 0 to unregister the listener. + * + * @see #EVENT_INPUT + * @see #EVENT_OUTPUT + * @see #EVENT_ERROR + */ + @Events int onFileDescriptorEvents(@NonNull FileDescriptor fd, @Events int events); + } + + static final class FileDescriptorRecord { + public final FileDescriptor mDescriptor; + public int mEvents; + public OnFileDescriptorEventListener mListener; + public int mSeq; + + public FileDescriptorRecord(FileDescriptor descriptor, + int events, OnFileDescriptorEventListener listener) { + mDescriptor = descriptor; + mEvents = events; + mListener = listener; + } + } +} diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 57853e7ded32..94c769293bff 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -13396,7 +13396,19 @@ public final class Settings { = "enable_freeform_support"; /** - * Whether to enable experimental desktop mode on secondary displays. + * Whether to override the availability of the desktop mode on the main display of the + * device. If on, users can make move an app to the desktop, allowing a freeform windowing + * experience. + * @hide + */ + @Readable + public static final String DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES = + "override_desktop_mode_features"; + + /** + * Whether to enable the legacy freeform support on secondary displays. If enabled, the + * SECONDARY_HOME of the launcher is started on any secondary display, allowing for a + * desktop experience. * @hide */ @Readable diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java index 88bd87e6c27c..d019bad68cd5 100644 --- a/core/java/android/provider/Telephony.java +++ b/core/java/android/provider/Telephony.java @@ -4940,7 +4940,7 @@ public final class Telephony { * * @hide */ - public static final String COLUMN_IS_NTN = "is_ntn"; + public static final String COLUMN_IS_ONLY_NTN = "is_only_ntn"; /** * TelephonyProvider column name for transferred status @@ -4976,6 +4976,15 @@ public final class Telephony { public static final String COLUMN_SATELLITE_ENTITLEMENT_PLMNS = "satellite_entitlement_plmns"; + /** + * TelephonyProvider column name to indicate the satellite ESOS supported. The value of this + * column is set based on {@link CarrierConfigManager#KEY_SATELLITE_ESOS_SUPPORTED_BOOL}. + * By default, it's disabled. + * + * @hide + */ + public static final String COLUMN_SATELLITE_ESOS_SUPPORTED = "satellite_esos_supported"; + /** All columns in {@link SimInfo} table. */ private static final List<String> ALL_COLUMNS = List.of( COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID, @@ -5047,11 +5056,12 @@ public final class Telephony { COLUMN_USER_HANDLE, COLUMN_SATELLITE_ENABLED, COLUMN_SATELLITE_ATTACH_ENABLED_FOR_CARRIER, - COLUMN_IS_NTN, + COLUMN_IS_ONLY_NTN, COLUMN_SERVICE_CAPABILITIES, COLUMN_TRANSFER_STATUS, COLUMN_SATELLITE_ENTITLEMENT_STATUS, - COLUMN_SATELLITE_ENTITLEMENT_PLMNS + COLUMN_SATELLITE_ENTITLEMENT_PLMNS, + COLUMN_SATELLITE_ESOS_SUPPORTED ); /** diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java index 8ecb1fb4e9ad..2948129ae956 100644 --- a/core/java/android/service/dreams/DreamService.java +++ b/core/java/android/service/dreams/DreamService.java @@ -19,6 +19,7 @@ package android.service.dreams; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.service.dreams.Flags.dreamHandlesConfirmKeys; import static android.service.dreams.Flags.dreamHandlesBeingObscured; +import static android.service.dreams.Flags.startAndStopDozingInBackground; import android.annotation.FlaggedApi; import android.annotation.IdRes; @@ -923,9 +924,16 @@ public class DreamService extends Service implements Window.Callback { if (mDozing) { try { - mDreamManager.startDozing( + if (startAndStopDozingInBackground()) { + mDreamManager.startDozingOneway( mDreamToken, mDozeScreenState, mDozeScreenStateReason, mDozeScreenBrightness); + } else { + mDreamManager.startDozing( + mDreamToken, mDozeScreenState, mDozeScreenStateReason, + mDozeScreenBrightness); + } + } catch (RemoteException ex) { // system server died } @@ -1250,7 +1258,11 @@ public class DreamService extends Service implements Window.Callback { try { // finishSelf will unbind the dream controller from the dream service. This will // trigger DreamService.this.onDestroy and DreamService.this will die. - mDreamManager.finishSelf(mDreamToken, true /*immediate*/); + if (startAndStopDozingInBackground()) { + mDreamManager.finishSelfOneway(mDreamToken, true /*immediate*/); + } else { + mDreamManager.finishSelf(mDreamToken, true /*immediate*/); + } } catch (RemoteException ex) { // system server died } diff --git a/core/java/android/service/dreams/IDreamManager.aidl b/core/java/android/service/dreams/IDreamManager.aidl index cf98bfe05faf..620eef66959f 100644 --- a/core/java/android/service/dreams/IDreamManager.aidl +++ b/core/java/android/service/dreams/IDreamManager.aidl @@ -50,4 +50,6 @@ interface IDreamManager { void startDreamActivity(in Intent intent); @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE)") oneway void setDreamIsObscured(in boolean isObscured); + oneway void startDozingOneway(in IBinder token, int screenState, int reason, int screenBrightness); + oneway void finishSelfOneway(in IBinder token, boolean immediate); } diff --git a/core/java/android/service/dreams/flags.aconfig b/core/java/android/service/dreams/flags.aconfig index 54d950c18af8..83e0adfca3be 100644 --- a/core/java/android/service/dreams/flags.aconfig +++ b/core/java/android/service/dreams/flags.aconfig @@ -47,3 +47,13 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "start_and_stop_dozing_in_background" + namespace: "systemui" + description: "Move the start-dozing and stop-dozing operation to the background" + bug: "330287187" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/core/java/android/tracing/flags.aconfig b/core/java/android/tracing/flags.aconfig index d7389bab8d6d..be60c2504f38 100644 --- a/core/java/android/tracing/flags.aconfig +++ b/core/java/android/tracing/flags.aconfig @@ -38,3 +38,11 @@ flag { is_fixed_read_only: true bug: "323166383" } + +flag { + name: "perfetto_wm_tracing" + namespace: "windowing_tools" + description: "Migrate WindowManager tracing to Perfetto" + is_fixed_read_only: true + bug: "323165543" +} diff --git a/core/java/android/view/ImeBackAnimationController.java b/core/java/android/view/ImeBackAnimationController.java index fc1852d739e2..c7e93c19484f 100644 --- a/core/java/android/view/ImeBackAnimationController.java +++ b/core/java/android/view/ImeBackAnimationController.java @@ -27,6 +27,7 @@ import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.WindowConfiguration; import android.graphics.Insets; import android.util.Log; import android.view.animation.BackGestureInterpolator; @@ -137,9 +138,10 @@ public class ImeBackAnimationController implements OnBackAnimationCallback { @Override public void onBackInvoked() { if (!isBackAnimationAllowed() || !mIsPreCommitAnimationInProgress) { - // play regular hide animation if back-animation is not allowed or if insets control has - // been cancelled by the system (this can happen in split screen for example) - mInsetsController.hide(ime()); + // play regular hide animation if predictive back-animation is not allowed or if insets + // control has been cancelled by the system. This can happen in multi-window mode for + // example (i.e. split-screen or activity-embedding) + notifyHideIme(); return; } startPostCommitAnim(/*hideIme*/ true); @@ -209,6 +211,11 @@ public class ImeBackAnimationController implements OnBackAnimationCallback { if (triggerBack) { mInsetsController.setPredictiveBackImeHideAnimInProgress(true); notifyHideIme(); + // requesting IME as invisible during post-commit + mInsetsController.setRequestedVisibleTypes(0, ime()); + // Changes the animation state. This also notifies RootView of changed insets, which + // causes it to reset its scrollY to 0f (animated) if it was panned + mInsetsController.onAnimationStateChanged(ime(), /*running*/ true); } if (mStartRootScrollY != 0 && !triggerBack) { // This causes RootView to update its scroll back to the panned position @@ -228,12 +235,6 @@ public class ImeBackAnimationController implements OnBackAnimationCallback { // the IME away mInsetsController.getHost().getInputMethodManager() .notifyImeHidden(mInsetsController.getHost().getWindowToken(), statsToken); - - // requesting IME as invisible during post-commit - mInsetsController.setRequestedVisibleTypes(0, ime()); - // Changes the animation state. This also notifies RootView of changed insets, which causes - // it to reset its scrollY to 0f (animated) if it was panned - mInsetsController.onAnimationStateChanged(ime(), /*running*/ true); } private void reset() { @@ -254,8 +255,18 @@ public class ImeBackAnimationController implements OnBackAnimationCallback { } private boolean isBackAnimationAllowed() { - // back animation is allowed in all cases except when softInputMode is adjust_resize AND - // there is no app-registered WindowInsetsAnimationCallback AND edge-to-edge is not enabled. + + if (mViewRoot.mContext.getResources().getConfiguration().windowConfiguration + .getWindowingMode() == WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW) { + // TODO(b/346726115) enable predictive back animation in multi-window mode in + // DisplayImeController + return false; + } + + // otherwise, the predictive back animation is allowed in all cases except when + // 1. softInputMode is adjust_resize AND + // 2. there is no app-registered WindowInsetsAnimationCallback AND + // 3. edge-to-edge is not enabled. return (mViewRoot.mWindowAttributes.softInputMode & SOFT_INPUT_MASK_ADJUST) != SOFT_INPUT_ADJUST_RESIZE || (mViewRoot.mView != null && mViewRoot.mView.hasWindowInsetsAnimationCallback()) diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 47669427cb9d..766e02bf3198 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -13859,6 +13859,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, }) @ResolvedLayoutDir public int getLayoutDirection() { + final int targetSdkVersion = getContext().getApplicationInfo().targetSdkVersion; + if (targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR1) { + mPrivateFlags2 |= PFLAG2_LAYOUT_DIRECTION_RESOLVED; + return LAYOUT_DIRECTION_RESOLVED_DEFAULT; + } return ((mPrivateFlags2 & PFLAG2_LAYOUT_DIRECTION_RESOLVED_RTL) == PFLAG2_LAYOUT_DIRECTION_RESOLVED_RTL) ? LAYOUT_DIRECTION_RTL : LAYOUT_DIRECTION_LTR; } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index c0bd535f95f3..1525bd1d4af7 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -114,6 +114,7 @@ import static android.view.accessibility.Flags.fixMergedContentChangeEventV2; import static android.view.accessibility.Flags.forceInvertColor; import static android.view.accessibility.Flags.reduceWindowContentChangedEventThrottle; import static android.view.flags.Flags.addSchandleToVriSurface; +import static android.view.flags.Flags.disableDrawWakeLock; import static android.view.flags.Flags.sensitiveContentAppProtection; import static android.view.flags.Flags.sensitiveContentPrematureProtectionRemovedFix; import static android.view.flags.Flags.toolkitFrameRateFunctionEnablingReadOnly; @@ -149,6 +150,8 @@ import android.app.ResourcesManager; import android.app.WindowConfiguration; import android.app.compat.CompatChanges; import android.app.servertransaction.WindowStateTransactionItem; +import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledSince; import android.compat.annotation.UnsupportedAppUsage; import android.content.ClipData; import android.content.ClipDescription; @@ -337,6 +340,15 @@ public final class ViewRootImpl implements ViewParent, private static final int LOGTAG_VIEWROOT_DRAW_EVENT = 60004; /** + * This change disables the {@code DRAW_WAKE_LOCK}, an internal wakelock acquired per-frame + * duration display DOZE. It was added to allow animation during AOD. This wakelock consumes + * battery severely if the animation is too heavy, so, it will be removed. + */ + @ChangeId + @EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM) + private static final long DISABLE_DRAW_WAKE_LOCK = 349153669L; + + /** * Set to false if we do not want to use the multi threaded renderer even though * threaded renderer (aka hardware renderering) is used. Note that by disabling * this, WindowCallbacks will not fire. @@ -2459,6 +2471,11 @@ public final class ViewRootImpl implements ViewParent, } void pokeDrawLockIfNeeded() { + // Disable DRAW_WAKE_LOCK starting U. Otherwise, only need to acquire it for DOZE state. + if (CompatChanges.isChangeEnabled(DISABLE_DRAW_WAKE_LOCK) && disableDrawWakeLock()) { + return; + } + if (!Display.isDozeState(mAttachInfo.mDisplayState)) { // Only need to acquire wake lock for DOZE state. return; @@ -7451,6 +7468,10 @@ public final class ViewRootImpl implements ViewParent, @Override protected int onProcess(QueuedInputEvent q) { + if (q.forPreImeOnly()) { + // this event is intended for the ViewPreImeInputStage only, let's forward + return FORWARD; + } if (q.mEvent instanceof KeyEvent) { final KeyEvent keyEvent = (KeyEvent) q.mEvent; diff --git a/core/java/android/view/autofill/AutofillFeatureFlags.java b/core/java/android/view/autofill/AutofillFeatureFlags.java index 199a69a723d8..5b1c7d54ddb7 100644 --- a/core/java/android/view/autofill/AutofillFeatureFlags.java +++ b/core/java/android/view/autofill/AutofillFeatureFlags.java @@ -256,6 +256,21 @@ public class AutofillFeatureFlags { "ignore_relayout_auth_pending"; /** + * Fixes to handle apps relaying out, and causing problems for autofill. + * + * @hide + */ + public static final String DEVICE_CONFIG_ENABLE_RELAYOUT = "enable_relayout"; + + /** + * Enable relative location of views for fingerprinting for relayout. + * + * @hide + */ + public static final String DEVICE_CONFIG_ENABLE_RELATIVE_LOCATION_FOR_RELAYOUT = + "enable_relative_location_for_relayout"; + + /** * Bugfix flag, Autofill should only fill in value from current session. * * See frameworks/base/services/autofill/bugfixes.aconfig#fill_fields_from_current_session_only @@ -543,6 +558,22 @@ public class AutofillFeatureFlags { false); } + /** @hide */ + public static boolean enableRelayoutFixes() { + return DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_AUTOFILL, + DEVICE_CONFIG_ENABLE_RELAYOUT, + true); + } + + /** @hide */ + public static boolean enableRelativeLocationForRelayout() { + return DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_AUTOFILL, + DEVICE_CONFIG_ENABLE_RELATIVE_LOCATION_FOR_RELAYOUT, + false); + } + /** @hide **/ public static boolean shouldFillFieldsFromCurrentSessionOnly() { return DeviceConfig.getBoolean( diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index 0d4c5560837c..515ed0e8f6af 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -748,7 +748,16 @@ public final class AutofillManager { // Controls logic around apps changing some properties of their views when activity loses // focus due to autofill showing biometric activity, password manager, or password breach check. - private boolean mRelayoutFix; + // Deprecated. TODO: Remove it after ramp of new solution. + private boolean mRelayoutFixDeprecated; + + // Controls logic around apps changing some properties of their views when activity loses + // focus due to autofill showing biometric activity, password manager, or password breach check. + private final boolean mRelayoutFix; + + // Controls logic around apps changing some properties of their views when activity loses + // focus due to autofill showing biometric activity, password manager, or password breach check. + private final boolean mRelativePositionForRelayout; // Indicates whether the credman integration is enabled. private final boolean mIsCredmanIntegrationEnabled; @@ -978,11 +987,31 @@ public final class AutofillManager { mShouldIncludeInvisibleViewInAssistStructure = AutofillFeatureFlags.shouldIncludeInvisibleViewInAssistStructure(); - mRelayoutFix = AutofillFeatureFlags.shouldIgnoreRelayoutWhenAuthPending(); + mRelayoutFixDeprecated = AutofillFeatureFlags.shouldIgnoreRelayoutWhenAuthPending(); + mRelayoutFix = AutofillFeatureFlags.enableRelayoutFixes(); + mRelativePositionForRelayout = AutofillFeatureFlags.enableRelativeLocationForRelayout(); mIsCredmanIntegrationEnabled = Flags.autofillCredmanIntegration(); } /** + * Whether to apply relayout fixes. + * + * @hide + */ + public boolean isRelayoutFixEnabled() { + return mRelayoutFix; + } + + /** + * Whether to use relative positions and locations of the views for disambiguation. + * + * @hide + */ + public boolean isRelativePositionForRelayoutEnabled() { + return mRelativePositionForRelayout; + } + + /** * Whether to apply heuristic check on important views before triggering fill request * * @hide @@ -1779,7 +1808,7 @@ public final class AutofillManager { } return; } - if (mRelayoutFix && mState == STATE_PENDING_AUTHENTICATION) { + if (mRelayoutFixDeprecated && mState == STATE_PENDING_AUTHENTICATION) { if (sVerbose) { Log.v(TAG, "notifyViewVisibilityChanged(): ignoring in auth pending mode"); } @@ -2917,7 +2946,7 @@ public final class AutofillManager { Intent fillInIntent, boolean authenticateInline) { synchronized (mLock) { if (sessionId == mSessionId) { - if (mRelayoutFix) { + if (mRelayoutFixDeprecated) { mState = STATE_PENDING_AUTHENTICATION; } final AutofillClient client = getClient(); @@ -3778,7 +3807,7 @@ public final class AutofillManager { @GuardedBy("mLock") private boolean isPendingAuthenticationLocked() { - return mRelayoutFix && mState == STATE_PENDING_AUTHENTICATION; + return mRelayoutFixDeprecated && mState == STATE_PENDING_AUTHENTICATION; } @GuardedBy("mLock") @@ -4322,6 +4351,13 @@ public final class AutofillManager { addToSet(mInvisibleDialogTrackedIds, id); } } + } else { + if (sDebug) { + // isClientVisibleForAutofillLocked() is checking whether + // activity has stopped under the hood + Log.d(TAG, "notifyViewVisibilityChangedLocked(): ignoring " + + "view visibility change since activity has stopped"); + } } if (mIsTrackedSaveView && mVisibleTrackedIds.isEmpty()) { diff --git a/core/java/android/view/autofill/OWNERS b/core/java/android/view/autofill/OWNERS index 898947adcd1b..7f3b4e5a21b3 100644 --- a/core/java/android/view/autofill/OWNERS +++ b/core/java/android/view/autofill/OWNERS @@ -1,10 +1,11 @@ # Bug component: 351486 -simranjit@google.com haoranzhang@google.com +jiewenlei@google.com +simranjit@google.com skxu@google.com +shuc@google.com yunicorn@google.com -reemabajwa@google.com # Bug component: 543785 = per-file *Augmented* per-file *Augmented* = wangqi@google.com diff --git a/core/java/android/view/flags/view_flags.aconfig b/core/java/android/view/flags/view_flags.aconfig index 4d4e4afb621c..f570a9a50ebf 100644 --- a/core/java/android/view/flags/view_flags.aconfig +++ b/core/java/android/view/flags/view_flags.aconfig @@ -89,4 +89,12 @@ flag { metadata { purpose: PURPOSE_BUGFIX } +} + +flag { + name: "disable_draw_wake_lock" + namespace: "wear_frameworks" + description: "Disable Draw Wakelock starting U." + bug: "331698645" + is_fixed_read_only: true }
\ No newline at end of file diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java index 4fb6e698d592..9b87e2351e3f 100644 --- a/core/java/android/window/WindowOnBackInvokedDispatcher.java +++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java @@ -489,7 +489,8 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { return; } OnBackAnimationCallback animationCallback = getBackAnimationCallback(); - if (animationCallback != null) { + if (animationCallback != null + && !(callback instanceof ImeBackAnimationController)) { mProgressAnimator.onBackInvoked(callback::onBackInvoked); } else { mProgressAnimator.reset(); diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig index 8cd2a3ed124e..68e33c61f71f 100644 --- a/core/java/android/window/flags/windowing_sdk.aconfig +++ b/core/java/android/window/flags/windowing_sdk.aconfig @@ -181,3 +181,14 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + namespace: "windowing_sdk" + name: "per_user_display_window_settings" + description: "Whether to store display window settings per user to avoid conflicts" + bug: "346668297" + is_fixed_read_only: true + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/core/java/com/android/internal/protolog/common/ProtoLog.java b/core/java/com/android/internal/protolog/ProtoLog.java index 8149cd5bbd3b..0118c056d682 100644 --- a/core/java/com/android/internal/protolog/common/ProtoLog.java +++ b/core/java/com/android/internal/protolog/ProtoLog.java @@ -14,7 +14,11 @@ * limitations under the License. */ -package com.android.internal.protolog.common; +package com.android.internal.protolog; + +import com.android.internal.protolog.common.IProtoLog; +import com.android.internal.protolog.common.IProtoLogGroup; +import com.android.internal.protolog.common.LogLevel; /** * ProtoLog API - exposes static logging methods. Usage of this API is similar @@ -35,7 +39,9 @@ package com.android.internal.protolog.common; * Methods in this class are stubs, that are replaced by optimised versions by the ProtoLogTool * during build. */ +// LINT.IfChange public class ProtoLog { +// LINT.ThenChange(frameworks/base/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt) // Needs to be set directly otherwise the protologtool tries to transform the method call public static boolean REQUIRE_PROTOLOGTOOL = true; diff --git a/core/res/res/anim/overlay_task_fragment_change.xml b/core/res/res/anim/overlay_task_fragment_change.xml new file mode 100644 index 000000000000..eb02ba84d6c1 --- /dev/null +++ b/core/res/res/anim/overlay_task_fragment_change.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<set xmlns:android="http://schemas.android.com/apk/res/android" + android:showBackdrop="false"> +</set>
\ No newline at end of file diff --git a/core/res/res/anim/overlay_task_fragment_close_to_bottom.xml b/core/res/res/anim/overlay_task_fragment_close_to_bottom.xml new file mode 100644 index 000000000000..d9487cba3265 --- /dev/null +++ b/core/res/res/anim/overlay_task_fragment_close_to_bottom.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<set xmlns:android="http://schemas.android.com/apk/res/android"> + <translate + android:fromYDelta="0" + android:toYDelta="100%" + android:interpolator="@interpolator/fast_out_extra_slow_in" + android:duration="517" /> +</set>
\ No newline at end of file diff --git a/core/res/res/anim/overlay_task_fragment_close_to_left.xml b/core/res/res/anim/overlay_task_fragment_close_to_left.xml new file mode 100644 index 000000000000..3cdb77a307d4 --- /dev/null +++ b/core/res/res/anim/overlay_task_fragment_close_to_left.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<set xmlns:android="http://schemas.android.com/apk/res/android"> + <translate + android:fromXDelta="0" + android:toXDelta="-100%" + android:interpolator="@interpolator/fast_out_extra_slow_in" + android:duration="517" /> +</set>
\ No newline at end of file diff --git a/core/res/res/anim/overlay_task_fragment_close_to_right.xml b/core/res/res/anim/overlay_task_fragment_close_to_right.xml new file mode 100644 index 000000000000..37645610796e --- /dev/null +++ b/core/res/res/anim/overlay_task_fragment_close_to_right.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<set xmlns:android="http://schemas.android.com/apk/res/android"> + <translate + android:fromXDelta="0" + android:toXDelta="100%" + android:interpolator="@interpolator/fast_out_extra_slow_in" + android:duration="517" /> +</set>
\ No newline at end of file diff --git a/core/res/res/anim/overlay_task_fragment_close_to_top.xml b/core/res/res/anim/overlay_task_fragment_close_to_top.xml new file mode 100644 index 000000000000..a8bfbbdd0e78 --- /dev/null +++ b/core/res/res/anim/overlay_task_fragment_close_to_top.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<set xmlns:android="http://schemas.android.com/apk/res/android"> + <translate + android:fromYDelta="0" + android:toYDelta="-100%" + android:interpolator="@interpolator/fast_out_extra_slow_in" + android:duration="517" /> +</set>
\ No newline at end of file diff --git a/core/res/res/anim/overlay_task_fragment_open_from_bottom.xml b/core/res/res/anim/overlay_task_fragment_open_from_bottom.xml new file mode 100644 index 000000000000..1d1223f8ead3 --- /dev/null +++ b/core/res/res/anim/overlay_task_fragment_open_from_bottom.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<set xmlns:android="http://schemas.android.com/apk/res/android"> + <translate + android:fromYDelta="100%" + android:toYDelta="0" + android:interpolator="@interpolator/fast_out_extra_slow_in" + android:duration="517" /> +</set>
\ No newline at end of file diff --git a/core/res/res/anim/overlay_task_fragment_open_from_left.xml b/core/res/res/anim/overlay_task_fragment_open_from_left.xml new file mode 100644 index 000000000000..5e5e080c5fee --- /dev/null +++ b/core/res/res/anim/overlay_task_fragment_open_from_left.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<set xmlns:android="http://schemas.android.com/apk/res/android"> + <translate + android:fromXDelta="-100%" + android:toXDelta="0" + android:interpolator="@interpolator/fast_out_extra_slow_in" + android:duration="517" /> +</set>
\ No newline at end of file diff --git a/core/res/res/anim/overlay_task_fragment_open_from_right.xml b/core/res/res/anim/overlay_task_fragment_open_from_right.xml new file mode 100644 index 000000000000..5674ff3199bc --- /dev/null +++ b/core/res/res/anim/overlay_task_fragment_open_from_right.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<set xmlns:android="http://schemas.android.com/apk/res/android"> + <translate + android:fromXDelta="100%" + android:toXDelta="0" + android:interpolator="@interpolator/fast_out_extra_slow_in" + android:duration="517" /> +</set>
\ No newline at end of file diff --git a/core/res/res/anim/overlay_task_fragment_open_from_top.xml b/core/res/res/anim/overlay_task_fragment_open_from_top.xml new file mode 100644 index 000000000000..2e3dd0a61031 --- /dev/null +++ b/core/res/res/anim/overlay_task_fragment_open_from_top.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<set xmlns:android="http://schemas.android.com/apk/res/android"> + <translate + android:fromYDelta="-100%" + android:toYDelta="0" + android:interpolator="@interpolator/fast_out_extra_slow_in" + android:duration="517" /> +</set>
\ No newline at end of file diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml index e44c7272bc1e..565d584875ac 100644 --- a/core/res/res/layout/notification_template_header.xml +++ b/core/res/res/layout/notification_template_header.xml @@ -62,7 +62,7 @@ android:layout_height="match_parent" android:layout_alignParentStart="true" android:layout_centerVertical="true" - android:layout_toStartOf="@id/expand_button" + android:layout_toStartOf="@id/notification_buttons_column" android:layout_alignWithParentIfMissing="true" android:clipChildren="false" android:gravity="center_vertical" diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index fa93e763c45b..6b71f97e3f17 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -6508,17 +6508,17 @@ ul.</string> <!-- Fingerprint dangling notification title --> <string name="fingerprint_dangling_notification_title">Set up Fingerprint Unlock again</string> <!-- Fingerprint dangling notification content for only 1 fingerprint deleted --> - <string name="fingerprint_dangling_notification_msg_1"><xliff:g id="fingerprint">%s</xliff:g> wasn\'t working well and was deleted</string> + <string name="fingerprint_dangling_notification_msg_1"><xliff:g id="fingerprint">%s</xliff:g> can no longer be recognized.</string> <!-- Fingerprint dangling notification content for more than 1 fingerprints deleted --> - <string name="fingerprint_dangling_notification_msg_2"><xliff:g id="fingerprint">%1$s</xliff:g> and <xliff:g id="fingerprint">%2$s</xliff:g> weren\'t working well and were deleted</string> + <string name="fingerprint_dangling_notification_msg_2"><xliff:g id="fingerprint">%1$s</xliff:g> and <xliff:g id="fingerprint">%2$s</xliff:g> can no longer be recognized.</string> <!-- Fingerprint dangling notification content for only 1 fingerprint deleted and no fingerprint left--> - <string name="fingerprint_dangling_notification_msg_all_deleted_1"><xliff:g id="fingerprint">%s</xliff:g> wasn\'t working well and was deleted. Set it up again to unlock your phone with fingerprint.</string> + <string name="fingerprint_dangling_notification_msg_all_deleted_1"><xliff:g id="fingerprint">%s</xliff:g> can no longer be recognized. Set up Fingerprint Unlock again.</string> <!-- Fingerprint dangling notification content for more than 1 fingerprints deleted and no fingerprint left --> - <string name="fingerprint_dangling_notification_msg_all_deleted_2"><xliff:g id="fingerprint">%1$s</xliff:g> and <xliff:g id="fingerprint">%2$s</xliff:g> weren\'t working well and were deleted. Set them up again to unlock your phone with your fingerprint.</string> + <string name="fingerprint_dangling_notification_msg_all_deleted_2"><xliff:g id="fingerprint">%1$s</xliff:g> and <xliff:g id="fingerprint">%2$s</xliff:g> can no longer be recognized. Set up Fingerprint Unlock again.</string> <!-- Face dangling notification title --> <string name="face_dangling_notification_title">Set up Face Unlock again</string> <!-- Face dangling notification content --> - <string name="face_dangling_notification_msg">Your face model wasn\'t working well and was deleted. Set it up again to unlock your phone with face.</string> + <string name="face_dangling_notification_msg">Your face model can no longer be recognized. Set up Face Unlock again.</string> <!-- Biometric dangling notification "set up" action button --> <string name="biometric_dangling_notification_action_set_up">Set up</string> <!-- Biometric dangling notification "Not now" action button --> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index bdcf13c24798..7d50d227ee13 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1753,6 +1753,15 @@ <java-symbol type="anim" name="task_fragment_clear_top_close_exit" /> <java-symbol type="anim" name="task_fragment_clear_top_open_enter" /> <java-symbol type="anim" name="task_fragment_clear_top_open_exit" /> + <java-symbol type="anim" name="overlay_task_fragment_open_from_left" /> + <java-symbol type="anim" name="overlay_task_fragment_open_from_top" /> + <java-symbol type="anim" name="overlay_task_fragment_open_from_right" /> + <java-symbol type="anim" name="overlay_task_fragment_open_from_bottom" /> + <java-symbol type="anim" name="overlay_task_fragment_change" /> + <java-symbol type="anim" name="overlay_task_fragment_close_to_left" /> + <java-symbol type="anim" name="overlay_task_fragment_close_to_top" /> + <java-symbol type="anim" name="overlay_task_fragment_close_to_right" /> + <java-symbol type="anim" name="overlay_task_fragment_close_to_bottom" /> <java-symbol type="array" name="config_autoRotationTiltTolerance" /> <java-symbol type="array" name="config_longPressVibePattern" /> diff --git a/core/tests/coretests/src/android/view/ImeBackAnimationControllerTest.java b/core/tests/coretests/src/android/view/ImeBackAnimationControllerTest.java index 58e5be2b823d..4d9b591c0990 100644 --- a/core/tests/coretests/src/android/view/ImeBackAnimationControllerTest.java +++ b/core/tests/coretests/src/android/view/ImeBackAnimationControllerTest.java @@ -34,6 +34,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.testng.Assert.assertEquals; +import android.app.WindowConfiguration; import android.content.Context; import android.graphics.Insets; import android.platform.test.annotations.Presubmit; @@ -102,6 +103,8 @@ public class ImeBackAnimationControllerTest { mViewRoot.setOnContentApplyWindowInsetsListener( mock(Window.OnContentApplyWindowInsetsListener.class)); mBackAnimationController = new ImeBackAnimationController(mViewRoot, mInsetsController); + mViewRoot.mContext.getResources().getConfiguration().windowConfiguration + .setWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN); when(mWindowInsetsAnimationController.getHiddenStateInsets()).thenReturn(Insets.NONE); when(mWindowInsetsAnimationController.getShownStateInsets()).thenReturn(IME_INSETS); @@ -156,8 +159,28 @@ public class ImeBackAnimationControllerTest { mBackAnimationController.onBackProgressed(new BackEvent(100f, 0f, 0.5f, EDGE_LEFT)); // commit back gesture mBackAnimationController.onBackInvoked(); - // verify that InsetsController#hide is called - verify(mInsetsController, times(1)).hide(ime()); + // verify that InputMethodManager#notifyImeHidden is called (which is the case whenever + // getInputMethodManager is called from ImeBackAnimationController) + verify(mViewRootInsetsControllerHost, times(1)).getInputMethodManager(); + // verify that ImeBackAnimationController does not take control over IME insets + verify(mInsetsController, never()).controlWindowInsetsAnimation(anyInt(), any(), any(), + anyBoolean(), anyLong(), any(), anyInt(), anyBoolean()); + } + + @Test + public void testMultiWindowModeNotPlayingAnim() { + // setup ViewRoot with WINDOWING_MODE_MULTI_WINDOW + mViewRoot.mContext.getResources().getConfiguration().windowConfiguration.setWindowingMode( + WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW); + // start back gesture + mBackAnimationController.onBackStarted(new BackEvent(0f, 0f, 0f, EDGE_LEFT)); + // progress back gesture + mBackAnimationController.onBackProgressed(new BackEvent(100f, 0f, 0.5f, EDGE_LEFT)); + // commit back gesture + mBackAnimationController.onBackInvoked(); + // verify that InputMethodManager#notifyImeHidden is called (which is the case whenever + // getInputMethodManager is called from ImeBackAnimationController) + verify(mViewRootInsetsControllerHost, times(1)).getInputMethodManager(); // verify that ImeBackAnimationController does not take control over IME insets verify(mInsetsController, never()).controlWindowInsetsAnimation(anyInt(), any(), any(), anyBoolean(), anyLong(), any(), anyInt(), anyBoolean()); @@ -277,9 +300,9 @@ public class ImeBackAnimationControllerTest { // commit back gesture mBackAnimationController.onBackInvoked(); - - // verify that InsetsController#hide is called - verify(mInsetsController, times(1)).hide(ime()); + // verify that InputMethodManager#notifyImeHidden is called (which is the case whenever + // getInputMethodManager is called from ImeBackAnimationController) + verify(mViewRootInsetsControllerHost, times(1)).getInputMethodManager(); }); } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java index d0e49d8c403f..eb1fc23d6b00 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java @@ -20,8 +20,10 @@ import static android.content.pm.PackageManager.MATCH_ALL; import static androidx.window.extensions.embedding.DividerPresenter.getBoundsOffsetForDivider; import static androidx.window.extensions.embedding.SplitAttributesHelper.isReversedLayout; +import static androidx.window.extensions.embedding.SplitController.TAG; import static androidx.window.extensions.embedding.WindowAttributes.DIM_AREA_ON_TASK; +import android.annotation.AnimRes; import android.app.Activity; import android.app.ActivityThread; import android.app.WindowConfiguration; @@ -31,9 +33,11 @@ import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.res.Configuration; +import android.content.res.Resources; import android.graphics.Rect; import android.os.Bundle; import android.os.IBinder; +import android.util.Log; import android.util.Pair; import android.util.Size; import android.view.View; @@ -56,6 +60,7 @@ import androidx.window.extensions.layout.FoldingFeature; import androidx.window.extensions.layout.WindowLayoutComponentImpl; import androidx.window.extensions.layout.WindowLayoutInfo; +import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.window.flags.Flags; @@ -125,6 +130,16 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { static final int RESULT_EXPAND_FAILED_NO_TF_INFO = 2; /** + * The key of {@link ActivityStack} alignment relative to its parent container. + * <p> + * See {@link ContainerPosition} for possible values. + * <p> + * Note that this constants must align with the definition in WM Jetpack library. + */ + private static final String KEY_ACTIVITY_STACK_ALIGNMENT = + "androidx.window.embedding.ActivityStackAlignment"; + + /** * Result of {@link #expandSplitContainerIfNeeded(WindowContainerTransaction, SplitContainer, * Activity, Activity, Intent)} */ @@ -649,14 +664,114 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { // TODO(b/243518738): Update to resizeTaskFragment after we migrate WCT#setRelativeBounds // and WCT#setWindowingMode to take fragmentToken. resizeTaskFragmentIfRegistered(wct, container, relativeBounds); - int windowingMode = container.getTaskContainer().getWindowingModeForTaskFragment( - relativeBounds); + final TaskContainer taskContainer = container.getTaskContainer(); + final int windowingMode = taskContainer.getWindowingModeForTaskFragment(relativeBounds); updateTaskFragmentWindowingModeIfRegistered(wct, container, windowingMode); - // Always use default animation for standalone ActivityStack. - updateAnimationParams(wct, fragmentToken, TaskFragmentAnimationParams.DEFAULT); + if (container.isOverlay() && isOverlayTransitionSupported()) { + // Use the overlay transition for the overlay container if it's supported. + final TaskFragmentAnimationParams params = createOverlayAnimationParams(relativeBounds, + taskContainer.getBounds(), container); + updateAnimationParams(wct, fragmentToken, params); + } else { + // Otherwise, fallabck to use the default animation params. + updateAnimationParams(wct, fragmentToken, TaskFragmentAnimationParams.DEFAULT); + } setTaskFragmentDimOnTask(wct, fragmentToken, dimOnTask); } + private static boolean isOverlayTransitionSupported() { + return Flags.moveAnimationOptionsToChange() + && Flags.activityEmbeddingOverlayPresentationFlag(); + } + + @NonNull + private static TaskFragmentAnimationParams createOverlayAnimationParams( + @NonNull Rect relativeBounds, @NonNull Rect parentContainerBounds, + @NonNull TaskFragmentContainer container) { + if (relativeBounds.isEmpty()) { + return TaskFragmentAnimationParams.DEFAULT; + } + + final int positionFromOptions = container.getLaunchOptions() + .getInt(KEY_ACTIVITY_STACK_ALIGNMENT , -1); + final int position = positionFromOptions != -1 ? positionFromOptions + // Fallback to calculate from bounds if the info can't be retrieved from options. + : getOverlayPosition(relativeBounds, parentContainerBounds); + + return new TaskFragmentAnimationParams.Builder() + .setOpenAnimationResId(getOpenAnimationResourcesId(position)) + .setChangeAnimationResId(R.anim.overlay_task_fragment_change) + .setCloseAnimationResId(getCloseAnimationResourcesId(position)) + .build(); + } + + @VisibleForTesting + @ContainerPosition + static int getOverlayPosition( + @NonNull Rect relativeBounds, @NonNull Rect parentContainerBounds) { + final Rect relativeParentBounds = new Rect(parentContainerBounds); + relativeParentBounds.offsetTo(0, 0); + final int leftMatch = (relativeParentBounds.left == relativeBounds.left) ? 1 : 0; + final int topMatch = (relativeParentBounds.top == relativeBounds.top) ? 1 : 0; + final int rightMatch = (relativeParentBounds.right == relativeBounds.right) ? 1 : 0; + final int bottomMatch = (relativeParentBounds.bottom == relativeBounds.bottom) ? 1 : 0; + + // Flag format: {left|top|right|bottom}. Note that overlay container could be shrunk and + // centered, which makes only one of overlay container edge matches the parent container. + final int directionFlag = (leftMatch << 3) + (topMatch << 2) + (rightMatch << 1) + + bottomMatch; + + final int position = switch (directionFlag) { + // Only the left edge match or only the right edge not match: should be on the left of + // the parent container. + case 0b1000, 0b1101 -> CONTAINER_POSITION_LEFT; + // Only the top edge match or only the bottom edge not match: should be on the top of + // the parent container. + case 0b0100, 0b1110 -> CONTAINER_POSITION_TOP; + // Only the right edge match or only the left edge not match: should be on the right of + // the parent container. + case 0b0010, 0b0111 -> CONTAINER_POSITION_RIGHT; + // Only the bottom edge match or only the top edge not match: should be on the bottom of + // the parent container. + case 0b0001, 0b1011 -> CONTAINER_POSITION_BOTTOM; + default -> { + Log.w(TAG, "Unsupported position:" + Integer.toBinaryString(directionFlag) + + " fallback to treat it as right. Relative parent bounds: " + + relativeParentBounds + ", relative overlay bounds:" + relativeBounds); + yield CONTAINER_POSITION_RIGHT; + } + }; + return position; + } + + @AnimRes + private static int getOpenAnimationResourcesId(@ContainerPosition int position) { + return switch (position) { + case CONTAINER_POSITION_LEFT -> R.anim.overlay_task_fragment_open_from_left; + case CONTAINER_POSITION_TOP -> R.anim.overlay_task_fragment_open_from_top; + case CONTAINER_POSITION_RIGHT -> R.anim.overlay_task_fragment_open_from_right; + case CONTAINER_POSITION_BOTTOM -> R.anim.overlay_task_fragment_open_from_bottom; + default -> { + Log.w(TAG, "Unknown position:" + position); + yield Resources.ID_NULL; + } + }; + } + + @AnimRes + private static int getCloseAnimationResourcesId(@ContainerPosition int position) { + return switch (position) { + case CONTAINER_POSITION_LEFT -> R.anim.overlay_task_fragment_close_to_left; + case CONTAINER_POSITION_TOP -> R.anim.overlay_task_fragment_close_to_top; + case CONTAINER_POSITION_RIGHT -> R.anim.overlay_task_fragment_close_to_right; + case CONTAINER_POSITION_BOTTOM -> R.anim.overlay_task_fragment_close_to_bottom; + default -> { + Log.w(TAG, "Unknown position:" + position); + yield Resources.ID_NULL; + } + }; + } + /** * Returns the expanded bounds if the {@code relBounds} violate minimum dimension or are not * fully covered by the task bounds. Otherwise, returns {@code relBounds}. diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java index 7a0b9a0ece6b..325750243744 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java @@ -30,6 +30,11 @@ import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSpli import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitPlaceholderRuleBuilder; import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitRule; import static androidx.window.extensions.embedding.EmbeddingTestUtils.createTfContainer; +import static androidx.window.extensions.embedding.SplitPresenter.CONTAINER_POSITION_BOTTOM; +import static androidx.window.extensions.embedding.SplitPresenter.CONTAINER_POSITION_LEFT; +import static androidx.window.extensions.embedding.SplitPresenter.CONTAINER_POSITION_RIGHT; +import static androidx.window.extensions.embedding.SplitPresenter.CONTAINER_POSITION_TOP; +import static androidx.window.extensions.embedding.SplitPresenter.getOverlayPosition; import static androidx.window.extensions.embedding.SplitPresenter.sanitizeBounds; import static androidx.window.extensions.embedding.WindowAttributes.DIM_AREA_ON_TASK; @@ -666,8 +671,8 @@ public class OverlayPresentationTest { attributes.getRelativeBounds()); verify(mSplitPresenter).updateTaskFragmentWindowingModeIfRegistered(mTransaction, container, WINDOWING_MODE_MULTI_WINDOW); - verify(mSplitPresenter).updateAnimationParams(mTransaction, token, - TaskFragmentAnimationParams.DEFAULT); + verify(mSplitPresenter).updateAnimationParams(eq(mTransaction), eq(token), + any(TaskFragmentAnimationParams.class)); verify(mSplitPresenter).setTaskFragmentIsolatedNavigation(mTransaction, container, true); verify(mSplitPresenter, never()).setTaskFragmentPinned(any(), any(TaskFragmentContainer.class), anyBoolean()); @@ -691,8 +696,8 @@ public class OverlayPresentationTest { attributes.getRelativeBounds()); verify(mSplitPresenter).updateTaskFragmentWindowingModeIfRegistered(mTransaction, container, WINDOWING_MODE_MULTI_WINDOW); - verify(mSplitPresenter).updateAnimationParams(mTransaction, token, - TaskFragmentAnimationParams.DEFAULT); + verify(mSplitPresenter).updateAnimationParams(eq(mTransaction), eq(token), + any(TaskFragmentAnimationParams.class)); verify(mSplitPresenter, never()).setTaskFragmentIsolatedNavigation(any(), any(TaskFragmentContainer.class), anyBoolean()); verify(mSplitPresenter).setTaskFragmentPinned(mTransaction, container, true); @@ -870,6 +875,59 @@ public class OverlayPresentationTest { eq(overlayContainer.getTaskFragmentToken()), eq(activityToken)); } + // TODO(b/243518738): Rewrite with TestParameter. + @Test + public void testGetOverlayPosition() { + assertWithMessage("It must be position left for left overlay.") + .that(getOverlayPosition(new Rect( + TASK_BOUNDS.left, + TASK_BOUNDS.top, + TASK_BOUNDS.right / 2, + TASK_BOUNDS.bottom), TASK_BOUNDS)).isEqualTo(CONTAINER_POSITION_LEFT); + assertWithMessage("It must be position left for shrunk left overlay.") + .that(getOverlayPosition(new Rect( + TASK_BOUNDS.left, + TASK_BOUNDS.top + 20, + TASK_BOUNDS.right / 2, + TASK_BOUNDS.bottom - 20), TASK_BOUNDS)).isEqualTo(CONTAINER_POSITION_LEFT); + assertWithMessage("It must be position left for top overlay.") + .that(getOverlayPosition(new Rect( + TASK_BOUNDS.left, + TASK_BOUNDS.top, + TASK_BOUNDS.right, + TASK_BOUNDS.bottom / 2), TASK_BOUNDS)).isEqualTo(CONTAINER_POSITION_TOP); + assertWithMessage("It must be position left for shrunk top overlay.") + .that(getOverlayPosition(new Rect( + TASK_BOUNDS.left + 20, + TASK_BOUNDS.top, + TASK_BOUNDS.right - 20, + TASK_BOUNDS.bottom / 2), TASK_BOUNDS)).isEqualTo(CONTAINER_POSITION_TOP); + assertWithMessage("It must be position left for right overlay.") + .that(getOverlayPosition(new Rect( + TASK_BOUNDS.right / 2, + TASK_BOUNDS.top, + TASK_BOUNDS.right, + TASK_BOUNDS.bottom), TASK_BOUNDS)).isEqualTo(CONTAINER_POSITION_RIGHT); + assertWithMessage("It must be position left for shrunk right overlay.") + .that(getOverlayPosition(new Rect( + TASK_BOUNDS.right / 2, + TASK_BOUNDS.top + 20, + TASK_BOUNDS.right, + TASK_BOUNDS.bottom - 20), TASK_BOUNDS)).isEqualTo(CONTAINER_POSITION_RIGHT); + assertWithMessage("It must be position left for bottom overlay.") + .that(getOverlayPosition(new Rect( + TASK_BOUNDS.left, + TASK_BOUNDS.bottom / 2, + TASK_BOUNDS.right, + TASK_BOUNDS.bottom), TASK_BOUNDS)).isEqualTo(CONTAINER_POSITION_BOTTOM); + assertWithMessage("It must be position left for shrunk bottom overlay.") + .that(getOverlayPosition(new Rect( + TASK_BOUNDS.left + 20, + TASK_BOUNDS.bottom / 20, + TASK_BOUNDS.right - 20, + TASK_BOUNDS.bottom), TASK_BOUNDS)).isEqualTo(CONTAINER_POSITION_BOTTOM); + } + /** * A simplified version of {@link SplitController#createOrUpdateOverlayTaskFragmentIfNeeded} */ diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp index 25d3067a34bc..1e6824196687 100644 --- a/libs/WindowManager/Shell/Android.bp +++ b/libs/WindowManager/Shell/Android.bp @@ -88,7 +88,7 @@ genrule { ], tools: ["protologtool"], cmd: "$(location protologtool) transform-protolog-calls " + - "--protolog-class com.android.internal.protolog.common.ProtoLog " + + "--protolog-class com.android.internal.protolog.ProtoLog " + "--loggroups-class com.android.wm.shell.protolog.ShellProtoLogGroup " + "--loggroups-jar $(location :wm_shell_protolog-groups) " + "--viewer-config-file-path /system_ext/etc/wmshell.protolog.pb " + @@ -107,7 +107,7 @@ genrule { ], tools: ["protologtool"], cmd: "$(location protologtool) generate-viewer-config " + - "--protolog-class com.android.internal.protolog.common.ProtoLog " + + "--protolog-class com.android.internal.protolog.ProtoLog " + "--loggroups-class com.android.wm.shell.protolog.ShellProtoLogGroup " + "--loggroups-jar $(location :wm_shell_protolog-groups) " + "--viewer-config-type json " + @@ -124,7 +124,7 @@ genrule { ], tools: ["protologtool"], cmd: "$(location protologtool) generate-viewer-config " + - "--protolog-class com.android.internal.protolog.common.ProtoLog " + + "--protolog-class com.android.internal.protolog.ProtoLog " + "--loggroups-class com.android.wm.shell.protolog.ShellProtoLogGroup " + "--loggroups-jar $(location :wm_shell_protolog-groups) " + "--viewer-config-type proto " + diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/OWNERS b/libs/WindowManager/Shell/multivalentScreenshotTests/OWNERS new file mode 100644 index 000000000000..dc11241fb76b --- /dev/null +++ b/libs/WindowManager/Shell/multivalentScreenshotTests/OWNERS @@ -0,0 +1,4 @@ +atsjenk@google.com +liranb@google.com +madym@google.com +mpodolian@google.com
\ No newline at end of file diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/dark_portrait_bubbles_education.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/dark_portrait_bubbles_education.png Binary files differindex eb2888199ddf..027b28e7ace7 100644 --- a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/dark_portrait_bubbles_education.png +++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/dark_portrait_bubbles_education.png diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/light_portrait_bubbles_education.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/light_portrait_bubbles_education.png Binary files differindex eb2888199ddf..027b28e7ace7 100644 --- a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/light_portrait_bubbles_education.png +++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/light_portrait_bubbles_education.png diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt index 9e1440d5716b..ae60d8bc2596 100644 --- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt +++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt @@ -27,7 +27,7 @@ import android.view.WindowManager import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.internal.protolog.common.ProtoLog +import com.android.internal.protolog.ProtoLog import com.android.wm.shell.R import com.android.wm.shell.bubbles.BubblePositioner.MAX_HEIGHT import com.android.wm.shell.common.bubbles.BubbleBarLocation diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt index 327e2059557c..5e673338bad3 100644 --- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt +++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt @@ -32,7 +32,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import androidx.test.platform.app.InstrumentationRegistry import com.android.internal.logging.testing.UiEventLoggerFake -import com.android.internal.protolog.common.ProtoLog +import com.android.internal.protolog.ProtoLog import com.android.launcher3.icons.BubbleIconFactory import com.android.wm.shell.Flags import com.android.wm.shell.R diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinControllerTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinControllerTest.kt index ace2c131050c..935d12916f56 100644 --- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinControllerTest.kt +++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinControllerTest.kt @@ -27,7 +27,7 @@ import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation -import com.android.internal.protolog.common.ProtoLog +import com.android.internal.protolog.ProtoLog import com.android.wm.shell.R import com.android.wm.shell.bubbles.BubblePositioner import com.android.wm.shell.bubbles.DeviceConfig diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/DesktopModeStatus.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/DesktopModeStatus.java index 4876f327a650..92084e403846 100644 --- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/DesktopModeStatus.java +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/DesktopModeStatus.java @@ -16,9 +16,13 @@ package com.android.wm.shell.shared; +import static android.provider.Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES; + import android.annotation.NonNull; import android.content.Context; import android.os.SystemProperties; +import android.provider.Settings; +import android.util.Log; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; @@ -29,6 +33,8 @@ import com.android.window.flags.Flags; */ public class DesktopModeStatus { + private static final String TAG = "DesktopModeStatus"; + /** * Flag to indicate whether task resizing is veiled. */ @@ -98,11 +104,12 @@ public class DesktopModeStatus { "persist.wm.debug.desktop_max_task_limit", DEFAULT_MAX_TASK_LIMIT); /** - * Return {@code true} if desktop windowing is enabled. Only to be used for testing. Callers - * should use {@link #canEnterDesktopMode(Context)} to query the state of desktop windowing. + * Return {@code true} if desktop windowing flag is enabled. Only to be used for testing. + * Callers should use {@link #canEnterDesktopMode(Context)} to query the state of desktop + * windowing. */ @VisibleForTesting - public static boolean isEnabled() { + public static boolean isDesktopModeFlagEnabled() { return Flags.enableDesktopWindowingMode(); } @@ -120,7 +127,7 @@ public class DesktopModeStatus { */ public static boolean useWindowShadow(boolean isFocusedWindow) { return USE_WINDOW_SHADOWS - || (USE_WINDOW_SHADOWS_FOCUSED_WINDOW && isFocusedWindow); + || (USE_WINDOW_SHADOWS_FOCUSED_WINDOW && isFocusedWindow); } /** @@ -154,10 +161,36 @@ public class DesktopModeStatus { } /** + * Return {@code true} if desktop mode dev option should be shown on current device + */ + public static boolean canShowDesktopModeDevOption(@NonNull Context context) { + return isDeviceEligibleForDesktopMode(context) && Flags.showDesktopWindowingDevOption(); + } + + /** Returns if desktop mode dev option should be enabled if there is no user override. */ + public static boolean shouldDevOptionBeEnabledByDefault() { + return isDesktopModeFlagEnabled(); + } + + /** * Return {@code true} if desktop mode is enabled and can be entered on the current device. */ public static boolean canEnterDesktopMode(@NonNull Context context) { - return (!enforceDeviceRestrictions() || isDesktopModeSupported(context)) && isEnabled(); + if (!isDeviceEligibleForDesktopMode(context)) return false; + + // If dev option has ever been manually toggled by the user, return its value + // TODO(b/348193756) : Move the logic for DW override based on toggle overides to a common + // infrastructure and add caching for the computation + int defaultOverrideState = -1; + int toggleState = Settings.Global.getInt(context.getContentResolver(), + DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES, defaultOverrideState); + if (toggleState != defaultOverrideState) { + Log.d(TAG, "Using Desktop mode dev option overridden state"); + return toggleState != 0; + } + + // Return Desktop windowing flag value + return isDesktopModeFlagEnabled(); } /** @@ -181,4 +214,11 @@ public class DesktopModeStatus { return DESKTOP_DENSITY_OVERRIDE >= DESKTOP_DENSITY_MIN && DESKTOP_DENSITY_OVERRIDE <= DESKTOP_DENSITY_MAX; } + + /** + * Return {@code true} if desktop mode is unrestricted and is supported in the device. + */ + private static boolean isDeviceEligibleForDesktopMode(@NonNull Context context) { + return !enforceDeviceRestrictions() || isDesktopModeSupported(context); + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ProtoLogController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ProtoLogController.java index ef9bf008b294..514307fed4f9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ProtoLogController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ProtoLogController.java @@ -19,7 +19,7 @@ package com.android.wm.shell; import com.android.internal.protolog.LegacyProtoLogImpl; import com.android.internal.protolog.common.ILogger; import com.android.internal.protolog.common.IProtoLog; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.sysui.ShellCommandHandler; import com.android.wm.shell.sysui.ShellInit; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/RootDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/RootDisplayAreaOrganizer.java index 2e5448a9e8d5..b9bf136837a6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/RootDisplayAreaOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/RootDisplayAreaOrganizer.java @@ -29,7 +29,7 @@ import android.window.WindowContainerTransaction; import androidx.annotation.NonNull; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.sysui.ShellInit; import java.io.PrintWriter; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java index 5143d419597b..9f01316d5b5c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java @@ -38,7 +38,7 @@ import android.window.SystemPerformanceHinter; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.sysui.ShellInit; import java.io.PrintWriter; 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 3ded7d246499..ebdea1bba942 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java @@ -52,7 +52,7 @@ import android.window.TaskAppearedInfo; import android.window.TaskOrganizer; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.internal.util.FrameworkStatsLog; import com.android.wm.shell.common.ScreenshotUtils; import com.android.wm.shell.common.ShellExecutor; @@ -124,6 +124,15 @@ public class ShellTaskOrganizer extends TaskOrganizer implements } /** + * Limited scope callback to notify when a task is removed from the system. This signal is + * not synchronized with anything (or any transition), and should not be used in cases where + * that is necessary. + */ + public interface TaskVanishedListener { + default void onTaskVanished(RunningTaskInfo taskInfo) {} + } + + /** * Callbacks for events on a task with a locus id. */ public interface LocusIdListener { @@ -167,6 +176,9 @@ public class ShellTaskOrganizer extends TaskOrganizer implements private final ArraySet<FocusListener> mFocusListeners = new ArraySet<>(); + // Listeners that should be notified when a task is removed + private final ArraySet<TaskVanishedListener> mTaskVanishedListeners = new ArraySet<>(); + private final Object mLock = new Object(); private StartingWindowController mStartingWindow; @@ -409,7 +421,7 @@ public class ShellTaskOrganizer extends TaskOrganizer implements } /** - * Removes listener. + * Removes a locus id listener. */ public void removeLocusIdListener(LocusIdListener listener) { synchronized (mLock) { @@ -430,7 +442,7 @@ public class ShellTaskOrganizer extends TaskOrganizer implements } /** - * Removes listener. + * Removes a focus listener. */ public void removeFocusListener(FocusListener listener) { synchronized (mLock) { @@ -439,6 +451,24 @@ public class ShellTaskOrganizer extends TaskOrganizer implements } /** + * Adds a listener to be notified when a task vanishes. + */ + public void addTaskVanishedListener(TaskVanishedListener listener) { + synchronized (mLock) { + mTaskVanishedListeners.add(listener); + } + } + + /** + * Removes a task-vanished listener. + */ + public void removeTaskVanishedListener(TaskVanishedListener listener) { + synchronized (mLock) { + mTaskVanishedListeners.remove(listener); + } + } + + /** * Returns a surface which can be used to attach overlays to the home root task */ @NonNull @@ -614,6 +644,9 @@ public class ShellTaskOrganizer extends TaskOrganizer implements t.apply(); ProtoLog.v(WM_SHELL_TASK_ORG, "Removing overlay surface"); } + for (TaskVanishedListener l : mTaskVanishedListeners) { + l.onTaskVanished(taskInfo); + } 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/activityembedding/ActivityEmbeddingAnimationRunner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java index 5a42817e839b..d270d2b4ccf1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java @@ -265,7 +265,7 @@ class ActivityEmbeddingAnimationRunner { for (TransitionInfo.Change change : openingChanges) { final Animation animation = animationProvider.get(info, change, openingWholeScreenBounds); - if (animation.getDuration() == 0) { + if (shouldUseJumpCutForAnimation(animation)) { continue; } final ActivityEmbeddingAnimationAdapter adapter = createOpenCloseAnimationAdapter( @@ -290,7 +290,7 @@ class ActivityEmbeddingAnimationRunner { } final Animation animation = animationProvider.get(info, change, closingWholeScreenBounds); - if (animation.getDuration() == 0) { + if (shouldUseJumpCutForAnimation(animation)) { continue; } final ActivityEmbeddingAnimationAdapter adapter = createOpenCloseAnimationAdapter( @@ -444,8 +444,16 @@ class ActivityEmbeddingAnimationRunner { calculateParentBounds(change, boundsAnimationChange, parentBounds); // There are two animations in the array. The first one is for the start leash // (snapshot), and the second one is for the end leash (TaskFragment). - final Animation[] animations = mAnimationSpec.createChangeBoundsChangeAnimations(change, - parentBounds); + final Animation[] animations = + mAnimationSpec.createChangeBoundsChangeAnimations(info, change, parentBounds); + // Jump cut if either animation has zero for duration. + if (Flags.activityEmbeddingAnimationCustomizationFlag()) { + for (Animation animation : animations) { + if (shouldUseJumpCutForAnimation(animation)) { + return new ArrayList<>(); + } + } + } // Keep track as we might need to add background color for the animation. // Although there may be multiple change animation, record one of them is sufficient // because the background color will be added to the root leash for the whole animation. @@ -492,12 +500,19 @@ class ActivityEmbeddingAnimationRunner { // window without bounds change. animation = ActivityEmbeddingAnimationSpec.createNoopAnimation(change); } else if (TransitionUtil.isClosingType(change.getMode())) { - animation = mAnimationSpec.createChangeBoundsCloseAnimation(change, parentBounds); + animation = + mAnimationSpec.createChangeBoundsCloseAnimation(info, change, parentBounds); shouldShowBackgroundColor = false; } else { - animation = mAnimationSpec.createChangeBoundsOpenAnimation(change, parentBounds); + animation = + mAnimationSpec.createChangeBoundsOpenAnimation(info, change, parentBounds); shouldShowBackgroundColor = false; } + if (Flags.activityEmbeddingAnimationCustomizationFlag()) { + if (shouldUseJumpCutForAnimation(animation)) { + return new ArrayList<>(); + } + } adapters.add(new ActivityEmbeddingAnimationAdapter(animation, change, TransitionUtil.getRootFor(change, info))); } @@ -640,6 +655,12 @@ class ActivityEmbeddingAnimationRunner { return true; } + /** Whether or not to use jump cut based on the animation. */ + @VisibleForTesting + static boolean shouldUseJumpCutForAnimation(@NonNull Animation animation) { + return animation.getDuration() == 0; + } + /** Updates the changes to end states in {@code startTransaction} for jump cut animation. */ private void prepareForJumpCut(@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java index 8d49614b021b..f49b90d08a75 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java @@ -46,7 +46,6 @@ import com.android.window.flags.Flags; import com.android.wm.shell.shared.TransitionUtil; /** Animation spec for ActivityEmbedding transition. */ -// TODO(b/206557124): provide an easier way to customize animation class ActivityEmbeddingAnimationSpec { private static final String TAG = "ActivityEmbeddingAnimSpec"; @@ -95,8 +94,14 @@ class ActivityEmbeddingAnimationSpec { /** Animation for window that is opening in a change transition. */ @NonNull - Animation createChangeBoundsOpenAnimation(@NonNull TransitionInfo.Change change, - @NonNull Rect parentBounds) { + Animation createChangeBoundsOpenAnimation(@NonNull TransitionInfo info, + @NonNull TransitionInfo.Change change, @NonNull Rect parentBounds) { + if (Flags.activityEmbeddingAnimationCustomizationFlag()) { + final Animation customAnimation = loadCustomAnimation(info, change); + if (customAnimation != null) { + return customAnimation; + } + } // Use end bounds for opening. final Rect bounds = change.getEndAbsBounds(); final int startLeft; @@ -123,8 +128,14 @@ class ActivityEmbeddingAnimationSpec { /** Animation for window that is closing in a change transition. */ @NonNull - Animation createChangeBoundsCloseAnimation(@NonNull TransitionInfo.Change change, - @NonNull Rect parentBounds) { + Animation createChangeBoundsCloseAnimation(@NonNull TransitionInfo info, + @NonNull TransitionInfo.Change change, @NonNull Rect parentBounds) { + if (Flags.activityEmbeddingAnimationCustomizationFlag()) { + final Animation customAnimation = loadCustomAnimation(info, change); + if (customAnimation != null) { + return customAnimation; + } + } // Use start bounds for closing. final Rect bounds = change.getStartAbsBounds(); final int endTop; @@ -155,8 +166,17 @@ class ActivityEmbeddingAnimationSpec { * the second one is for the end leash. */ @NonNull - Animation[] createChangeBoundsChangeAnimations(@NonNull TransitionInfo.Change change, - @NonNull Rect parentBounds) { + Animation[] createChangeBoundsChangeAnimations(@NonNull TransitionInfo info, + @NonNull TransitionInfo.Change change, @NonNull Rect parentBounds) { + if (Flags.activityEmbeddingAnimationCustomizationFlag()) { + // TODO(b/293658614): Support more complicated animations that may need more than a noop + // animation as the start leash. + final Animation noopAnimation = createNoopAnimation(change); + final Animation customAnimation = loadCustomAnimation(info, change); + if (customAnimation != null) { + return new Animation[]{noopAnimation, customAnimation}; + } + } // Both start bounds and end bounds are in screen coordinates. We will post translate // to the local coordinates in ActivityEmbeddingAnimationAdapter#onAnimationUpdate final Rect startBounds = change.getStartAbsBounds(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java index 7041ea307b0f..ece02711070e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java @@ -59,7 +59,7 @@ import android.window.IBackAnimationRunner; import android.window.IOnBackInvokedCallback; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.internal.util.LatencyTracker; import com.android.internal.view.AppearanceRegion; import com.android.wm.shell.R; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt index c9d3dbdcae05..4f04c5c28412 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt @@ -48,7 +48,7 @@ import com.android.internal.dynamicanimation.animation.SpringForce import com.android.internal.jank.Cuj import com.android.internal.policy.ScreenDecorationsUtils import com.android.internal.policy.SystemBarUtils -import com.android.internal.protolog.common.ProtoLog +import com.android.internal.protolog.ProtoLog import com.android.wm.shell.R import com.android.wm.shell.RootTaskDisplayAreaOrganizer import com.android.wm.shell.animation.Interpolators diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java index 381914a58cf2..103a65422504 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java @@ -49,7 +49,7 @@ import android.window.IOnBackInvokedCallback; import com.android.internal.policy.ScreenDecorationsUtils; import com.android.internal.policy.SystemBarUtils; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.R; import com.android.wm.shell.animation.Interpolators; import com.android.wm.shell.shared.annotations.ShellMainThread; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomCrossActivityBackAnimation.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomCrossActivityBackAnimation.kt index c738ce542f8a..e266e2cd7eea 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomCrossActivityBackAnimation.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomCrossActivityBackAnimation.kt @@ -28,7 +28,7 @@ import android.window.BackMotionEvent import android.window.BackNavigationInfo import com.android.internal.R import com.android.internal.policy.TransitionAnimation -import com.android.internal.protolog.common.ProtoLog +import com.android.internal.protolog.ProtoLog import com.android.wm.shell.RootTaskDisplayAreaOrganizer import com.android.wm.shell.protolog.ShellProtoLogGroup import com.android.wm.shell.shared.annotations.ShellMainThread diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java index 2aefc64a3ebb..7dbbb04e4406 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java @@ -48,7 +48,7 @@ import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.InstanceId; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.launcher3.icons.BubbleIconFactory; import com.android.wm.shell.bubbles.bar.BubbleBarExpandedView; import com.android.wm.shell.bubbles.bar.BubbleBarLayerView; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java index c853301519e9..e36f6e6c04c5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java @@ -84,7 +84,7 @@ import androidx.annotation.MainThread; import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.util.CollectionUtils; import com.android.launcher3.icons.BubbleIconFactory; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java index 761e02598460..4e6c517b9194 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java @@ -35,7 +35,7 @@ import android.view.View; import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.internal.util.FrameworkStatsLog; import com.android.wm.shell.R; import com.android.wm.shell.bubbles.Bubbles.DismissReason; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java index c7ccd50af550..f7a5c271a729 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java @@ -67,7 +67,7 @@ import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.policy.ScreenDecorationsUtils; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.Flags; import com.android.wm.shell.R; import com.android.wm.shell.common.AlphaOptimizedButton; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java index 18e04d14c71b..bf98ef82b475 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java @@ -42,7 +42,7 @@ import androidx.annotation.Nullable; import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.RecyclerView; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.internal.util.ContrastColorUtil; import com.android.wm.shell.Flags; import com.android.wm.shell.R; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java index 2382545ab324..0cf187bd9c0f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java @@ -29,7 +29,7 @@ import android.view.WindowManager; import androidx.annotation.VisibleForTesting; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.launcher3.icons.IconNormalizer; import com.android.wm.shell.R; import com.android.wm.shell.common.bubbles.BubbleBarLocation; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java index 09bec8c37b9a..f93f19d5d1eb 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java @@ -78,7 +78,7 @@ import androidx.dynamicanimation.animation.SpringForce; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.policy.ScreenDecorationsUtils; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.internal.util.FrameworkStatsLog; import com.android.wm.shell.Flags; import com.android.wm.shell.R; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java index 0b66bcb6930e..c79d9c4942bf 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java @@ -35,7 +35,7 @@ import android.view.ViewGroup; import androidx.annotation.Nullable; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.taskview.TaskView; /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesNavBarGestureTracker.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesNavBarGestureTracker.java index 137568458e3c..9429c9e71b3b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesNavBarGestureTracker.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesNavBarGestureTracker.java @@ -29,7 +29,7 @@ import android.view.InputMonitor; import androidx.annotation.Nullable; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.bubbles.BubblesNavBarMotionEventHandler.MotionEventListener; /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandler.java index b7107f09b17f..d4f53ab353ea 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandler.java @@ -28,7 +28,7 @@ import android.view.ViewConfiguration; import androidx.annotation.Nullable; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; /** * Handles {@link MotionEvent}s for bubbles that begin in the nav bar area diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedViewAnimationControllerImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedViewAnimationControllerImpl.java index aa4129a14dbc..fbef6b5e4a99 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedViewAnimationControllerImpl.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedViewAnimationControllerImpl.java @@ -38,7 +38,7 @@ import androidx.dynamicanimation.animation.FloatPropertyCompat; import androidx.dynamicanimation.animation.SpringAnimation; import androidx.dynamicanimation.animation.SpringForce; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.animation.FlingAnimationUtils; import com.android.wm.shell.animation.Interpolators; import com.android.wm.shell.bubbles.BubbleExpandedView; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java index e261d92bda5c..f7923924789e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java @@ -28,7 +28,7 @@ import android.window.WindowContainerTransaction; import android.window.WindowContainerTransactionCallback; import android.window.WindowOrganizer; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.transition.LegacyTransitions; import java.util.ArrayList; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TabletopModeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TabletopModeController.java index 43c92cab6a68..43f9cb984322 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TabletopModeController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TabletopModeController.java @@ -32,7 +32,7 @@ import android.util.ArraySet; import android.view.Surface; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.shared.annotations.ShellMainThread; import com.android.wm.shell.sysui.ShellInit; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsAlgorithm.java index 58007b50350b..8e026f04ac31 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsAlgorithm.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsAlgorithm.java @@ -27,7 +27,7 @@ import android.util.DisplayMetrics; import android.util.Size; import android.view.Gravity; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.R; import com.android.wm.shell.protolog.ShellProtoLogGroup; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java index 7ceaaea3962f..64a1b0c804da 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java @@ -32,7 +32,7 @@ import android.util.ArraySet; import android.util.Size; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.internal.util.function.TriConsumer; import com.android.wm.shell.R; import com.android.wm.shell.common.DisplayLayout; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipPerfHintController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipPerfHintController.java index c421dec025f2..b9c698e5d8b4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipPerfHintController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipPerfHintController.java @@ -26,7 +26,7 @@ import android.window.SystemPerformanceHinter.HighPerfSession; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.shared.annotations.ShellMainThread; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt index 3e9366fd6459..dcf84d927ad3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt @@ -30,7 +30,7 @@ import android.util.Log import android.util.Pair import android.util.TypedValue import android.window.TaskSnapshot -import com.android.internal.protolog.common.ProtoLog +import com.android.internal.protolog.ProtoLog import com.android.wm.shell.Flags import com.android.wm.shell.protolog.ShellProtoLogGroup import kotlin.math.abs diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java index 2234041b8c9d..3ad60e7031e5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java @@ -54,7 +54,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.R; import com.android.wm.shell.animation.Interpolators; import com.android.wm.shell.protolog.ShellProtoLogGroup; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java index 8ced76fd23af..d3c349f9c866 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java @@ -59,7 +59,7 @@ import android.window.WindowContainerTransaction; import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.R; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.animation.Interpolators; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java index 43cdcca19c3d..4ea41d5256f9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java @@ -646,6 +646,7 @@ public abstract class WMShellModule { ShellInit shellInit, ShellController shellController, ShellCommandHandler shellCommandHandler, + ShellTaskOrganizer shellTaskOrganizer, DisplayController displayController, UiEventLogger uiEventLogger, IconProvider iconProvider, @@ -653,8 +654,8 @@ public abstract class WMShellModule { Transitions transitions, @ShellMainThread ShellExecutor mainExecutor) { return new DragAndDropController(context, shellInit, shellController, shellCommandHandler, - displayController, uiEventLogger, iconProvider, globalDragListener, transitions, - mainExecutor); + shellTaskOrganizer, displayController, uiEventLogger, iconProvider, + globalDragListener, transitions, mainExecutor); } // diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt index e71056043d5c..a67dee3a4a8d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt @@ -35,7 +35,7 @@ import androidx.core.util.plus import androidx.core.util.putAll import com.android.internal.logging.InstanceId import com.android.internal.logging.InstanceIdSequence -import com.android.internal.protolog.common.ProtoLog +import com.android.internal.protolog.ProtoLog import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.EnterReason import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ExitReason import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.TaskUpdate diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java index c374eb8e8f03..b3c3a3dcf272 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java @@ -59,9 +59,10 @@ import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import com.android.internal.logging.UiEventLogger; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.launcher3.icons.IconProvider; import com.android.wm.shell.R; +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; @@ -85,6 +86,7 @@ import java.util.function.Function; public class DragAndDropController implements RemoteCallable<DragAndDropController>, GlobalDragListener.GlobalDragListenerCallback, DisplayController.OnDisplaysChangedListener, + ShellTaskOrganizer.TaskVanishedListener, View.OnDragListener, ComponentCallbacks2 { private static final String TAG = DragAndDropController.class.getSimpleName(); @@ -92,6 +94,7 @@ public class DragAndDropController implements RemoteCallable<DragAndDropControll private final Context mContext; private final ShellController mShellController; private final ShellCommandHandler mShellCommandHandler; + private final ShellTaskOrganizer mShellTaskOrganizer; private final DisplayController mDisplayController; private final DragAndDropEventLogger mLogger; private final IconProvider mIconProvider; @@ -133,6 +136,7 @@ public class DragAndDropController implements RemoteCallable<DragAndDropControll ShellInit shellInit, ShellController shellController, ShellCommandHandler shellCommandHandler, + ShellTaskOrganizer shellTaskOrganizer, DisplayController displayController, UiEventLogger uiEventLogger, IconProvider iconProvider, @@ -142,6 +146,7 @@ public class DragAndDropController implements RemoteCallable<DragAndDropControll mContext = context; mShellController = shellController; mShellCommandHandler = shellCommandHandler; + mShellTaskOrganizer = shellTaskOrganizer; mDisplayController = displayController; mLogger = new DragAndDropEventLogger(uiEventLogger); mIconProvider = iconProvider; @@ -163,6 +168,7 @@ public class DragAndDropController implements RemoteCallable<DragAndDropControll }, 0); mShellController.addExternalInterface(KEY_EXTRA_SHELL_DRAG_AND_DROP, this::createExternalInterface, this); + mShellTaskOrganizer.addTaskVanishedListener(this); mShellCommandHandler.addDumpCallback(this::dump, this); mGlobalDragListener.setListener(this); } @@ -281,6 +287,34 @@ public class DragAndDropController implements RemoteCallable<DragAndDropControll } @Override + public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) { + if (taskInfo.baseIntent == null) { + // Invalid info + return; + } + // Find the active drag + PerDisplay pd = null; + for (int i = 0; i < mDisplayDropTargets.size(); i++) { + final PerDisplay iPd = mDisplayDropTargets.valueAt(i); + if (iPd.isHandlingDrag) { + pd = iPd; + break; + } + } + if (pd == null || !pd.isHandlingDrag) { + // Not currently dragging + return; + } + + // Update the drag session + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, + "Handling vanished task: id=%d component=%s", taskInfo.taskId, + taskInfo.baseIntent.getComponent()); + pd.dragSession.updateRunningTask(); + pd.dragLayout.updateSession(pd.dragSession); + } + + @Override public boolean onDrag(View target, DragEvent event) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "Drag event: action=%s x=%f y=%f xOffset=%f yOffset=%f", @@ -313,11 +347,10 @@ public class DragAndDropController implements RemoteCallable<DragAndDropControll Slog.w(TAG, "Unexpected drag start during an active drag"); return false; } - // TODO(b/290391688): Also update the session data with task stack changes pd.dragSession = new DragSession(ActivityTaskManager.getInstance(), mDisplayController.getDisplayLayout(displayId), event.getClipData(), event.getDragFlags()); - pd.dragSession.update(); + pd.dragSession.initialize(); pd.activeDragCount++; pd.dragLayout.prepare(pd.dragSession, mLogger.logStart(pd.dragSession)); setDropTargetWindowVisibility(pd, View.VISIBLE); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java index a42ca1905ee7..9c7476d1a1b0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java @@ -66,7 +66,7 @@ import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import com.android.internal.logging.InstanceId; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.R; import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition; import com.android.wm.shell.protolog.ShellProtoLogGroup; @@ -84,7 +84,10 @@ public class DragAndDropPolicy { private static final String TAG = DragAndDropPolicy.class.getSimpleName(); private final Context mContext; - private final Starter mStarter; + // Used only for launching a fullscreen task (or as a fallback if there is no split starter) + private final Starter mFullscreenStarter; + // Used for launching tasks into splitscreen + private final Starter mSplitscreenStarter; private final SplitScreenController mSplitScreen; private final ArrayList<DragAndDropPolicy.Target> mTargets = new ArrayList<>(); private final RectF mDisallowHitRegion = new RectF(); @@ -97,10 +100,12 @@ public class DragAndDropPolicy { } @VisibleForTesting - DragAndDropPolicy(Context context, SplitScreenController splitScreen, Starter starter) { + DragAndDropPolicy(Context context, SplitScreenController splitScreen, + Starter fullscreenStarter) { mContext = context; mSplitScreen = splitScreen; - mStarter = mSplitScreen != null ? mSplitScreen : starter; + mFullscreenStarter = fullscreenStarter; + mSplitscreenStarter = splitScreen; } /** @@ -245,17 +250,20 @@ public class DragAndDropPolicy { mSplitScreen.onDroppedToSplit(position, mLoggerSessionId); } + final Starter starter = target.type == TYPE_FULLSCREEN + ? mFullscreenStarter + : mSplitscreenStarter; if (mSession.appData != null) { - launchApp(mSession, position); + launchApp(mSession, starter, position); } else { - launchIntent(mSession, position); + launchIntent(mSession, starter, position); } } /** * Launches an app provided by SysUI. */ - private void launchApp(DragSession session, @SplitPosition int position) { + private void launchApp(DragSession session, Starter starter, @SplitPosition int position) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "Launching app data at position=%d", position); final ClipDescription description = session.getClipDescription(); @@ -275,11 +283,11 @@ public class DragAndDropPolicy { if (isTask) { final int taskId = session.appData.getIntExtra(EXTRA_TASK_ID, INVALID_TASK_ID); - mStarter.startTask(taskId, position, opts); + starter.startTask(taskId, position, opts); } else if (isShortcut) { final String packageName = session.appData.getStringExtra(EXTRA_PACKAGE_NAME); final String id = session.appData.getStringExtra(EXTRA_SHORTCUT_ID); - mStarter.startShortcut(packageName, id, position, opts, user); + starter.startShortcut(packageName, id, position, opts, user); } else { final PendingIntent launchIntent = session.appData.getParcelableExtra(EXTRA_PENDING_INTENT); @@ -288,7 +296,7 @@ public class DragAndDropPolicy { Log.e(TAG, "Expected app intent's EXTRA_USER to match pending intent user"); } } - mStarter.startIntent(launchIntent, user.getIdentifier(), null /* fillIntent */, + starter.startIntent(launchIntent, user.getIdentifier(), null /* fillIntent */, position, opts); } } @@ -296,7 +304,7 @@ public class DragAndDropPolicy { /** * Launches an intent sender provided by an application. */ - private void launchIntent(DragSession session, @SplitPosition int position) { + private void launchIntent(DragSession session, Starter starter, @SplitPosition int position) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "Launching intent at position=%d", position); final ActivityOptions baseActivityOpts = ActivityOptions.makeBasic(); @@ -309,7 +317,7 @@ public class DragAndDropPolicy { | FLAG_ACTIVITY_MULTIPLE_TASK); final Bundle opts = baseActivityOpts.toBundle(); - mStarter.startIntent(session.launchableIntent, + starter.startIntent(session.launchableIntent, session.launchableIntent.getCreatorUserHandle().getIdentifier(), null /* fillIntent */, position, opts); } @@ -420,7 +428,7 @@ public class DragAndDropPolicy { @Override public String toString() { - return "Target {hit=" + hitRegion + " draw=" + drawRegion + "}"; + return "Target {type=" + type + " hit=" + hitRegion + " draw=" + drawRegion + "}"; } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java index 4bb10dfdf8c6..910175ef3023 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java @@ -20,7 +20,6 @@ import static android.app.StatusBarManager.DISABLE_NONE; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.content.pm.ActivityInfo.CONFIG_ASSETS_PATHS; import static android.content.pm.ActivityInfo.CONFIG_UI_MODE; -import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION; @@ -42,6 +41,7 @@ import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Insets; +import android.graphics.Point; import android.graphics.Rect; import android.graphics.Region; import android.graphics.drawable.Drawable; @@ -55,7 +55,7 @@ import android.widget.LinearLayout; import androidx.annotation.NonNull; import com.android.internal.logging.InstanceId; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.launcher3.icons.IconProvider; import com.android.wm.shell.R; import com.android.wm.shell.animation.Interpolators; @@ -102,6 +102,8 @@ public class DragLayout extends LinearLayout private boolean mIsShowing; private boolean mHasDropped; private DragSession mSession; + // The last position that was handled by the drag layout + private final Point mLastPosition = new Point(); @SuppressLint("WrongConstant") public DragLayout(Context context, SplitScreenController splitScreenController, @@ -265,6 +267,15 @@ public class DragLayout extends LinearLayout */ public void prepare(DragSession session, InstanceId loggerSessionId) { mPolicy.start(session, loggerSessionId); + updateSession(session); + } + + /** + * Updates the drag layout based on the diven drag session. + */ + public void updateSession(DragSession session) { + // Note: The policy currently just keeps a reference to the session + boolean updatingExistingSession = mSession != null; mSession = session; mHasDropped = false; mCurrentTarget = null; @@ -312,6 +323,11 @@ public class DragLayout extends LinearLayout updateDropZoneSizes(topOrLeftBounds, bottomOrRightBounds); } requestLayout(); + if (updatingExistingSession) { + // Update targets if we are already currently dragging + recomputeDropTargets(); + update(mLastPosition.x, mLastPosition.y); + } } private void updateDropZoneSizesForSingleTask() { @@ -359,6 +375,9 @@ public class DragLayout extends LinearLayout mDropZoneView2.setLayoutParams(dropZoneView2); } + /** + * Shows the drag layout. + */ public void show() { mIsShowing = true; recomputeDropTargets(); @@ -384,13 +403,19 @@ public class DragLayout extends LinearLayout * Updates the visible drop target as the user drags. */ public void update(DragEvent event) { + update((int) event.getX(), (int) event.getY()); + } + + /** + * Updates the visible drop target as the user drags to the given coordinates. + */ + private void update(int x, int y) { if (mHasDropped) { return; } // Find containing region, if the same as mCurrentRegion, then skip, otherwise, animate the // visibility of the current region - DragAndDropPolicy.Target target = mPolicy.getTargetAtLocation( - (int) event.getX(), (int) event.getY()); + DragAndDropPolicy.Target target = mPolicy.getTargetAtLocation(x, y); if (mCurrentTarget != target) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "Current target: %s", target); if (target == null) { @@ -429,6 +454,7 @@ public class DragLayout extends LinearLayout } mCurrentTarget = target; } + mLastPosition.set(x, y); } /** @@ -436,6 +462,7 @@ public class DragLayout extends LinearLayout */ public void hide(DragEvent event, Runnable hideCompleteCallback) { mIsShowing = false; + mLastPosition.set(-1, -1); animateSplitContainers(false, () -> { if (hideCompleteCallback != null) { hideCompleteCallback.run(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragSession.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragSession.java index 0addd432aff0..3bedef21184e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragSession.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragSession.java @@ -30,7 +30,9 @@ import android.content.pm.ActivityInfo; import androidx.annotation.Nullable; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.common.DisplayLayout; +import com.android.wm.shell.protolog.ShellProtoLogGroup; import java.util.List; @@ -79,17 +81,27 @@ public class DragSession { } /** - * Updates the session data based on the current state of the system. + * Updates the running task for this drag session. */ - void update() { - List<ActivityManager.RunningTaskInfo> tasks = + void updateRunningTask() { + final List<ActivityManager.RunningTaskInfo> tasks = mActivityTaskManager.getTasks(1, false /* filterOnlyVisibleRecents */); if (!tasks.isEmpty()) { final ActivityManager.RunningTaskInfo task = tasks.get(0); runningTaskInfo = task; runningTaskWinMode = task.getWindowingMode(); runningTaskActType = task.getActivityType(); + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, + "Running task: id=%d component=%s", task.taskId, + task.baseIntent != null ? task.baseIntent.getComponent() : "null"); } + } + + /** + * Updates the session data based on the current state of the system at the start of the drag. + */ + void initialize() { + updateRunningTask(); activityInfo = mInitialDragData.getItemAt(0).getActivityInfo(); // TODO: This should technically check & respect config_supportsNonResizableMultiWindow diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java index 724a130ef52d..b6b49a87484e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java @@ -19,6 +19,7 @@ package com.android.wm.shell.draganddrop; import static com.android.wm.shell.animation.Interpolators.FAST_OUT_SLOW_IN; import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.content.Context; import android.graphics.Canvas; @@ -37,13 +38,16 @@ import android.widget.ImageView; import androidx.annotation.Nullable; import com.android.internal.policy.ScreenDecorationsUtils; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.R; +import com.android.wm.shell.protolog.ShellProtoLogGroup; /** * Renders a drop zone area for items being dragged. */ public class DropZoneView extends FrameLayout { + private static final boolean DEBUG_LAYOUT = false; private static final float SPLASHSCREEN_ALPHA = 0.90f; private static final float HIGHLIGHT_ALPHA = 1f; private static final int MARGIN_ANIMATION_ENTER_DURATION = 400; @@ -77,6 +81,7 @@ public class DropZoneView extends FrameLayout { private int mHighlightColor; private ObjectAnimator mBackgroundAnimator; + private int mTargetBackgroundColor; private ObjectAnimator mMarginAnimator; private float mMarginPercent; @@ -181,6 +186,9 @@ public class DropZoneView extends FrameLayout { /** Animates between highlight and splashscreen depending on current state. */ public void animateSwitch() { + if (DEBUG_LAYOUT) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "animateSwitch"); + } mShowingHighlight = !mShowingHighlight; mShowingSplash = !mShowingHighlight; final int newColor = mShowingHighlight ? mHighlightColor : mSplashScreenColor; @@ -190,6 +198,10 @@ public class DropZoneView extends FrameLayout { /** Animates the highlight indicating the zone is hovered on or not. */ public void setShowingHighlight(boolean showingHighlight) { + if (DEBUG_LAYOUT) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "setShowingHighlight: showing=%b", + showingHighlight); + } mShowingHighlight = showingHighlight; mShowingSplash = !mShowingHighlight; final int newColor = mShowingHighlight ? mHighlightColor : mSplashScreenColor; @@ -199,6 +211,10 @@ public class DropZoneView extends FrameLayout { /** Animates the margins around the drop zone to show or hide. */ public void setShowingMargin(boolean visible) { + if (DEBUG_LAYOUT) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "setShowingMargin: visible=%b", + visible); + } if (mShowingMargin != visible) { mShowingMargin = visible; animateMarginToState(); @@ -212,6 +228,15 @@ public class DropZoneView extends FrameLayout { } private void animateBackground(int startColor, int endColor) { + if (DEBUG_LAYOUT) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, + "animateBackground: start=%s end=%s", + Integer.toHexString(startColor), Integer.toHexString(endColor)); + } + if (endColor == mTargetBackgroundColor) { + // Already at, or animating to, that background color + return; + } if (mBackgroundAnimator != null) { mBackgroundAnimator.cancel(); } @@ -223,6 +248,7 @@ public class DropZoneView extends FrameLayout { mBackgroundAnimator.setInterpolator(FAST_OUT_SLOW_IN); } mBackgroundAnimator.start(); + mTargetBackgroundColor = endColor; } private void animateSplashScreenIcon() { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/GlobalDragListener.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/GlobalDragListener.kt index 31214eba8dd0..ffcfe6447e2d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/GlobalDragListener.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/GlobalDragListener.kt @@ -25,7 +25,7 @@ import android.view.IWindowManager import android.window.IGlobalDragListener import android.window.IUnhandledDragCallback import androidx.annotation.VisibleForTesting -import com.android.internal.protolog.common.ProtoLog +import com.android.internal.protolog.ProtoLog import com.android.wm.shell.common.ShellExecutor import com.android.wm.shell.protolog.ShellProtoLogGroup import java.util.function.Consumer diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java index b48aee5ccd5e..1641668a98a1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java @@ -25,7 +25,7 @@ import android.content.Context; import android.util.SparseArray; import android.view.SurfaceControl; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.desktopmode.DesktopModeTaskRepository; import com.android.wm.shell.protolog.ShellProtoLogGroup; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java index 2626e7380163..d2ceb67030fc 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java @@ -27,7 +27,7 @@ import android.view.SurfaceControl; import androidx.annotation.NonNull; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.protolog.ShellProtoLogGroup; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java index cd478e5bd567..333c75f92ffc 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java @@ -49,7 +49,7 @@ import android.window.TransitionRequestInfo; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.TaskStackListenerCallback; import com.android.wm.shell.common.TaskStackListenerImpl; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java index 0a3c15b6057f..dc449d1aaf74 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java @@ -37,7 +37,7 @@ import android.window.TaskSnapshot; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.graphics.SfVsyncFrameCallbackProvider; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.launcher3.icons.IconProvider; import com.android.wm.shell.animation.Interpolators; import com.android.wm.shell.common.pip.PipUtils; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index 3fae37014fba..789f7068c0a9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -73,7 +73,7 @@ import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.R; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.animation.Interpolators; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java index f3a8fbf85754..e5633de2a3a2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java @@ -63,7 +63,7 @@ import android.window.WindowContainerTransaction; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.R; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.pip.PipBoundsAlgorithm; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java index a7c47f92eb14..ccbe94c4c780 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java @@ -41,7 +41,7 @@ import android.window.WindowContainerTransaction; import androidx.annotation.NonNull; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.pip.PipBoundsAlgorithm; import com.android.wm.shell.common.pip.PipBoundsState; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java index 0169e8c40f45..c7369a3cd347 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java @@ -32,7 +32,7 @@ import android.view.View; import android.view.ViewRootImpl; import android.view.WindowManagerGlobal; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SystemWindows; import com.android.wm.shell.common.pip.PipBoundsState; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java index 448d4f527d16..d1d82755d12c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java @@ -59,7 +59,7 @@ import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.jank.InteractionJankMonitor; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.R; import com.android.wm.shell.WindowManagerShellWrapper; import com.android.wm.shell.common.DisplayChangeController; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipInputConsumer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipInputConsumer.java index f6cab485fa2a..d1978c30eecb 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipInputConsumer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipInputConsumer.java @@ -28,7 +28,7 @@ import android.view.IWindowManager; import android.view.InputChannel; import android.view.InputEvent; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.protolog.ShellProtoLogGroup; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java index 15342be0e8b7..c18964240f98 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java @@ -59,7 +59,7 @@ import android.view.accessibility.AccessibilityNodeInfo; import android.widget.FrameLayout; import android.widget.LinearLayout; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.R; import com.android.wm.shell.animation.Interpolators; import com.android.wm.shell.common.ShellExecutor; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java index f5bd006b4621..df3803d54d9d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java @@ -34,7 +34,7 @@ import android.graphics.PointF; import android.graphics.Rect; import android.os.Debug; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.R; import com.android.wm.shell.animation.FloatProperties; import com.android.wm.shell.common.FloatingContentCoordinator; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java index d8ac8e948a97..9c4e723efc23 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java @@ -48,7 +48,7 @@ import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityWindowInfo; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.R; import com.android.wm.shell.common.FloatingContentCoordinator; import com.android.wm.shell.common.ShellExecutor; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchState.java index 5d858fa9aa3f..cb82db630715 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchState.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchState.java @@ -23,7 +23,7 @@ import android.view.VelocityTracker; import android.view.ViewConfiguration; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.protolog.ShellProtoLogGroup; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipActionsProvider.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipActionsProvider.java index 6b890c49b713..50d22ad00b11 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipActionsProvider.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipActionsProvider.java @@ -33,7 +33,7 @@ import android.app.RemoteAction; import android.content.Context; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.R; import com.android.wm.shell.common.pip.PipMediaController; import com.android.wm.shell.common.pip.PipUtils; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBackgroundView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBackgroundView.java index 0221db836dda..eb7a10cc9dfb 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBackgroundView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBackgroundView.java @@ -28,7 +28,7 @@ import android.widget.FrameLayout; import androidx.annotation.NonNull; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.R; import com.android.wm.shell.protolog.ShellProtoLogGroup; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java index 72c0cd71f198..188c35ff3562 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java @@ -33,7 +33,7 @@ import android.view.Gravity; import androidx.annotation.NonNull; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.R; import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.pip.PipBoundsAlgorithm; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsController.java index 8a215b4b2e25..1afb470c5e9b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsController.java @@ -26,7 +26,7 @@ import android.graphics.Rect; import android.os.Handler; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.R; import com.android.wm.shell.pip.tv.TvPipKeepClearAlgorithm.Placement; import com.android.wm.shell.protolog.ShellProtoLogGroup; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java index b6a7c56527bd..0ed5079b7fba 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java @@ -39,7 +39,7 @@ import android.view.Gravity; import androidx.annotation.NonNull; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.R; import com.android.wm.shell.WindowManagerShellWrapper; import com.android.wm.shell.common.DisplayController; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipCustomAction.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipCustomAction.java index 977aad4a898a..327ceef00e6a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipCustomAction.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipCustomAction.java @@ -27,7 +27,7 @@ import android.content.Context; import android.os.Bundle; import android.os.Handler; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.common.TvWindowMenuActionButton; import com.android.wm.shell.protolog.ShellProtoLogGroup; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java index 6b5bdd2299e1..e74870d4d139 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java @@ -37,7 +37,7 @@ import android.window.SurfaceSyncGroup; import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.R; import com.android.wm.shell.common.SystemWindows; import com.android.wm.shell.common.pip.PipMenuController; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuEduTextDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuEduTextDrawer.java index adc03cf5c4d7..eabf1b0b3063 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuEduTextDrawer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuEduTextDrawer.java @@ -39,7 +39,7 @@ import android.widget.TextView; import androidx.annotation.NonNull; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.R; import java.util.Arrays; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java index 4a767ef2a113..c7704f0b4eed 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java @@ -50,7 +50,7 @@ import android.widget.ImageView; import androidx.annotation.NonNull; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.internal.widget.LinearLayoutManager; import com.android.internal.widget.RecyclerView; import com.android.wm.shell.R; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java index 54e162bba2f3..ce5079227b61 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java @@ -33,7 +33,7 @@ import android.os.Bundle; import android.text.TextUtils; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.internal.util.ImageUtils; import com.android.wm.shell.R; import com.android.wm.shell.common.pip.PipMediaController; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java index ca0d61f8fc9b..7a0e6694cb51 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java @@ -62,7 +62,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.internal.graphics.SfVsyncFrameCallbackProvider; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.R; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.pip.PipDisplayLayoutState; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PhonePipMenuController.java index 6e36a32ac931..9cfe1620a2ff 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PhonePipMenuController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PhonePipMenuController.java @@ -32,7 +32,7 @@ import android.view.View; import android.view.ViewRootImpl; import android.view.WindowManagerGlobal; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SystemWindows; import com.android.wm.shell.common.pip.PipBoundsState; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java index fc0d36d13b2e..68202ef642c4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java @@ -36,7 +36,7 @@ import androidx.annotation.BinderThread; import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.internal.util.Preconditions; import com.android.wm.shell.R; import com.android.wm.shell.ShellTaskOrganizer; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipInputConsumer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipInputConsumer.java index b757b00f16dd..ffda56d89276 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipInputConsumer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipInputConsumer.java @@ -28,7 +28,7 @@ import android.view.IWindowManager; import android.view.InputChannel; import android.view.InputEvent; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.protolog.ShellProtoLogGroup; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMenuView.java index 42b8e9f5a3ad..c54e4cd90f57 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMenuView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMenuView.java @@ -59,7 +59,7 @@ import android.view.accessibility.AccessibilityNodeInfo; import android.widget.FrameLayout; import android.widget.LinearLayout; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.R; import com.android.wm.shell.animation.Interpolators; import com.android.wm.shell.common.ShellExecutor; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java index 495cd0075494..cec246923e2c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java @@ -35,7 +35,7 @@ import android.os.Bundle; import android.os.Debug; import android.view.SurfaceControl; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.R; import com.android.wm.shell.animation.FloatProperties; import com.android.wm.shell.common.FloatingContentCoordinator; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java index 9c1e321a1273..4f62192eaf5b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java @@ -33,7 +33,7 @@ import androidx.annotation.IntDef; import androidx.annotation.Nullable; import androidx.core.content.ContextCompat; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.pip.PipBoundsState; import com.android.wm.shell.common.pip.PipUtils; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java index 56a465a4889a..a9f3b54006d9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java @@ -51,7 +51,7 @@ import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityWindowInfo; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.R; import com.android.wm.shell.common.FloatingContentCoordinator; import com.android.wm.shell.common.ShellExecutor; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchState.java index d093f1e5ccc1..bb8d4ee9c80f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchState.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchState.java @@ -23,7 +23,7 @@ import android.view.VelocityTracker; import android.view.ViewConfiguration; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.protolog.ShellProtoLogGroup; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java index ad298dcc253e..814eaae1ea74 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java @@ -43,7 +43,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.common.ExternalInterfaceBinder; import com.android.wm.shell.common.RemoteCallable; import com.android.wm.shell.common.ShellExecutor; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java index c67cf1d85918..e46625debcc6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java @@ -63,7 +63,7 @@ import androidx.annotation.NonNull; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.IResultReceiver; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.pip.PipUtils; import com.android.wm.shell.protolog.ShellProtoLogGroup; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java index 64e26dbd70be..1cbb8bbe5f75 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java @@ -23,7 +23,7 @@ import android.view.SurfaceSession; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.launcher3.icons.IconProvider; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.SyncTransactionQueue; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java index f5fbae55960a..27fd309c09b0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java @@ -24,7 +24,7 @@ import android.view.SurfaceSession; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.launcher3.icons.IconProvider; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.SyncTransactionQueue; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java index dd219d32bbaa..b4941a5b49b5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java @@ -73,7 +73,7 @@ import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.InstanceId; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.launcher3.icons.IconProvider; import com.android.wm.shell.R; import com.android.wm.shell.RootTaskDisplayAreaOrganizer; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java index 0541a0287179..b3dab8527617 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java @@ -46,7 +46,7 @@ import android.window.TransitionInfo; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.common.split.SplitDecorManager; import com.android.wm.shell.protolog.ShellProtoLogGroup; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index 45eff4a24898..d9e97766e4fe 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -119,7 +119,7 @@ import android.window.WindowContainerTransaction; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.InstanceId; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.internal.util.ArrayUtils; import com.android.launcher3.icons.IconProvider; import com.android.wm.shell.R; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java index 0f3d6cade95a..1076eca60369 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java @@ -45,7 +45,7 @@ import android.window.WindowContainerTransaction; import androidx.annotation.NonNull; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.internal.util.ArrayUtils; import com.android.launcher3.icons.IconProvider; import com.android.wm.shell.ShellTaskOrganizer; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitMenuController.java index 1d8a8d506c5c..e0f63940663a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitMenuController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitMenuController.java @@ -36,7 +36,7 @@ import android.view.LayoutInflater; import android.view.WindowManager; import android.view.WindowManagerGlobal; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.R; import com.android.wm.shell.common.SystemWindows; import com.android.wm.shell.common.split.SplitScreenConstants; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java index 2b12a22f907d..90eb22f369df 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java @@ -74,7 +74,7 @@ import com.android.internal.graphics.palette.Palette; import com.android.internal.graphics.palette.Quantizer; import com.android.internal.graphics.palette.VariationalKMeansQuantizer; import com.android.internal.policy.PhoneWindow; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.launcher3.icons.BaseIconFactory; import com.android.launcher3.icons.IconProvider; import com.android.wm.shell.common.TransactionPool; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenWindowCreator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenWindowCreator.java index e552e6cdacf3..08211ab5df9c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenWindowCreator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenWindowCreator.java @@ -53,7 +53,7 @@ import android.window.SplashScreenView; import android.window.StartingWindowInfo; import android.window.StartingWindowRemovalInfo; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.internal.util.ContrastColorUtil; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.protolog.ShellProtoLogGroup; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java index 3353c7bd81c2..97a695f34cf7 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java @@ -43,7 +43,7 @@ import android.window.StartingWindowRemovalInfo; import android.window.TaskSnapshot; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.launcher3.icons.IconProvider; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.TransactionPool; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java index 8fc54edcbd4b..6e084d6e05a4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java @@ -48,7 +48,7 @@ import android.window.SnapshotDrawerUtils; import android.window.StartingWindowInfo; import android.window.TaskSnapshot; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.internal.view.BaseIWindow; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.protolog.ShellProtoLogGroup; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java index 72fc8686f648..2036d9c13f0c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java @@ -35,7 +35,7 @@ import static android.window.StartingWindowInfo.TYPE_PARAMETER_WINDOWLESS; import android.window.StartingWindowInfo; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.startingsurface.StartingWindowTypeAlgorithm; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellCommandHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellCommandHandler.java index 2e6ddc363906..aa9f15c37531 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellCommandHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellCommandHandler.java @@ -18,7 +18,7 @@ package com.android.wm.shell.sysui; import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_INIT; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import java.io.PrintWriter; import java.util.Arrays; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellController.java index 5ced1fb41a41..0202b6cf3eab 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellController.java @@ -40,7 +40,7 @@ import android.view.SurfaceControlRegistry; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.common.DisplayInsetsController; import com.android.wm.shell.common.DisplayInsetsController.OnInsetsChangedListener; import com.android.wm.shell.common.ExternalInterfaceBinder; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInit.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInit.java index 2e2f569a52b8..3a680097554f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInit.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInit.java @@ -25,7 +25,7 @@ import android.view.SurfaceControl; import androidx.annotation.VisibleForTesting; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.common.ShellExecutor; import java.util.ArrayList; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java index 8ee1efa90a30..4f4b8097cfac 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java @@ -39,7 +39,7 @@ import android.window.TransitionRequestInfo; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.activityembedding.ActivityEmbeddingController; import com.android.wm.shell.common.split.SplitScreenUtils; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java index c33fb80fdefc..c8921d256d7f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java @@ -28,7 +28,7 @@ import android.os.IBinder; import android.view.SurfaceControl; import android.window.TransitionInfo; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.activityembedding.ActivityEmbeddingController; import com.android.wm.shell.keyguard.KeyguardTransitionHandler; import com.android.wm.shell.pip.PipTransitionController; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java index 9db153f2a5c5..73b32a24246a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java @@ -103,7 +103,7 @@ import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.policy.ScreenDecorationsUtils; import com.android.internal.policy.TransitionAnimation; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.window.flags.Flags; import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.common.DisplayController; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/LegacyTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/LegacyTransitions.java index 89b0e25b306b..978b8da2eb6d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/LegacyTransitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/LegacyTransitions.java @@ -28,7 +28,7 @@ import android.view.SurfaceControl; import android.view.WindowManager; import android.window.IWindowContainerTransactionCallback; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; /** * Utilities and interfaces for transition-like usage on top of the legacy app-transition and diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHelper.java index e8b01b5880fb..8cc7f212af25 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHelper.java @@ -35,7 +35,7 @@ import android.annotation.Nullable; import android.view.SurfaceControl; import android.window.TransitionInfo; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.keyguard.KeyguardTransitionHandler; import com.android.wm.shell.pip.PipTransitionController; import com.android.wm.shell.protolog.ShellProtoLogGroup; @@ -184,7 +184,8 @@ public class MixedTransitionHelper { for (int i = info.getChanges().size() - 1; i >= 0; --i) { TransitionInfo.Change change = info.getChanges().get(i); - if (change == pipChange || !isOpeningMode(change.getMode())) { + if (change == pipChange || !isOpeningMode(change.getMode()) || + change.getTaskInfo() == null) { // Ignore the change/task that's going into Pip or not opening continue; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java index 69c41675e989..c5dc668582bc 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java @@ -30,7 +30,7 @@ import android.window.TransitionRequestInfo; import android.window.WindowAnimationState; import android.window.WindowContainerTransaction; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.protolog.ShellProtoLogGroup; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java index 9fc6702562bb..391c5fe3473c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java @@ -30,7 +30,7 @@ import android.view.SurfaceControl; import android.window.TransitionInfo; import android.window.WindowContainerTransaction; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.desktopmode.DesktopTasksController; import com.android.wm.shell.keyguard.KeyguardTransitionHandler; import com.android.wm.shell.pip.PipTransitionController; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java index d6860464d055..6013a1ea1d9d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java @@ -39,7 +39,7 @@ import android.window.WindowContainerTransaction; import androidx.annotation.BinderThread; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.shared.TransitionUtil; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java index 2047b5a88604..a5f071af6973 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java @@ -54,7 +54,7 @@ import android.window.TransitionInfo; import com.android.internal.R; import com.android.internal.policy.TransitionAnimation; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.window.flags.Flags; import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.shared.TransitionUtil; 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 f6e38dac859c..ec6802da85f6 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 @@ -75,7 +75,7 @@ import androidx.annotation.BinderThread; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java index 7c2ba455c0c9..88bfebf9331e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java @@ -33,7 +33,7 @@ import android.window.WindowContainerTransaction; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.shared.TransitionUtil; import com.android.wm.shell.sysui.ShellInit; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/util/KtProtoLog.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/util/KtProtoLog.kt index 564e716c7378..ee6c81a3fa04 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/util/KtProtoLog.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/util/KtProtoLog.kt @@ -18,10 +18,10 @@ package com.android.wm.shell.util import android.util.Log import com.android.internal.protolog.common.IProtoLogGroup -import com.android.internal.protolog.common.ProtoLog +import com.android.internal.protolog.ProtoLog /** - * Log messages using an API similar to [com.android.internal.protolog.common.ProtoLog]. Useful for + * Log messages using an API similar to [com.android.internal.protolog.ProtoLog]. Useful for * logging from Kotlin classes as ProtoLog does not have support for Kotlin. * * All messages are logged to logcat if logging is enabled for that [IProtoLogGroup]. @@ -29,42 +29,42 @@ import com.android.internal.protolog.common.ProtoLog // TODO(b/168581922): remove once ProtoLog adds support for Kotlin class KtProtoLog { companion object { - /** @see [com.android.internal.protolog.common.ProtoLog.d] */ + /** @see [com.android.internal.protolog.ProtoLog.d] */ fun d(group: IProtoLogGroup, messageString: String, vararg args: Any) { if (group.isLogToLogcat) { Log.d(group.tag, String.format(messageString, *args)) } } - /** @see [com.android.internal.protolog.common.ProtoLog.v] */ + /** @see [com.android.internal.protolog.ProtoLog.v] */ fun v(group: IProtoLogGroup, messageString: String, vararg args: Any) { if (group.isLogToLogcat) { Log.v(group.tag, String.format(messageString, *args)) } } - /** @see [com.android.internal.protolog.common.ProtoLog.i] */ + /** @see [com.android.internal.protolog.ProtoLog.i] */ fun i(group: IProtoLogGroup, messageString: String, vararg args: Any) { if (group.isLogToLogcat) { Log.i(group.tag, String.format(messageString, *args)) } } - /** @see [com.android.internal.protolog.common.ProtoLog.w] */ + /** @see [com.android.internal.protolog.ProtoLog.w] */ fun w(group: IProtoLogGroup, messageString: String, vararg args: Any) { if (group.isLogToLogcat) { Log.w(group.tag, String.format(messageString, *args)) } } - /** @see [com.android.internal.protolog.common.ProtoLog.e] */ + /** @see [com.android.internal.protolog.ProtoLog.e] */ fun e(group: IProtoLogGroup, messageString: String, vararg args: Any) { if (group.isLogToLogcat) { Log.e(group.tag, String.format(messageString, *args)) } } - /** @see [com.android.internal.protolog.common.ProtoLog.wtf] */ + /** @see [com.android.internal.protolog.ProtoLog.wtf] */ fun wtf(group: IProtoLogGroup, messageString: String, vararg args: Any) { if (group.isLogToLogcat) { Log.wtf(group.tag, String.format(messageString, *args)) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/util/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/util/OWNERS new file mode 100644 index 000000000000..482aaab6bc74 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/util/OWNERS @@ -0,0 +1 @@ +per-file KtProtolog.kt = file:platform/development:/tools/winscope/OWNERS diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java index 180e4f999726..a05dbf844db0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java @@ -73,7 +73,7 @@ import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.jank.Cuj; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.window.flags.Flags; import com.android.wm.shell.R; import com.android.wm.shell.RootTaskDisplayAreaOrganizer; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java index d902444d4b15..a3616f65f3b2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java @@ -55,7 +55,7 @@ import android.view.ViewConfiguration; import android.view.WindowManagerGlobal; import android.window.InputTransferToken; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayLayout; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java index f9b4108bc8c2..8303317d39fc 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java @@ -687,6 +687,25 @@ public class ShellTaskOrganizerTests extends ShellTestCase { verify(mRecentTasksController).onTaskRunningInfoChanged(task2); } + @Test + public void testTaskVanishedCallback() { + RunningTaskInfo task1 = createTaskInfo(/* taskId= */ 1, WINDOWING_MODE_FULLSCREEN); + mOrganizer.onTaskAppeared(task1, /* leash= */ null); + + RunningTaskInfo[] vanishedTasks = new RunningTaskInfo[1]; + ShellTaskOrganizer.TaskVanishedListener listener = + new ShellTaskOrganizer.TaskVanishedListener() { + @Override + public void onTaskVanished(RunningTaskInfo taskInfo) { + vanishedTasks[0] = taskInfo; + } + }; + mOrganizer.addTaskVanishedListener(listener); + mOrganizer.onTaskVanished(task1); + + assertEquals(vanishedTasks[0], task1); + } + private static RunningTaskInfo createTaskInfo(int taskId, int windowingMode) { RunningTaskInfo taskInfo = new RunningTaskInfo(); taskInfo.taskId = taskId; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTestCase.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTestCase.java index 51a20ee9d090..a2df22c5468f 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTestCase.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTestCase.java @@ -26,7 +26,7 @@ import android.testing.TestableContext; import androidx.test.platform.app.InstrumentationRegistry; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import org.junit.After; import org.junit.Before; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java index 731f75bf9f5d..55b6bd278f20 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java @@ -21,10 +21,12 @@ import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY; import static android.window.TransitionInfo.FLAG_IS_BEHIND_STARTING_WINDOW; import static com.android.wm.shell.activityembedding.ActivityEmbeddingAnimationRunner.calculateParentBounds; +import static com.android.wm.shell.activityembedding.ActivityEmbeddingAnimationRunner.shouldUseJumpCutForAnimation; import static com.android.wm.shell.transition.Transitions.TRANSIT_TASK_FRAGMENT_DRAG_RESIZE; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertFalse; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doNothing; @@ -40,6 +42,8 @@ import android.graphics.Rect; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; +import android.view.animation.AlphaAnimation; +import android.view.animation.Animation; import android.window.TransitionInfo; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -281,6 +285,18 @@ public class ActivityEmbeddingAnimationRunnerTests extends ActivityEmbeddingAnim actualParentBounds); } + @Test + public void testShouldUseJumpCutForAnimation() { + final Animation noopAnimation = new AlphaAnimation(0f, 1f); + assertTrue("Animation without duration should use jump cut.", + shouldUseJumpCutForAnimation(noopAnimation)); + + final Animation alphaAnimation = new AlphaAnimation(0f, 1f); + alphaAnimation.setDuration(100); + assertFalse("Animation with duration should not use jump cut.", + shouldUseJumpCutForAnimation(alphaAnimation)); + } + @NonNull private static TransitionInfo.Change prepareChangeForParentBoundsCalculationTest( @NonNull Point endRelOffset, @NonNull Rect endAbsBounds, @NonNull Point endParentSize) { diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt index 0e53e10cde08..5f36e9a5e7b3 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt @@ -193,7 +193,7 @@ class DesktopTasksControllerTest : ShellTestCase() { .strictness(Strictness.LENIENT) .spyStatic(DesktopModeStatus::class.java) .startMocking() - whenever(DesktopModeStatus.isEnabled()).thenReturn(true) + whenever(DesktopModeStatus.isDesktopModeFlagEnabled()).thenReturn(true) doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) } shellInit = spy(ShellInit(testExecutor)) @@ -264,7 +264,7 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test fun instantiate_flagOff_doNotAddInitCallback() { - whenever(DesktopModeStatus.isEnabled()).thenReturn(false) + whenever(DesktopModeStatus.isDesktopModeFlagEnabled()).thenReturn(false) clearInvocations(shellInit) createController() diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java index a64ebd301c00..840126421c08 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java @@ -76,6 +76,8 @@ public class DragAndDropControllerTest extends ShellTestCase { @Mock private ShellCommandHandler mShellCommandHandler; @Mock + private ShellTaskOrganizer mShellTaskOrganizer; + @Mock private DisplayController mDisplayController; @Mock private UiEventLogger mUiEventLogger; @@ -96,8 +98,8 @@ public class DragAndDropControllerTest extends ShellTestCase { public void setUp() throws RemoteException { MockitoAnnotations.initMocks(this); mController = new DragAndDropController(mContext, mShellInit, mShellController, - mShellCommandHandler, mDisplayController, mUiEventLogger, mIconProvider, - mGlobalDragListener, mTransitions, mMainExecutor); + mShellCommandHandler, mShellTaskOrganizer, mDisplayController, mUiEventLogger, + mIconProvider, mGlobalDragListener, mTransitions, mMainExecutor); mController.onInit(); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java index 6e72e8df8d62..582fb91559e5 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java @@ -65,8 +65,6 @@ import android.content.res.Resources; import android.graphics.Insets; import android.os.RemoteException; import android.view.DisplayInfo; -import android.view.DragEvent; -import android.view.View; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; @@ -76,7 +74,6 @@ import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.draganddrop.DragAndDropPolicy.Target; import com.android.wm.shell.splitscreen.SplitScreenController; -import com.android.wm.shell.startingsurface.TaskSnapshotWindow; import org.junit.After; import org.junit.Before; @@ -106,6 +103,8 @@ public class DragAndDropPolicyTest extends ShellTestCase { // Both the split-screen and start interface. @Mock private SplitScreenController mSplitScreenStarter; + @Mock + private DragAndDropPolicy.Starter mFullscreenStarter; @Mock private InstanceId mLoggerSessionId; @@ -151,7 +150,7 @@ public class DragAndDropPolicyTest extends ShellTestCase { mPortraitDisplayLayout = new DisplayLayout(info2, res, false, false); mInsets = Insets.of(0, 0, 0, 0); - mPolicy = spy(new DragAndDropPolicy(mContext, mSplitScreenStarter, mSplitScreenStarter)); + mPolicy = spy(new DragAndDropPolicy(mContext, mSplitScreenStarter, mFullscreenStarter)); mActivityClipData = createAppClipData(MIMETYPE_APPLICATION_ACTIVITY); mLaunchableIntentPendingIntent = mock(PendingIntent.class); when(mLaunchableIntentPendingIntent.getCreatorUserHandle()) @@ -285,13 +284,13 @@ public class DragAndDropPolicyTest extends ShellTestCase { setRunningTask(mHomeTask); DragSession dragSession = new DragSession(mActivityTaskManager, mLandscapeDisplayLayout, data, 0 /* dragFlags */); - dragSession.update(); + dragSession.initialize(); mPolicy.start(dragSession, mLoggerSessionId); ArrayList<Target> targets = assertExactTargetTypes( mPolicy.getTargets(mInsets), TYPE_FULLSCREEN); mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN)); - verify(mSplitScreenStarter).startIntent(any(), anyInt(), any(), + verify(mFullscreenStarter).startIntent(any(), anyInt(), any(), eq(SPLIT_POSITION_UNDEFINED), any()); } @@ -300,7 +299,7 @@ public class DragAndDropPolicyTest extends ShellTestCase { setRunningTask(mFullscreenAppTask); DragSession dragSession = new DragSession(mActivityTaskManager, mLandscapeDisplayLayout, data, 0 /* dragFlags */); - dragSession.update(); + dragSession.initialize(); mPolicy.start(dragSession, mLoggerSessionId); ArrayList<Target> targets = assertExactTargetTypes( mPolicy.getTargets(mInsets), TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT); @@ -320,7 +319,7 @@ public class DragAndDropPolicyTest extends ShellTestCase { setRunningTask(mFullscreenAppTask); DragSession dragSession = new DragSession(mActivityTaskManager, mPortraitDisplayLayout, data, 0 /* dragFlags */); - dragSession.update(); + dragSession.initialize(); mPolicy.start(dragSession, mLoggerSessionId); ArrayList<Target> targets = assertExactTargetTypes( mPolicy.getTargets(mInsets), TYPE_SPLIT_TOP, TYPE_SPLIT_BOTTOM); @@ -340,7 +339,7 @@ public class DragAndDropPolicyTest extends ShellTestCase { setRunningTask(mFullscreenAppTask); DragSession dragSession = new DragSession(mActivityTaskManager, mLandscapeDisplayLayout, mActivityClipData, 0 /* dragFlags */); - dragSession.update(); + dragSession.initialize(); mPolicy.start(dragSession, mLoggerSessionId); ArrayList<Target> targets = mPolicy.getTargets(mInsets); for (Target t : targets) { diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt index 86aded76c0f3..ac5aeec3adc8 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt @@ -27,6 +27,9 @@ import android.platform.test.flag.junit.SetFlagsRule import android.testing.AndroidTestingRunner import android.view.Display import android.window.WindowContainerToken +import com.android.dx.mockito.inline.extended.ExtendedMockito +import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn +import com.android.dx.mockito.inline.extended.StaticMockitoSession import com.android.window.flags.Flags import com.android.wm.shell.R import com.android.wm.shell.common.DisplayController @@ -37,6 +40,7 @@ import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_RIGHT import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_TOP import com.google.common.truth.Truth.assertThat import junit.framework.Assert.assertTrue +import org.junit.After import org.junit.Before import org.junit.Rule import org.junit.Test @@ -45,6 +49,7 @@ import org.mockito.Mock import org.mockito.Mockito.any import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations +import org.mockito.quality.Strictness /** * Tests for [DragPositioningCallbackUtility]. @@ -82,9 +87,13 @@ class DragPositioningCallbackUtilityTest { @Rule val setFlagsRule = SetFlagsRule() + private lateinit var mockitoSession: StaticMockitoSession + @Before fun setup() { MockitoAnnotations.initMocks(this) + mockitoSession = ExtendedMockito.mockitoSession().strictness(Strictness.LENIENT) + .spyStatic(DesktopModeStatus::class.java).startMocking() whenever(taskToken.asBinder()).thenReturn(taskBinder) whenever(mockDisplayController.getDisplayLayout(DISPLAY_ID)).thenReturn(mockDisplayLayout) @@ -105,6 +114,11 @@ class DragPositioningCallbackUtilityTest { whenever(mockDisplay.displayId).thenAnswer { DISPLAY_ID } } + @After + fun tearDown() { + mockitoSession.finishMocking() + } + @Test fun testChangeBoundsDoesNotChangeHeightWhenLessThanMin() { val startingPoint = PointF(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.top.toFloat()) @@ -252,7 +266,7 @@ class DragPositioningCallbackUtilityTest { @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_SIZE_CONSTRAINTS) fun taskMinWidthHeightUndefined_changeBoundsInDesktopModeLessThanMin_shouldNotChangeBounds() { - whenever(DesktopModeStatus.canEnterDesktopMode(mockContext)).thenReturn(true) + doReturn(true).`when`{ DesktopModeStatus.canEnterDesktopMode(mockContext) } initializeTaskInfo(taskMinWidth = -1, taskMinHeight = -1) val startingPoint = PointF(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.bottom.toFloat()) @@ -275,7 +289,7 @@ class DragPositioningCallbackUtilityTest { @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_SIZE_CONSTRAINTS) fun taskMinWidthHeightUndefined_changeBoundsInDesktopModeAllowedSize_shouldChangeBounds() { - whenever(DesktopModeStatus.canEnterDesktopMode(mockContext)).thenReturn(true) + doReturn(true).`when`{ DesktopModeStatus.canEnterDesktopMode(mockContext) } initializeTaskInfo(taskMinWidth = -1, taskMinHeight = -1) val startingPoint = PointF(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.bottom.toFloat()) @@ -361,7 +375,7 @@ class DragPositioningCallbackUtilityTest { @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_SIZE_CONSTRAINTS) fun testChangeBoundsInDesktopMode_windowSizeExceedsStableBounds_shouldBeLimitedToDisplaySize() { - whenever(DesktopModeStatus.canEnterDesktopMode(mockContext)).thenReturn(true) + doReturn(true).`when`{ DesktopModeStatus.canEnterDesktopMode(mockContext) } val startingPoint = PointF(OFF_CENTER_STARTING_BOUNDS.right.toFloat(), OFF_CENTER_STARTING_BOUNDS.bottom.toFloat()) diff --git a/media/tests/MediaFrameworkTest/Android.bp b/media/tests/MediaFrameworkTest/Android.bp index 1325fc161b77..028c97ec51f7 100644 --- a/media/tests/MediaFrameworkTest/Android.bp +++ b/media/tests/MediaFrameworkTest/Android.bp @@ -24,6 +24,7 @@ android_test { "flag-junit", "testng", "truth", + "collector-device-lib-platform", ], jni_libs: [ "libdexmakerjvmtiagent", diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.kt index 2e9b7b421a7b..a23df64f3582 100644 --- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.kt +++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.kt @@ -97,6 +97,7 @@ class InstallRepository(private val context: Context) { private set private var callingUid = Process.INVALID_UID private var originatingUid = Process.INVALID_UID + private var originatingUidFromSessionInfo = Process.INVALID_UID private var callingPackage: String? = null private var sessionStager: SessionStager? = null private lateinit var intent: Intent @@ -136,17 +137,25 @@ class InstallRepository(private val context: Context) { callingPackage = callerInfo.packageName - if (sessionId != SessionInfo.INVALID_ID) { - val sessionInfo: SessionInfo? = packageInstaller.getSessionInfo(sessionId) - callingPackage = sessionInfo?.getInstallerPackageName() - callingAttributionTag = sessionInfo?.getInstallerAttributionTag() - } - // Uid of the source package, coming from ActivityManager callingUid = callerInfo.uid if (callingUid == Process.INVALID_UID) { Log.e(LOG_TAG, "Could not determine the launching uid.") } + + originatingUidFromSessionInfo = callingUid + val sessionInfo: SessionInfo? = + if (sessionId != SessionInfo.INVALID_ID) + packageInstaller.getSessionInfo(sessionId) + else null + if (sessionInfo != null) { + callingPackage = sessionInfo.installerPackageName + callingAttributionTag = sessionInfo.installerAttributionTag + if (sessionInfo.originatingUid != Process.INVALID_UID) { + originatingUidFromSessionInfo = sessionInfo.originatingUid + } + } + val sourceInfo: ApplicationInfo? = getSourceInfo(callingPackage) // Uid of the source package, with a preference to uid from ApplicationInfo originatingUid = sourceInfo?.uid ?: callingUid @@ -651,7 +660,12 @@ class InstallRepository(private val context: Context) { private fun getUpdateMessage(pkgInfo: PackageInfo, userActionReason: Int): String? { if (isAppUpdating(pkgInfo)) { val existingUpdateOwnerLabel = getExistingUpdateOwnerLabel(pkgInfo) - val requestedUpdateOwnerLabel = getApplicationLabel(callingPackage) + + val originatingPackageNameFromSessionInfo = + getPackageNameForUid(context, originatingUidFromSessionInfo, callingPackage) + val requestedUpdateOwnerLabel = + getApplicationLabel(originatingPackageNameFromSessionInfo) + if (!TextUtils.isEmpty(existingUpdateOwnerLabel) && userActionReason == PackageInstaller.REASON_REMIND_OWNERSHIP ) { diff --git a/packages/SettingsLib/aconfig/settingslib.aconfig b/packages/SettingsLib/aconfig/settingslib.aconfig index 4ac3e671a378..8666584e0972 100644 --- a/packages/SettingsLib/aconfig/settingslib.aconfig +++ b/packages/SettingsLib/aconfig/settingslib.aconfig @@ -64,13 +64,6 @@ flag { } flag { - name: "allow_all_widgets_on_lockscreen_by_default" - namespace: "systemui" - description: "Allow all widgets on the lock screen by default." - bug: "328261690" -} - -flag { name: "enable_determining_advanced_details_header_with_metadata" namespace: "pixel_cross_device_control" description: "Use metadata instead of device type to determine whether a bluetooth device should use advanced details header." diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index 27c386e52c8c..cfd74d4b9f42 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -1010,10 +1010,10 @@ <!-- UI debug setting: force allow on external summary [CHAR LIMIT=150] --> <string name="force_resizable_activities_summary">Make all activities resizable for multi-window, regardless of manifest values.</string> - <!-- UI debug setting: enable freeform window support [CHAR LIMIT=50] --> - <string name="enable_freeform_support">Enable freeform windows</string> - <!-- UI debug setting: enable freeform window support summary [CHAR LIMIT=150] --> - <string name="enable_freeform_support_summary">Enable support for experimental freeform windows.</string> + <!-- UI debug setting: enable legacy freeform window support [CHAR LIMIT=50] --> + <string name="enable_freeform_support">Enable freeform windows (legacy)</string> + <!-- UI debug setting: enable legacy freeform window support summary [CHAR LIMIT=150] --> + <string name="enable_freeform_support_summary">Enable support for experimental legacy freeform windows.</string> <!-- Local (desktop) backup password menu title [CHAR LIMIT=25] --> <string name="local_backup_password_title">Desktop backup password</string> diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.kt b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.kt index 727c61ca1ae0..a7e04640d069 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.kt +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.kt @@ -498,7 +498,7 @@ open class WifiUtils { ): Job = coroutineScope.launch { val wifiManager = context.getSystemService(WifiManager::class.java) ?: return@launch - if (wifiManager.queryWepAllowed()) { + if (wifiManager.isWepSupported == true && wifiManager.queryWepAllowed()) { onAllowed() } else { val intent = Intent(Intent.ACTION_MAIN).apply { diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index d54236e60dd7..54f0d7a4d853 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -1189,6 +1189,8 @@ public class SettingsProvider extends ContentProvider { synchronized (mLock) { if (getSyncDisabledModeConfigLocked() != SYNC_DISABLED_MODE_NONE) { + Slog.v(LOG_TAG, "did not write settings for prefix '" + + prefix + "' because sync is disabled"); return SET_ALL_RESULT_DISABLED; } final int key = makeKey(SETTINGS_TYPE_CONFIG, UserHandle.USER_SYSTEM); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java index 05eb0449495f..861c405b1542 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java @@ -203,6 +203,12 @@ final class SettingsState { private static final String NULL_VALUE = "null"; + // TOBO(b/312444587): remove after Test Mission 2. + // Bulk sync names + private static final String BULK_SYNC_MARKER = "aconfigd_marker/bulk_synced"; + private static final String BULK_SYNC_TRIGGER_COUNTER = + "core_experiments_team_internal/BulkSyncTriggerCounterFlag__bulk_sync_trigger_counter"; + private static final ArraySet<String> sSystemPackages = new ArraySet<>(); private final Object mWriteLock = new Object(); @@ -409,8 +415,7 @@ final class SettingsState { } } // TOBO(b/312444587): remove the comparison logic after Test Mission 2. - if (mSettings.get("aconfigd_marker/bulk_synced").value.equals("true") - && requests == null) { + if (requests == null) { Map<String, AconfigdFlagInfo> aconfigdFlagMap = AconfigdJavaUtils.listFlagsValueInNewStorage(localSocket); compareFlagValueInNewStorage( @@ -534,7 +539,7 @@ final class SettingsState { return null; } AconfigdFlagInfo flag = flagInfoDefault.get(fullFlagName); - if (flag == null) { + if (flag == null || !namespace.equals(flag.getNamespace())) { return null; } @@ -553,15 +558,33 @@ final class SettingsState { public ProtoOutputStream handleBulkSyncToNewStorage( Map<String, AconfigdFlagInfo> aconfigFlagMap) { // get marker or add marker if it does not exist - final String bulkSyncMarkerName = new String("aconfigd_marker/bulk_synced"); - Setting markerSetting = mSettings.get(bulkSyncMarkerName); + Setting markerSetting = mSettings.get(BULK_SYNC_MARKER); + int localCounter = 0; if (markerSetting == null) { - markerSetting = new Setting(bulkSyncMarkerName, "false", false, "aconfig", "aconfig"); - mSettings.put(bulkSyncMarkerName, markerSetting); + markerSetting = new Setting(BULK_SYNC_MARKER, "0", false, "aconfig", "aconfig"); + mSettings.put(BULK_SYNC_MARKER, markerSetting); + } + try { + localCounter = Integer.parseInt(markerSetting.value); + } catch(NumberFormatException e) { + // reset local counter + markerSetting.value = "0"; } if (enableAconfigStorageDaemon()) { - if (markerSetting.value.equals("true")) { + Setting bulkSyncCounter = mSettings.get(BULK_SYNC_TRIGGER_COUNTER); + int serverCounter = 0; + if (bulkSyncCounter != null) { + try { + serverCounter = Integer.parseInt(bulkSyncCounter.value); + } catch (NumberFormatException e) { + // reset the local value of server counter + bulkSyncCounter.value = "0"; + } + } + + boolean shouldSync = localCounter < serverCounter; + if (!shouldSync) { // CASE 1, flag is on, bulk sync marker true, nothing to do return null; } else { @@ -600,20 +623,12 @@ final class SettingsState { } // mark sync has been done - markerSetting.value = "true"; + markerSetting.value = String.valueOf(serverCounter); scheduleWriteIfNeededLocked(); return requests; } } else { - if (markerSetting.value.equals("true")) { - // CASE 3, flag is off, bulk sync marker true, clear the marker - markerSetting.value = "false"; - scheduleWriteIfNeededLocked(); - return null; - } else { - // CASE 4, flag is off, bulk sync marker false, nothing to do - return null; - } + return null; } } @@ -692,6 +707,7 @@ final class SettingsState { .setFlagName(flag.getName()) .setDefaultFlagValue(flagValue) .setIsReadWrite(isReadWrite) + .setNamespace(flag.getNamespace()) .build()); } } diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index 5f8e933e0652..411decd8476a 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -177,6 +177,7 @@ public class SettingsBackupTest { Settings.Global.DESK_UNDOCK_SOUND, Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS, + Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES, Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES, Settings.Global.DEVELOPMENT_FORCE_RTL, Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java index 94aeb9b8dcc4..4b4ced3c0753 100644 --- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java +++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java @@ -151,12 +151,14 @@ public class SettingsStateTest { .setFlagName("flag1") .setDefaultFlagValue("false") .setIsReadWrite(true) + .setNamespace("test_namespace") .build(); AconfigdFlagInfo flag2 = AconfigdFlagInfo.newBuilder() .setPackageName("com.android.flags") .setFlagName("flag2") .setDefaultFlagValue("true") .setIsReadWrite(false) + .setNamespace("test_namespace") .build(); Map<String, AconfigdFlagInfo> flagInfoDefault = new HashMap<>(); @@ -1018,12 +1020,17 @@ public class SettingsStateTest { .setFlagName("flag1") .setDefaultFlagValue("false") .setIsReadWrite(true) + .setNamespace("test_namespace") .build(); flagInfoDefault.put(flag1.getFullFlagName(), flag1); - // server override + // not the right namespace + assertNull( + settingsState.getFlagOverrideToSync( + "some_namespace/com.android.flags.flag1", "true", flagInfoDefault)); + // server override settingsState.getFlagOverrideToSync( "test_namespace/com.android.flags.flag1", "true", flagInfoDefault); assertEquals("com.android.flags", flag1.getPackageName()); @@ -1079,21 +1086,45 @@ public class SettingsStateTest { .setIsReadWrite(false) .build()); + String bulkSyncMarker = "aconfigd_marker/bulk_synced"; + String bulkSyncCounter = + "core_experiments_team_internal/" + + "BulkSyncTriggerCounterFlag__bulk_sync_trigger_counter"; + synchronized (lock) { - settingsState.insertSettingLocked( - "aconfigd_marker/bulk_synced", "false", null, false, "aconfig"); + settingsState.insertSettingLocked(bulkSyncMarker, "0", null, false, "aconfig"); + settingsState.insertSettingLocked(bulkSyncCounter, "1", null, false, + "com.google.android.platform.core_experiments_team_internal"); // first bulk sync ProtoOutputStream requests = settingsState.handleBulkSyncToNewStorage(flags); assertTrue(requests != null); String value = settingsState.getSettingLocked("aconfigd_marker/bulk_synced").getValue(); - assertEquals("true", value); + assertEquals("1", value); // send time should no longer bulk sync requests = settingsState.handleBulkSyncToNewStorage(flags); - assertTrue(requests == null); + assertNull(requests); + value = settingsState.getSettingLocked("aconfigd_marker/bulk_synced").getValue(); + assertEquals("1", value); + + // won't sync if the marker is string + settingsState.insertSettingLocked(bulkSyncMarker, "true", null, false, "aconfig"); + settingsState.insertSettingLocked(bulkSyncCounter, "0", null, false, + "com.google.android.platform.core_experiments_team_internal"); + requests = settingsState.handleBulkSyncToNewStorage(flags); + assertNull(requests); value = settingsState.getSettingLocked("aconfigd_marker/bulk_synced").getValue(); - assertEquals("true", value); + assertEquals("0", value); + + // won't sync if the marker and counter value are the same + settingsState.insertSettingLocked(bulkSyncMarker, "1", null, false, "aconfig"); + settingsState.insertSettingLocked(bulkSyncCounter, "1", null, false, + "com.google.android.platform.core_experiments_team_internal"); + requests = settingsState.handleBulkSyncToNewStorage(flags); + assertNull(requests); + value = settingsState.getSettingLocked("aconfigd_marker/bulk_synced").getValue(); + assertEquals("1", value); } } @@ -1107,21 +1138,34 @@ public class SettingsStateTest { SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper()); Map<String, AconfigdFlagInfo> flags = new HashMap<>(); + String bulkSyncMarker = "aconfigd_marker/bulk_synced"; + String bulkSyncCounter = + "core_experiments_team_internal/" + + "BulkSyncTriggerCounterFlag__bulk_sync_trigger_counter"; synchronized (lock) { settingsState.insertSettingLocked("aconfigd_marker/bulk_synced", "true", null, false, "aconfig"); // when aconfigd is off, should change the marker to false ProtoOutputStream requests = settingsState.handleBulkSyncToNewStorage(flags); - assertTrue(requests == null); + assertNull(requests); String value = settingsState.getSettingLocked("aconfigd_marker/bulk_synced").getValue(); - assertEquals("false", value); + assertEquals("0", value); // marker started with false value, after call, it should remain false requests = settingsState.handleBulkSyncToNewStorage(flags); - assertTrue(requests == null); + assertNull(requests); value = settingsState.getSettingLocked("aconfigd_marker/bulk_synced").getValue(); - assertEquals("false", value); + assertEquals("0", value); + + // won't sync + settingsState.insertSettingLocked(bulkSyncMarker, "0", null, false, "aconfig"); + settingsState.insertSettingLocked(bulkSyncCounter, "1", null, false, + "com.google.android.platform.core_experiments_team_internal"); + requests = settingsState.handleBulkSyncToNewStorage(flags); + assertNull(requests); + value = settingsState.getSettingLocked("aconfigd_marker/bulk_synced").getValue(); + assertEquals("0", value); } } @@ -1164,6 +1208,7 @@ public class SettingsStateTest { .setFlagName("flag1") .setDefaultFlagValue("false") .setIsReadWrite(true) + .setNamespace("test_namespace") .build(); flagInfoDefault.put(flag1.getFullFlagName(), flag1); @@ -1186,6 +1231,7 @@ public class SettingsStateTest { .setFlagName("flag2") .setDefaultFlagValue("false") .setIsReadWrite(true) + .setNamespace("test_namespace") .build(); flagInfoDefault.put(flag2.getFullFlagName(), flag2); synchronized (lock) { @@ -1207,6 +1253,7 @@ public class SettingsStateTest { .setFlagName("flag3") .setDefaultFlagValue("false") .setIsReadWrite(false) + .setNamespace("test_namespace") .build(); flagInfoDefault.put(flag3.getFullFlagName(), flag3); synchronized (lock) { diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig index 0731616b81bf..f5153e1aa8b3 100644 --- a/packages/SystemUI/aconfig/systemui.aconfig +++ b/packages/SystemUI/aconfig/systemui.aconfig @@ -1122,3 +1122,13 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "translucent_occluding_activity_fix" + namespace: "systemui" + description: "Fixes occlusion animation for transluent activities" + bug: "303010980" + metadata { + purpose: PURPOSE_BUGFIX + } +}
\ No newline at end of file diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt index b6e4e9b13a1c..c14ee6208176 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt @@ -22,6 +22,7 @@ import android.app.PendingIntent import android.app.TaskInfo import android.app.WindowConfiguration import android.content.ComponentName +import android.graphics.Color import android.graphics.Matrix import android.graphics.Rect import android.graphics.RectF @@ -53,6 +54,7 @@ import com.android.app.animation.Interpolators import com.android.internal.annotations.VisibleForTesting import com.android.internal.policy.ScreenDecorationsUtils import com.android.systemui.Flags.activityTransitionUseLargestWindow +import com.android.systemui.Flags.translucentOccludingActivityFix import com.android.systemui.shared.Flags.returnAnimationFrameworkLibrary import com.android.wm.shell.shared.IShellTransitions import com.android.wm.shell.shared.ShellTransitions @@ -991,7 +993,12 @@ constructor( controller.createAnimatorState() } val windowBackgroundColor = - window.taskInfo?.let { callback.getBackgroundColor(it) } ?: window.backgroundColor + if (translucentOccludingActivityFix() && window.isTranslucent) { + Color.TRANSPARENT + } else { + window.taskInfo?.let { callback.getBackgroundColor(it) } + ?: window.backgroundColor + } // TODO(b/184121838): We should somehow get the top and bottom radius of the window // instead of recomputing isExpandingFullyAbove here. diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt index 22566e70f409..9c9e6c69de1c 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt @@ -34,6 +34,8 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.compose.animation.scene.MutableSceneTransitionLayoutState import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.SceneTransitionLayout +import com.android.compose.animation.scene.UserAction +import com.android.compose.animation.scene.UserActionResult import com.android.compose.animation.scene.observableTransitionState import com.android.systemui.ribbon.ui.composable.BottomRightCornerRibbon import com.android.systemui.scene.shared.model.SceneDataSourceDelegator @@ -56,7 +58,6 @@ import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel * must have entries in this map. * @param modifier A modifier. */ -@OptIn(ExperimentalComposeUiApi::class) @Composable fun SceneContainer( viewModel: SceneContainerViewModel, @@ -66,8 +67,6 @@ fun SceneContainer( ) { val coroutineScope = rememberCoroutineScope() val currentSceneKey: SceneKey by viewModel.currentScene.collectAsStateWithLifecycle() - val currentDestinations by - viewModel.currentDestinationScenes(coroutineScope).collectAsStateWithLifecycle() val state: MutableSceneTransitionLayoutState = remember { MutableSceneTransitionLayoutState( initialScene = currentSceneKey, @@ -88,20 +87,19 @@ fun SceneContainer( onDispose { viewModel.setTransitionState(null) } } + val userActionsBySceneKey: Map<SceneKey, Map<UserAction, UserActionResult>> = + sceneByKey.values.associate { scene -> + val userActions by scene.destinationScenes.collectAsStateWithLifecycle(emptyMap()) + val resolvedUserActions = viewModel.resolveSceneFamilies(userActions) + scene.key to resolvedUserActions + } + Box( modifier = Modifier.fillMaxSize(), ) { SceneTransitionLayout(state = state, modifier = modifier.fillMaxSize()) { sceneByKey.forEach { (sceneKey, composableScene) -> - scene( - key = sceneKey, - userActions = - if (sceneKey == currentSceneKey) { - currentDestinations - } else { - viewModel.resolveSceneFamilies(composableScene.destinationScenes.value) - }, - ) { + scene(key = sceneKey, userActions = checkNotNull(userActionsBySceneKey[sceneKey])) { with(composableScene) { this@scene.Content( modifier = Modifier.element(sceneKey.rootElementKey).fillMaxSize(), diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java index fa79ea01cf51..54e0725957e6 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java @@ -1284,6 +1284,41 @@ public class UdfpsControllerTest extends SysuiTestCase { } @Test + public void onAodDownAndDownTouchReceived() throws RemoteException { + final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L, + 0L); + final TouchProcessorResult processorResultDown = + new TouchProcessorResult.ProcessedTouch(InteractionEvent.DOWN, + -1 /* pointerId */, touchData); + + mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, + BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); + mFgExecutor.runAllReady(); + + verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); + + // WHEN fingerprint is requested because of AOD interrupt + // GIVEN there's been an AoD interrupt + when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true); + mScreenObserver.onScreenTurnedOn(); + mUdfpsController.onAodInterrupt(0, 0, 0, 0); + mFgExecutor.runAllReady(); + + // and an ACTION_DOWN is received and touch is within sensor + when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( + processorResultDown); + MotionEvent firstDownEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0); + mTouchListenerCaptor.getValue().onTouch(mUdfpsView, firstDownEvent); + mBiometricExecutor.runAllReady(); + firstDownEvent.recycle(); + + // THEN the touch is only processed once + verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(), + anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), + anyBoolean()); + } + + @Test public void onTouch_pilferPointerWhenAltBouncerShowing() throws RemoteException { final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult = diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalDreamStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalDreamStartableTest.kt index fe683e07a93d..dcc9c7aacdf0 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalDreamStartableTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalDreamStartableTest.kt @@ -47,6 +47,7 @@ import org.mockito.Mockito.verify @OptIn(ExperimentalCoroutinesApi::class) @SmallTest +@EnableFlags(Flags.FLAG_COMMUNAL_HUB) @RunWith(AndroidJUnit4::class) class CommunalDreamStartableTest : SysuiTestCase() { private val kosmos = testKosmos() @@ -76,7 +77,7 @@ class CommunalDreamStartableTest : SysuiTestCase() { testScope.runTest { keyguardRepository.setDreaming(false) powerRepository.setScreenPowerState(ScreenPowerState.SCREEN_ON) - whenever(dreamManager.canStartDreaming(/* isScreenOn = */ true)).thenReturn(true) + whenever(dreamManager.canStartDreaming(/* isScreenOn= */ true)).thenReturn(true) runCurrent() verify(dreamManager, never()).startDream() @@ -92,7 +93,7 @@ class CommunalDreamStartableTest : SysuiTestCase() { testScope.runTest { keyguardRepository.setDreaming(false) powerRepository.setScreenPowerState(ScreenPowerState.SCREEN_ON) - whenever(dreamManager.canStartDreaming(/* isScreenOn = */ true)).thenReturn(true) + whenever(dreamManager.canStartDreaming(/* isScreenOn= */ true)).thenReturn(true) runCurrent() verify(dreamManager, never()).startDream() @@ -118,7 +119,7 @@ class CommunalDreamStartableTest : SysuiTestCase() { keyguardRepository.setDreaming(false) powerRepository.setScreenPowerState(ScreenPowerState.SCREEN_ON) // Not eligible to dream - whenever(dreamManager.canStartDreaming(/* isScreenOn = */ true)).thenReturn(false) + whenever(dreamManager.canStartDreaming(/* isScreenOn= */ true)).thenReturn(false) transition(from = KeyguardState.DREAMING, to = KeyguardState.GLANCEABLE_HUB) verify(dreamManager, never()).startDream() @@ -129,7 +130,7 @@ class CommunalDreamStartableTest : SysuiTestCase() { testScope.runTest { keyguardRepository.setDreaming(true) powerRepository.setScreenPowerState(ScreenPowerState.SCREEN_ON) - whenever(dreamManager.canStartDreaming(/* isScreenOn = */ true)).thenReturn(true) + whenever(dreamManager.canStartDreaming(/* isScreenOn= */ true)).thenReturn(true) transition(from = KeyguardState.DREAMING, to = KeyguardState.GLANCEABLE_HUB) verify(dreamManager, never()).startDream() @@ -140,7 +141,7 @@ class CommunalDreamStartableTest : SysuiTestCase() { testScope.runTest { keyguardRepository.setDreaming(true) powerRepository.setScreenPowerState(ScreenPowerState.SCREEN_ON) - whenever(dreamManager.canStartDreaming(/* isScreenOn = */ true)).thenReturn(true) + whenever(dreamManager.canStartDreaming(/* isScreenOn= */ true)).thenReturn(true) // Verify we do not trigger dreaming for any other state besides glanceable hub for (state in KeyguardState.entries) { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt index fb2b33d70c47..da40f640d5fa 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt @@ -20,7 +20,6 @@ import android.app.admin.DevicePolicyManager import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL import android.app.admin.devicePolicyManager -import android.appwidget.AppWidgetProviderInfo import android.content.Intent import android.content.pm.UserInfo import android.os.UserManager.USER_TYPE_PROFILE_MANAGED @@ -29,7 +28,6 @@ import android.platform.test.annotations.EnableFlags import android.provider.Settings import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.settingslib.flags.Flags.FLAG_ALLOW_ALL_WIDGETS_ON_LOCKSCREEN_BY_DEFAULT import com.android.systemui.Flags.FLAG_COMMUNAL_HUB import com.android.systemui.SysuiTestCase import com.android.systemui.broadcast.broadcastDispatcher @@ -183,42 +181,6 @@ class CommunalSettingsRepositoryImplTest : SysuiTestCase() { ) } - @EnableFlags(FLAG_COMMUNAL_HUB) - @Test - fun hubShowsWidgetCategoriesSetByUser() = - testScope.runTest { - kosmos.fakeSettings.putIntForUser( - CommunalSettingsRepositoryImpl.GLANCEABLE_HUB_CONTENT_SETTING, - AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN, - PRIMARY_USER.id - ) - val setting by collectLastValue(underTest.getWidgetCategories(PRIMARY_USER)) - assertThat(setting?.categories) - .isEqualTo(AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN) - } - - @EnableFlags(FLAG_COMMUNAL_HUB) - @DisableFlags(FLAG_ALLOW_ALL_WIDGETS_ON_LOCKSCREEN_BY_DEFAULT) - @Test - fun hubShowsKeyguardWidgetsByDefault() = - testScope.runTest { - val setting by collectLastValue(underTest.getWidgetCategories(PRIMARY_USER)) - assertThat(setting?.categories) - .isEqualTo(AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD) - } - - @EnableFlags(FLAG_COMMUNAL_HUB, FLAG_ALLOW_ALL_WIDGETS_ON_LOCKSCREEN_BY_DEFAULT) - @Test - fun hubShowsAllWidgetsByDefaultWhenFlagEnabled() = - testScope.runTest { - val setting by collectLastValue(underTest.getWidgetCategories(PRIMARY_USER)) - assertThat(setting?.categories) - .isEqualTo( - AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD + - AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN - ) - } - @Test fun backgroundType_defaultValue() = testScope.runTest { 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 d951cca89f64..7b26db50814e 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 @@ -36,7 +36,6 @@ import com.android.compose.animation.scene.ObservableTransitionState import com.android.systemui.Flags.FLAG_COMMUNAL_HUB import com.android.systemui.SysuiTestCase import com.android.systemui.broadcast.broadcastDispatcher -import com.android.systemui.communal.data.repository.CommunalSettingsRepositoryImpl import com.android.systemui.communal.data.repository.FakeCommunalMediaRepository import com.android.systemui.communal.data.repository.FakeCommunalPrefsRepository import com.android.systemui.communal.data.repository.FakeCommunalSceneRepository @@ -81,7 +80,6 @@ import com.android.systemui.util.mockito.capture import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.nullable import com.android.systemui.util.mockito.whenever -import com.android.systemui.util.settings.fakeSettings import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow @@ -915,14 +913,6 @@ class CommunalInteractorTest : SysuiTestCase() { ) runCurrent() - // Keyguard widgets are allowed. - kosmos.fakeSettings.putIntForUser( - CommunalSettingsRepositoryImpl.GLANCEABLE_HUB_CONTENT_SETTING, - AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD, - mainUser.id - ) - runCurrent() - // When work profile is paused. whenever(userManager.isQuietModeEnabled(eq(UserHandle.of(USER_INFO_WORK.id)))) .thenReturn(true) @@ -956,93 +946,6 @@ class CommunalInteractorTest : SysuiTestCase() { } @Test - fun widgetContent_containsDisabledWidgets_whenCategoryNotAllowed() = - testScope.runTest { - // Communal available, and tutorial completed. - keyguardRepository.setKeyguardShowing(true) - keyguardRepository.setKeyguardOccluded(false) - tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED) - - val userInfos = listOf(MAIN_USER_INFO, USER_INFO_WORK) - userRepository.setUserInfos(userInfos) - userTracker.set( - userInfos = userInfos, - selectedUserIndex = 0, - ) - userRepository.setSelectedUserInfo(MAIN_USER_INFO) - runCurrent() - - // Widgets available. - val widget1 = - createWidgetWithCategory(1, AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN) - val widget2 = - createWidgetWithCategory(2, AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD) - val widget3 = - createWidgetWithCategory(3, AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX) - val widgets = listOf(widget1, widget2, widget3) - widgetRepository.setCommunalWidgets(widgets) - - val widgetContent by collectLastValue(underTest.widgetContent) - kosmos.fakeSettings.putIntForUser( - CommunalSettingsRepositoryImpl.GLANCEABLE_HUB_CONTENT_SETTING, - AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD, - mainUser.id - ) - - // Only the keyguard widget is enabled. - assertThat(widgetContent).hasSize(3) - assertThat(widgetContent!!.get(0)) - .isInstanceOf(CommunalContentModel.WidgetContent.DisabledWidget::class.java) - assertThat(widgetContent!!.get(1)) - .isInstanceOf(CommunalContentModel.WidgetContent.Widget::class.java) - assertThat(widgetContent!!.get(2)) - .isInstanceOf(CommunalContentModel.WidgetContent.DisabledWidget::class.java) - } - - @Test - fun widgetContent_allEnabled_whenCategoryAllowed() = - testScope.runTest { - // Communal available, and tutorial completed. - keyguardRepository.setKeyguardShowing(true) - keyguardRepository.setKeyguardOccluded(false) - tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED) - - val userInfos = listOf(MAIN_USER_INFO, USER_INFO_WORK) - userRepository.setUserInfos(userInfos) - userTracker.set( - userInfos = userInfos, - selectedUserIndex = 0, - ) - userRepository.setSelectedUserInfo(MAIN_USER_INFO) - runCurrent() - - // Widgets available. - val widget1 = - createWidgetWithCategory(1, AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN) - val widget2 = - createWidgetWithCategory(2, AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD) - val widget3 = - createWidgetWithCategory(3, AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD) - val widgets = listOf(widget1, widget2, widget3) - widgetRepository.setCommunalWidgets(widgets) - - val widgetContent by collectLastValue(underTest.widgetContent) - kosmos.fakeSettings.putIntForUser( - CommunalSettingsRepositoryImpl.GLANCEABLE_HUB_CONTENT_SETTING, - AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD or - AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN, - mainUser.id - ) - - // All widgets are enabled. - assertThat(widgetContent).hasSize(3) - widgetContent!!.forEach { model -> - assertThat(model) - .isInstanceOf(CommunalContentModel.WidgetContent.Widget::class.java) - } - } - - @Test fun filterWidgets_whenDisallowedByDevicePolicyForWorkProfile() = testScope.runTest { // Keyguard showing, and tutorial completed. diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetInteractionHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetInteractionHandlerTest.kt index 7b7d03b2024a..70448955eff0 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetInteractionHandlerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetInteractionHandlerTest.kt @@ -72,6 +72,7 @@ class WidgetInteractionHandlerTest : SysuiTestCase() { /* animationController = */ notNull(), /* fillInIntent = */ refEq(fillInIntent), /* extraOptions = */ refEq(activityOptions.toBundle()), + /* customMessage */ isNull(), ) } @@ -93,6 +94,7 @@ class WidgetInteractionHandlerTest : SysuiTestCase() { /* animationController = */ isNull(), /* fillInIntent = */ refEq(fillInIntent), /* extraOptions = */ refEq(activityOptions.toBundle()), + /* customMessage */ isNull(), ) } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt index 9ce2e0f87499..c33e2a49ef5d 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt @@ -92,7 +92,8 @@ class QSTileViewModelTest : SysuiTestCase() { runCurrent() assertThat(states()).isNotEmpty() - assertThat(states().first().label).isEqualTo(testTileData) + assertThat(states().last()).isNotNull() + assertThat(states().last()!!.label).isEqualTo(testTileData) verify(qsTileLogger).logInitialRequest(eq(tileConfig.tileSpec)) } @@ -196,6 +197,7 @@ class QSTileViewModelTest : SysuiTestCase() { qsTileLogger, FakeSystemClock(), testCoroutineDispatcher, + testCoroutineDispatcher, scope.backgroundScope, ) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.kt index 6066d24c2214..7955f2fc1335 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.kt @@ -256,6 +256,7 @@ class QSTileViewModelUserInputTest : SysuiTestCase() { qsTileLogger, FakeSystemClock(), testCoroutineDispatcher, + testCoroutineDispatcher, scope.backgroundScope, ) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt index cb4d96fa735f..39b366226987 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt @@ -133,7 +133,6 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { sceneInteractor = sceneInteractor, falsingInteractor = kosmos.falsingInteractor, powerInteractor = kosmos.powerInteractor, - scenes = kosmos.scenes, ) .apply { setTransitionState(transitionState) } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt index 5c30379ea2fb..ea95aab4a1c4 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt @@ -28,10 +28,8 @@ import com.android.systemui.kosmos.testScope import com.android.systemui.power.data.repository.fakePowerRepository import com.android.systemui.power.domain.interactor.powerInteractor import com.android.systemui.scene.domain.interactor.sceneInteractor -import com.android.systemui.scene.fakeScenes import com.android.systemui.scene.sceneContainerConfig import com.android.systemui.scene.sceneKeys -import com.android.systemui.scene.scenes import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.shared.model.fakeSceneDataSource import com.android.systemui.testKosmos @@ -66,7 +64,6 @@ class SceneContainerViewModelTest : SysuiTestCase() { sceneInteractor = sceneInteractor, falsingInteractor = kosmos.falsingInteractor, powerInteractor = kosmos.powerInteractor, - scenes = kosmos.scenes, ) } @@ -217,23 +214,4 @@ class SceneContainerViewModelTest : SysuiTestCase() { assertThat(isVisible).isFalse() } - - @Test - fun currentDestinationScenes_onlyTheCurrentSceneIsCollected() = - testScope.runTest { - val unused by collectLastValue(underTest.currentDestinationScenes(backgroundScope)) - val currentScene by collectLastValue(sceneInteractor.currentScene) - kosmos.fakeScenes.forEach { scene -> - fakeSceneDataSource.changeScene(toScene = scene.key) - runCurrent() - assertThat(currentScene).isEqualTo(scene.key) - - assertThat(scene.isDestinationScenesBeingCollected).isTrue() - kosmos.fakeScenes - .filter { it.key != scene.key } - .forEach { otherScene -> - assertThat(otherScene.isDestinationScenesBeingCollected).isFalse() - } - } - } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImplTest.kt index 5887f90036ec..ccd78ee82169 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImplTest.kt @@ -154,6 +154,25 @@ class LegacyActivityStarterInternalImplTest : SysuiTestCase() { } @Test + fun startPendingIntentDismissingKeyguard_withCustomMessage_dismissWithAction() { + val pendingIntent = mock(PendingIntent::class.java) + `when`(pendingIntent.isActivity).thenReturn(true) + `when`(keyguardStateController.isShowing).thenReturn(true) + `when`(deviceProvisionedController.isDeviceProvisioned).thenReturn(true) + val customMessage = "Custom unlock reason" + + underTest.startPendingIntentDismissingKeyguard( + intent = pendingIntent, + dismissShade = true, + customMessage = customMessage + ) + mainExecutor.runAllReady() + + verify(statusBarKeyguardViewManager) + .dismissWithAction(any(), eq(null), anyBoolean(), eq(customMessage)) + } + + @Test fun startPendingIntentMaybeDismissingKeyguard_keyguardShowing_showOverLs_launchAnimator() { val pendingIntent = mock(PendingIntent::class.java) val parent = FrameLayout(context) @@ -466,6 +485,7 @@ class LegacyActivityStarterInternalImplTest : SysuiTestCase() { animationController: ActivityTransitionAnimator.Controller?, fillInIntent: Intent? = null, extraOptions: Bundle? = null, + customMessage: String? = null, ) { underTest.startPendingIntentDismissingKeyguard( intent = intent, @@ -475,6 +495,7 @@ class LegacyActivityStarterInternalImplTest : SysuiTestCase() { showOverLockscreen = true, fillInIntent = fillInIntent, extraOptions = extraOptions, + customMessage = customMessage, ) } diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java index 7cf56aa5c40e..abb721ab0dd8 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java @@ -84,14 +84,17 @@ public interface ActivityStarter { * Similar to {@link #startPendingIntentMaybeDismissingKeyguard(PendingIntent, Runnable, * ActivityTransitionAnimator.Controller)}, but also specifies a fill-in intent and extra * option that could be used to populate the pending intent and launch the activity. This also - * allows the caller to avoid dismissing the shade. + * allows the caller to avoid dismissing the shade. An optional custom message can be set as + * the unlock reason in the alternate bouncer. */ void startPendingIntentMaybeDismissingKeyguard(PendingIntent intent, boolean dismissShade, @Nullable Runnable intentSentUiThreadCallback, @Nullable ActivityTransitionAnimator.Controller animationController, @Nullable Intent fillInIntent, - @Nullable Bundle extraOptions); + @Nullable Bundle extraOptions, + @Nullable String customMessage + ); /** * The intent flag can be specified in startActivity(). @@ -134,14 +137,20 @@ public interface ActivityStarter { void dismissKeyguardThenExecute(OnDismissAction action, @Nullable Runnable cancel, boolean afterKeyguardGone); - /** Authenticates if needed and dismisses keyguard to execute an action. */ + /** + * Authenticates if needed and dismisses keyguard to execute an action. + * + * TODO(b/348431835) Display the custom message in the new alternate bouncer, when the + * device_entry_udfps_refactor flag is enabled. + */ void dismissKeyguardThenExecute(OnDismissAction action, @Nullable Runnable cancel, boolean afterKeyguardGone, @Nullable String customMessage); /** Starts an activity and dismisses keyguard. */ void startActivityDismissingKeyguard(Intent intent, boolean onlyProvisioned, - boolean dismissShade); + boolean dismissShade, + @Nullable String customMessage); /** Starts an activity and dismisses keyguard. */ void startActivityDismissingKeyguard(Intent intent, diff --git a/packages/SystemUI/res/color/connected_network_primary_color.xml b/packages/SystemUI/res/color/connected_network_primary_color.xml new file mode 100644 index 000000000000..f173c8dd5473 --- /dev/null +++ b/packages/SystemUI/res/color/connected_network_primary_color.xml @@ -0,0 +1,20 @@ +<!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<selector xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"> + <item android:color="?androidprv:attr/materialColorOnPrimaryContainer" /> +</selector>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/settingslib_switch_bar_bg_on.xml b/packages/SystemUI/res/drawable/settingslib_switch_bar_bg_on.xml index 250188b892f4..fab2d8db859f 100644 --- a/packages/SystemUI/res/drawable/settingslib_switch_bar_bg_on.xml +++ b/packages/SystemUI/res/drawable/settingslib_switch_bar_bg_on.xml @@ -16,10 +16,11 @@ --> <ripple xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" android:color="?android:attr/colorControlHighlight"> <item> <shape android:shape="rectangle"> - <solid android:color="@color/settingslib_state_on_color"/> + <solid android:color="?androidprv:attr/materialColorPrimaryContainer"/> <corners android:radius="@dimen/settingslib_switch_bar_radius"/> </shape> </item> diff --git a/packages/SystemUI/res/drawable/settingslib_thumb_on.xml b/packages/SystemUI/res/drawable/settingslib_thumb_on.xml index 5566ea3f62fc..e316a93c1c3d 100644 --- a/packages/SystemUI/res/drawable/settingslib_thumb_on.xml +++ b/packages/SystemUI/res/drawable/settingslib_thumb_on.xml @@ -15,7 +15,8 @@ limitations under the License. --> -<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> +<layer-list xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"> <item android:top="@dimen/settingslib_switch_thumb_margin" android:bottom="@dimen/settingslib_switch_thumb_margin"> @@ -23,7 +24,7 @@ <size android:height="@dimen/settingslib_switch_thumb_size" android:width="@dimen/settingslib_switch_thumb_size"/> - <solid android:color="@color/settingslib_state_on_color"/> + <solid android:color="?androidprv:attr/materialColorOnPrimary"/> </shape> </item> </layer-list> diff --git a/packages/SystemUI/res/drawable/settingslib_track_on_background.xml b/packages/SystemUI/res/drawable/settingslib_track_on_background.xml index 1d9dacd6c0f9..e2e64684f9c3 100644 --- a/packages/SystemUI/res/drawable/settingslib_track_on_background.xml +++ b/packages/SystemUI/res/drawable/settingslib_track_on_background.xml @@ -16,11 +16,12 @@ --> <shape xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" android:shape="rectangle" android:width="@dimen/settingslib_switch_track_width" android:height="@dimen/settingslib_switch_track_height"> <padding android:left="@dimen/settingslib_switch_thumb_margin" android:right="@dimen/settingslib_switch_thumb_margin"/> - <solid android:color="@color/settingslib_track_on_color"/> + <solid android:color="?androidprv:attr/materialColorPrimary"/> <corners android:radius="@dimen/settingslib_switch_track_radius"/> </shape> diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml index d377e0196e98..21f1cfbe4c9e 100644 --- a/packages/SystemUI/res/values-night/colors.xml +++ b/packages/SystemUI/res/values-night/colors.xml @@ -104,10 +104,6 @@ <color name="people_tile_background">@color/material_dynamic_secondary20</color> - <!-- Internet Dialog --> - <color name="connected_network_primary_color">@color/material_dynamic_primary80</color> - <color name="connected_network_secondary_color">@color/material_dynamic_secondary80</color> - <!-- Keyboard shortcut helper dialog --> <color name="ksh_key_item_color">@*android:color/system_on_surface_variant_dark</color> </resources> diff --git a/packages/SystemUI/res/values-night/styles.xml b/packages/SystemUI/res/values-night/styles.xml index 546bf1c3ac95..e9dd039f38c2 100644 --- a/packages/SystemUI/res/values-night/styles.xml +++ b/packages/SystemUI/res/values-night/styles.xml @@ -60,18 +60,6 @@ <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item> </style> - <style name="TextAppearance.InternetDialog.Active"> - <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item> - <item name="android:textSize">16sp</item> - <item name="android:textColor">@color/material_dynamic_primary80</item> - <item name="android:textDirection">locale</item> - </style> - - <style name="TextAppearance.InternetDialog.Secondary.Active"> - <item name="android:textSize">14sp</item> - <item name="android:textColor">@color/material_dynamic_secondary80</item> - </style> - <style name="ShortcutHelperTheme" parent="@style/ShortcutHelperThemeCommon"> <item name="android:windowLightNavigationBar">false</item> </style> diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index b3d3021028e1..0350cd7dab98 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -236,11 +236,8 @@ <!-- Internet Dialog --> <!-- Material next state on color--> <color name="settingslib_state_on_color">@color/settingslib_state_on</color> - <!-- Material next track on color--> - <color name="settingslib_track_on_color">@color/settingslib_track_on</color> <!-- Material next track off color--> <color name="settingslib_track_off_color">@color/settingslib_track_off</color> - <color name="connected_network_primary_color">#191C18</color> <color name="connected_network_secondary_color">#41493D</color> <color name="dream_overlay_camera_mic_off_dot_color">#FCBE03</color> diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml index 177ba598add7..212dae279387 100644 --- a/packages/SystemUI/res/values/ids.xml +++ b/packages/SystemUI/res/values/ids.xml @@ -233,6 +233,7 @@ <item type="id" name="smart_space_barrier_bottom" /> <item type="id" name="small_clock_guideline_top" /> <item type="id" name="weather_clock_date_and_icons_barrier_bottom" /> + <item type="id" name="weather_clock_bc_smartspace_bottom" /> <item type="id" name="accessibility_actions_view" /> <!-- Privacy dialog --> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index ff43c9bc276e..e92b942635f2 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1193,6 +1193,8 @@ <string name="popup_on_dismiss_cta_tile_text">Long press to customize widgets</string> <!-- Text for the button to configure widgets after long press. [CHAR LIMIT=50] --> <string name="button_to_configure_widgets_text">Customize widgets</string> + <!-- Text for unlock reason on the bouncer before customizing widgets. [CHAR LIMIT=NONE] --> + <string name="unlock_reason_to_customize_widgets">Unlock to customize widgets</string> <!-- Description for the App icon of disabled widget. [CHAR LIMIT=NONE] --> <string name="icon_description_for_disabled_widget">App icon for disabled widget</string> <!-- Description for the App icon of a package that is currently being installed. [CHAR LIMIT=NONE] --> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 73b7586f1210..7475eb2eceaa 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -1315,7 +1315,7 @@ <item name="android:background">?android:attr/selectableItemBackground</item> </style> - <style name="MainSwitch.Settingslib" parent="@android:style/Theme.DeviceDefault"> + <style name="MainSwitch.Settingslib" parent="@android:style/Theme.DeviceDefault.DayNight"> <item name="android:switchMinWidth">@dimen/settingslib_min_switch_width</item> </style> @@ -1358,6 +1358,7 @@ <style name="InternetDialog.NetworkTitle.Active"> <item name="android:textAppearance">@style/TextAppearance.InternetDialog.Active</item> + <item name="android:textColor">?androidprv:attr/materialColorOnPrimaryContainer</item> </style> <style name="InternetDialog.NetworkSummary"> @@ -1370,18 +1371,19 @@ <style name="InternetDialog.NetworkSummary.Active"> <item name="android:textAppearance">@style/TextAppearance.InternetDialog.Secondary.Active </item> + <item name="android:textColor">?androidprv:attr/materialColorOnPrimaryContainer</item> </style> <style name="TextAppearance.InternetDialog"> <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item> <item name="android:textSize">16sp</item> - <item name="android:textColor">?android:attr/textColorPrimary</item> + <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item> <item name="android:textDirection">locale</item> </style> <style name="TextAppearance.InternetDialog.Secondary"> <item name="android:textSize">14sp</item> - <item name="android:textColor">?android:attr/textColorSecondary</item> + <item name="android:textColor">?androidprv:attr/materialColorOnSurfaceVariant</item> </style> <style name="TextAppearance.InternetDialog.Active"/> diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java index 4b0740288844..56de5a3ed88a 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java @@ -38,7 +38,7 @@ import android.view.View; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.systemui.dagger.GlobalRootComponent; import com.android.systemui.dagger.SysUIComponent; import com.android.systemui.dump.DumpManager; diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java index 9d573d3919b9..4a28d8b05661 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java @@ -469,6 +469,7 @@ class MenuViewLayer extends FrameLayout implements private void onSpringAnimationsEndAction() { if (mShouldShowDockTooltip) { + mEduTooltipView.ifPresent(this::removeTooltip); mEduTooltipView = Optional.of(new MenuEduTooltipView(mContext, mMenuViewAppearance)); mEduTooltipView.ifPresent(view -> addTooltipView(view, getContext().getText(R.string.accessibility_floating_button_docking_tooltip), diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index ad142a86fa03..9d3c6a49a616 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -558,7 +558,12 @@ public class UdfpsController implements DozeReceiver, Dumpable { Log.w(TAG, "onTouch down received without a preceding up"); } mActivePointerId = MotionEvent.INVALID_POINTER_ID; - mOnFingerDown = false; + + // It's possible on some devices to get duplicate touches from both doze and the + // normal touch listener. Don't reset the down in this case to avoid duplicate downs + if (!mIsAodInterruptActive) { + mOnFingerDown = false; + } } else if (!DeviceEntryUdfpsRefactor.isEnabled()) { if ((mLockscreenShadeTransitionController.getQSDragProgress() != 0f && !mAlternateBouncerInteractor.isVisibleState()) diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalWidgetCategories.kt b/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalWidgetCategories.kt index 5cd15f278f00..75f0badfc7cb 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalWidgetCategories.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalWidgetCategories.kt @@ -17,7 +17,6 @@ package com.android.systemui.communal.data.model import android.appwidget.AppWidgetProviderInfo -import com.android.settingslib.flags.Flags.allowAllWidgetsOnLockscreenByDefault /** * The widget categories to display on communal hub (where categories is a bitfield with values that @@ -31,9 +30,7 @@ value class CommunalWidgetCategories(val categories: Int = defaultCategories) { val defaultCategories: Int get() { return AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD or - if (allowAllWidgetsOnLockscreenByDefault()) - AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN - else 0 + AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN } } } diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt index 1c47e507c972..2940a95fdc33 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt @@ -24,7 +24,6 @@ import android.provider.Settings import com.android.systemui.Flags.communalHub import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.communal.data.model.CommunalEnabledState -import com.android.systemui.communal.data.model.CommunalWidgetCategories import com.android.systemui.communal.data.model.DisabledReason import com.android.systemui.communal.data.model.DisabledReason.DISABLED_REASON_DEVICE_POLICY import com.android.systemui.communal.data.model.DisabledReason.DISABLED_REASON_FLAG @@ -52,12 +51,6 @@ interface CommunalSettingsRepository { /** A [CommunalEnabledState] for the specified user. */ fun getEnabledState(user: UserInfo): Flow<CommunalEnabledState> - /** - * A flow that reports the widget categories to show on the hub as selected by the user in - * Settings. - */ - fun getWidgetCategories(user: UserInfo): Flow<CommunalWidgetCategories> - /** Keyguard widgets enabled state by Device Policy Manager for the specified user. */ fun getAllowedByDevicePolicy(user: UserInfo): Flow<Boolean> @@ -104,22 +97,6 @@ constructor( .flowOn(bgDispatcher) } - override fun getWidgetCategories(user: UserInfo): Flow<CommunalWidgetCategories> = - secureSettings - .observerFlow(userId = user.id, names = arrayOf(GLANCEABLE_HUB_CONTENT_SETTING)) - // Force an update - .onStart { emit(Unit) } - .map { - CommunalWidgetCategories( - secureSettings.getIntForUser( - GLANCEABLE_HUB_CONTENT_SETTING, - CommunalWidgetCategories.defaultCategories, - user.id - ) - ) - } - .flowOn(bgDispatcher) - override fun getAllowedByDevicePolicy(user: UserInfo): Flow<Boolean> = broadcastDispatcher .broadcastFlow( @@ -159,7 +136,6 @@ constructor( } companion object { - const val GLANCEABLE_HUB_CONTENT_SETTING = "glanceable_hub_content_setting" const val GLANCEABLE_HUB_BACKGROUND_SETTING = "glanceable_hub_background" private const val ENABLED_SETTING_DEFAULT = 1 } 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 9f3ade9cd425..f5255ac4d545 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 @@ -392,26 +392,17 @@ constructor( allowedForWorkProfile -> filterWidgetsAllowedByDevicePolicy(widgets, allowedForWorkProfile) }, - communalSettingsInteractor.communalWidgetCategories, updateOnWorkProfileBroadcastReceived, - ) { widgets, allowedCategories, _ -> + ) { widgets, _ -> widgets.map { widget -> when (widget) { is CommunalWidgetContentModel.Available -> { - if (widget.providerInfo.widgetCategory and allowedCategories != 0) { - // At least one category this widget specified is allowed, so show it - WidgetContent.Widget( - appWidgetId = widget.appWidgetId, - providerInfo = widget.providerInfo, - appWidgetHost = appWidgetHost, - inQuietMode = isQuietModeEnabled(widget.providerInfo.profile) - ) - } else { - WidgetContent.DisabledWidget( - appWidgetId = widget.appWidgetId, - providerInfo = widget.providerInfo, - ) - } + WidgetContent.Widget( + appWidgetId = widget.appWidgetId, + providerInfo = widget.providerInfo, + appWidgetHost = appWidgetHost, + inQuietMode = isQuietModeEnabled(widget.providerInfo.profile) + ) } is CommunalWidgetContentModel.Pending -> { WidgetContent.PendingWidget( diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt index f043d58543fc..47b75c458d20 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt @@ -19,7 +19,6 @@ package com.android.systemui.communal.domain.interactor import android.content.pm.UserInfo import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow import com.android.systemui.communal.data.model.CommunalEnabledState -import com.android.systemui.communal.data.model.CommunalWidgetCategories import com.android.systemui.communal.data.repository.CommunalSettingsRepository import com.android.systemui.communal.shared.model.CommunalBackgroundType import com.android.systemui.dagger.SysUISingleton @@ -70,18 +69,6 @@ constructor( // Start this eagerly since the value is accessed synchronously in many places. .stateIn(scope = bgScope, started = SharingStarted.Eagerly, initialValue = false) - /** What widget categories to show on the hub. */ - val communalWidgetCategories: StateFlow<Int> = - userInteractor.selectedUserInfo - .flatMapLatest { user -> repository.getWidgetCategories(user) } - .map { categories -> categories.categories } - .stateIn( - scope = bgScope, - // Start this eagerly since the value can be accessed synchronously. - started = SharingStarted.Eagerly, - initialValue = CommunalWidgetCategories.defaultCategories - ) - /** The type of background to use for the hub. Used to experiment with different backgrounds */ val communalBackground: Flow<CommunalBackgroundType> = userInteractor.selectedUserInfo diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt index 9185384e79a3..fab243575670 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt @@ -25,6 +25,7 @@ import android.util.Log import androidx.activity.result.ActivityResultLauncher import com.android.internal.logging.UiEventLogger import com.android.systemui.Flags.enableWidgetPickerSizeFilter +import com.android.systemui.communal.data.model.CommunalWidgetCategories import com.android.systemui.communal.domain.interactor.CommunalInteractor import com.android.systemui.communal.domain.interactor.CommunalPrefsInteractor import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor @@ -183,7 +184,7 @@ constructor( } putExtra( AppWidgetManager.EXTRA_CATEGORY_FILTER, - communalSettingsInteractor.communalWidgetCategories.value + CommunalWidgetCategories.defaultCategories ) putExtra(EXTRA_UI_SURFACE_KEY, EXTRA_UI_SURFACE_VALUE) putParcelableArrayListExtra(EXTRA_ADDED_APP_WIDGETS_KEY, excludeList) diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivityStarter.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivityStarter.kt index 76be0055c44b..af87f09d3c89 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivityStarter.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivityStarter.kt @@ -22,6 +22,7 @@ import com.android.systemui.communal.widgets.EditWidgetsActivity.Companion.EXTRA import com.android.systemui.communal.widgets.EditWidgetsActivity.Companion.EXTRA_PRESELECTED_KEY import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.plugins.ActivityStarter +import com.android.systemui.res.R import javax.inject.Inject interface EditWidgetsActivityStarter { @@ -48,6 +49,7 @@ constructor( }, /* onlyProvisioned = */ true, /* dismissShade = */ true, + applicationContext.resources.getString(R.string.unlock_reason_to_customize_widgets), ) } } diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt index cbc6c977e3e6..72f9180c51d2 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt @@ -34,10 +34,11 @@ constructor( private val activityStarter: ActivityStarter, ) : RemoteViews.InteractionHandler { - private val delegate = InteractionHandlerDelegate( - findViewToAnimate = { view -> view is CommunalAppWidgetHostView }, - intentStarter = this::startIntent, - ) + private val delegate = + InteractionHandlerDelegate( + findViewToAnimate = { view -> view is CommunalAppWidgetHostView }, + intentStarter = this::startIntent, + ) override fun onInteraction( view: View, @@ -45,7 +46,6 @@ constructor( response: RemoteViews.RemoteResponse ): Boolean = delegate.onInteraction(view, pendingIntent, response) - private fun startIntent( pendingIntent: PendingIntent, fillInIntent: Intent, @@ -59,6 +59,8 @@ constructor( controller, fillInIntent, extraOptions.toBundle(), + // TODO(b/325110448): UX to provide copy + /* customMessage = */ null, ) return true } diff --git a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt index 140434040ca7..af7ecf66d107 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt @@ -29,6 +29,7 @@ import com.android.systemui.keyguard.KeyguardBottomAreaRefactor import com.android.systemui.keyguard.MigrateClocksToBlueprint import com.android.systemui.keyguard.shared.ComposeLockscreen import com.android.systemui.scene.shared.flag.SceneContainerFlag +import com.android.systemui.shade.shared.flag.DualShade import com.android.systemui.statusbar.notification.collection.SortBySectionTimeFlag import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor import com.android.systemui.statusbar.notification.interruption.VisualInterruptionRefactor @@ -62,6 +63,9 @@ class FlagDependencies @Inject constructor(featureFlags: FeatureFlagsClassic, ha // CommunalHub dependencies communalHub dependsOn MigrateClocksToBlueprint.token + + // DualShade dependencies + DualShade.token dependsOn SceneContainerFlag.getMainAconfigFlag() } private inline val politeNotifications diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 73347ada371c..1ea5d1c00561 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -42,6 +42,7 @@ import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STR import static com.android.systemui.DejankUtils.whitelistIpcs; import static com.android.systemui.Flags.notifyPowerManagerUserActivityBackground; import static com.android.systemui.Flags.refactorGetCurrentUser; +import static com.android.systemui.Flags.translucentOccludingActivityFix; import static com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel.DREAMING_ANIMATION_DURATION_MS; import android.animation.Animator; @@ -1036,6 +1037,17 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, (int) (fullWidth - initialWidth) /* left */, fullWidth /* right */, mWindowCornerRadius, mWindowCornerRadius); + } else if (translucentOccludingActivityFix() + && mOccludingRemoteAnimationTarget != null + && mOccludingRemoteAnimationTarget.isTranslucent) { + // Animating in a transparent window looks really weird. Just let it be + // fullscreen and the app can do an internal animation if it wants to. + return new TransitionAnimator.State( + 0, + fullHeight, + 0, + fullWidth, + 0f, 0f); } else { final float initialHeight = fullHeight / 2f; final float initialWidth = fullWidth / 2f; @@ -1399,6 +1411,11 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, private final Lazy<DreamViewModel> mDreamViewModel; private final Lazy<CommunalTransitionViewModel> mCommunalTransitionViewModel; private RemoteAnimationTarget mRemoteAnimationTarget; + + /** + * The most recent RemoteAnimationTarget provided for an occluding activity animation. + */ + private RemoteAnimationTarget mOccludingRemoteAnimationTarget; private boolean mShowCommunalWhenUnoccluding = false; private final Lazy<WindowManagerLockscreenVisibilityManager> mWmLockscreenVisibilityManager; @@ -3941,6 +3958,13 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, public void onAnimationStart(int transit, RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps, IRemoteAnimationFinishedCallback finishedCallback) throws RemoteException { + // Save mRemoteAnimationTarget for reference in the animation controller. Needs to be + // called prior to super.onAnimationStart() since that's the call that eventually asks + // the animation controller to configure the animation state. + if (apps.length > 0) { + mOccludingRemoteAnimationTarget = apps[0]; + } + super.onAnimationStart(transit, apps, wallpapers, nonApps, finishedCallback); mInteractionJankMonitor.begin( 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 4f75e6fb717a..22ab82b383a7 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 @@ -249,7 +249,7 @@ constructor( val isKeyguardGoingAway: Flow<Boolean> = repository.isKeyguardGoingAway /** Keyguard can be clipped at the top as the shade is dragged */ - val topClippingBounds: Flow<Int?> = + val topClippingBounds: Flow<Int?> by lazy { combineTransform( keyguardTransitionInteractor .transitionValue(scene = Scenes.Gone, stateWithoutSceneContainer = GONE) @@ -263,6 +263,7 @@ constructor( } } .distinctUntilChanged() + } /** Last point that [KeyguardRootView] view was tapped */ val lastRootViewTapPosition: Flow<Point?> = repository.lastRootViewTapPosition.asStateFlow() diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt index f2821a0f49d2..e0633807db88 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt @@ -37,6 +37,10 @@ import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.plugins.clocks.AodClockBurnInModel import com.android.systemui.plugins.clocks.ClockController +import com.android.systemui.util.ui.value +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch object KeyguardClockViewBinder { @@ -99,15 +103,20 @@ object KeyguardClockViewBinder { launch { if (!MigrateClocksToBlueprint.isEnabled) return@launch - viewModel.isAodIconsVisible.collect { - viewModel.currentClock.value?.let { - if ( - viewModel.isLargeClockVisible.value && it.config.useCustomClockScene - ) { - blueprintInteractor.refreshBlueprint(Type.DefaultTransition) + combine( + viewModel.hasAodIcons, + rootViewModel.isNotifIconContainerVisible.map { it.value } + ) { hasIcon, isVisible -> + hasIcon && isVisible + } + .distinctUntilChanged() + .collect { _ -> + viewModel.currentClock.value?.let { + if (it.config.useCustomClockScene) { + blueprintInteractor.refreshBlueprint(Type.DefaultTransition) + } } } - } } launch { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt index 34a1da54c123..0637bba9f1de 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt @@ -45,6 +45,7 @@ import com.android.systemui.plugins.clocks.ClockController import com.android.systemui.plugins.clocks.ClockFaceLayout import com.android.systemui.res.R import com.android.systemui.shared.R as sharedR +import com.android.systemui.util.ui.value import dagger.Lazy import javax.inject.Inject @@ -70,6 +71,7 @@ constructor( private val rootViewModel: KeyguardRootViewModel, ) : KeyguardSection() { override fun addViews(constraintLayout: ConstraintLayout) {} + override fun bindData(constraintLayout: ConstraintLayout) { if (!MigrateClocksToBlueprint.isEnabled) { return @@ -121,35 +123,39 @@ constructor( private fun getTargetClockFace(clock: ClockController): ClockFaceLayout = if (keyguardClockViewModel.isLargeClockVisible.value) clock.largeClock.layout else clock.smallClock.layout + private fun getNonTargetClockFace(clock: ClockController): ClockFaceLayout = if (keyguardClockViewModel.isLargeClockVisible.value) clock.smallClock.layout else clock.largeClock.layout fun constrainWeatherClockDateIconsBarrier(constraints: ConstraintSet) { constraints.apply { - if (keyguardClockViewModel.isAodIconsVisible.value) { + createBarrier( + R.id.weather_clock_bc_smartspace_bottom, + Barrier.BOTTOM, + getDimen(ENHANCED_SMARTSPACE_HEIGHT), + (custR.id.weather_clock_time) + ) + if ( + rootViewModel.isNotifIconContainerVisible.value.value && + keyguardClockViewModel.hasAodIcons.value + ) { createBarrier( R.id.weather_clock_date_and_icons_barrier_bottom, Barrier.BOTTOM, 0, - *intArrayOf(sharedR.id.bc_smartspace_view, R.id.aod_notification_icon_container) + *intArrayOf( + R.id.aod_notification_icon_container, + R.id.weather_clock_bc_smartspace_bottom + ) ) } else { - if (smartspaceViewModel.bcSmartspaceVisibility.value == VISIBLE) { - createBarrier( - R.id.weather_clock_date_and_icons_barrier_bottom, - Barrier.BOTTOM, - 0, - (sharedR.id.bc_smartspace_view) - ) - } else { - createBarrier( - R.id.weather_clock_date_and_icons_barrier_bottom, - Barrier.BOTTOM, - getDimen(ENHANCED_SMARTSPACE_HEIGHT), - (R.id.lockscreen_clock_view) - ) - } + createBarrier( + R.id.weather_clock_date_and_icons_barrier_bottom, + Barrier.BOTTOM, + 0, + *intArrayOf(R.id.weather_clock_bc_smartspace_bottom) + ) } } } @@ -198,6 +204,7 @@ constructor( companion object { private const val DATE_WEATHER_VIEW_HEIGHT = "date_weather_view_height" private const val ENHANCED_SMARTSPACE_HEIGHT = "enhanced_smartspace_height" + fun getDimen(context: Context, name: String): Int { val res = context.packageManager.getResourcesForApplication(context.packageName) val id = res.getIdentifier(name, "dimen", context.packageName) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt index f5c521a3d8c7..573b75e623fb 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt @@ -31,7 +31,6 @@ import com.android.systemui.keyguard.shared.model.ClockSizeSetting import com.android.systemui.res.R import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.shade.shared.model.ShadeMode -import com.android.systemui.statusbar.notification.domain.interactor.NotificationsKeyguardInteractor import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerAlwaysOnDisplayViewModel import com.android.systemui.statusbar.ui.SystemBarUtilsProxy import javax.inject.Inject @@ -50,7 +49,6 @@ constructor( keyguardClockInteractor: KeyguardClockInteractor, @Application private val applicationScope: CoroutineScope, aodNotificationIconViewModel: NotificationIconContainerAlwaysOnDisplayViewModel, - notifsKeyguardInteractor: NotificationsKeyguardInteractor, @get:VisibleForTesting val shadeInteractor: ShadeInteractor, private val systemBarUtils: SystemBarUtilsProxy, configurationInteractor: ConfigurationInteractor, @@ -90,14 +88,13 @@ constructor( currentClock?.let { clock -> val face = if (isLargeClock) clock.largeClock else clock.smallClock face.config.hasCustomWeatherDataDisplay - } - ?: false + } ?: false } .stateIn( scope = applicationScope, started = SharingStarted.WhileSubscribed(), - initialValue = currentClock.value?.largeClock?.config?.hasCustomWeatherDataDisplay - ?: false + initialValue = + currentClock.value?.largeClock?.config?.hasCustomWeatherDataDisplay ?: false ) val clockShouldBeCentered: StateFlow<Boolean> = @@ -109,15 +106,14 @@ constructor( // To translate elements below smartspace in weather clock to avoid overlapping between date // element in weather clock and aod icons - val isAodIconsVisible: StateFlow<Boolean> = combine(aodNotificationIconViewModel.icons.map { - it.visibleIcons.isNotEmpty() - }, notifsKeyguardInteractor.areNotificationsFullyHidden) { hasIcons, visible -> - hasIcons && visible - }.stateIn( - scope = applicationScope, - started = SharingStarted.WhileSubscribed(), - initialValue = false - ) + val hasAodIcons: StateFlow<Boolean> = + aodNotificationIconViewModel.icons + .map { it.visibleIcons.isNotEmpty() } + .stateIn( + scope = applicationScope, + started = SharingStarted.WhileSubscribed(), + initialValue = false + ) val currentClockLayout: StateFlow<ClockLayout> = combine( diff --git a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java index e7c2a454e16c..bda006915c94 100644 --- a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java +++ b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java @@ -17,6 +17,7 @@ package com.android.systemui.media; import android.annotation.Nullable; +import android.content.ContentProvider; import android.content.ContentResolver; import android.content.Context; import android.content.pm.PackageManager.NameNotFoundException; @@ -123,9 +124,13 @@ public class RingtonePlayer implements CoreStartable { boolean looping, @Nullable VolumeShaper.Configuration volumeShaperConfig) throws RemoteException { if (LOGD) { - Log.d(TAG, "play(token=" + token + ", uri=" + uri + ", uid=" - + Binder.getCallingUid() + ")"); + Log.d(TAG, "play(token=" + token + ", uri=" + uri + + ", uid=" + Binder.getCallingUid() + + ") uriUserId=" + ContentProvider.getUserIdFromUri(uri) + + " callingUserId=" + Binder.getCallingUserHandle().getIdentifier()); } + enforceUriUserId(uri); + Client client; synchronized (mClients) { client = mClients.get(token); @@ -207,6 +212,7 @@ public class RingtonePlayer implements CoreStartable { @Override public String getTitle(Uri uri) { + enforceUriUserId(uri); final UserHandle user = Binder.getCallingUserHandle(); return Ringtone.getTitle(getContextForUser(user), uri, false /*followSettingsUri*/, false /*allowRemote*/); @@ -239,6 +245,25 @@ public class RingtonePlayer implements CoreStartable { } throw new SecurityException("Uri is not ringtone, alarm, or notification: " + uri); } + + /** + * Must be called from the Binder calling thread. + * Ensures caller is from the same userId as the content they're trying to access. + * @param uri the URI to check + * @throws SecurityException when non-system call or userId in uri differs from the + * caller's userId + */ + private void enforceUriUserId(Uri uri) throws SecurityException { + final int uriUserId = ContentProvider.getUserIdFromUri(uri); + final int callerUserId = Binder.getCallingUserHandle().getIdentifier(); + // for a non-system call, verify the URI to play belongs to the same user as the caller + if (UserHandle.isApp(Binder.getCallingUid()) && uriUserId != callerUserId) { + throw new SecurityException("Illegal access to uri=" + uri + + " content associated with user=" + uriUserId + + ", request originates from user=" + callerUserId); + } + } + }; private Context getContextForUser(UserHandle user) { diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java index 846c596a238d..69a157f19ea1 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java @@ -585,7 +585,8 @@ public class MediaControlPanel { /* intentSentUiThreadCallback = */ null, buildLaunchAnimatorController(mMediaViewHolder.getPlayer()), /* fillIntent = */ null, - /* extraOptions = */ null); + /* extraOptions = */ null, + /* customMessage */ null); } else { try { ActivityOptions opts = ActivityOptions.makeBasic(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelFactory.kt index 4c210804b0c0..8c75cf001441 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelFactory.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelFactory.kt @@ -17,6 +17,7 @@ package com.android.systemui.qs.tiles.base.viewmodel import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.dagger.qualifiers.UiBackground import com.android.systemui.plugins.FalsingManager import com.android.systemui.qs.pipeline.shared.TileSpec import com.android.systemui.qs.tiles.base.analytics.QSTileAnalytics @@ -61,6 +62,7 @@ sealed interface QSTileViewModelFactory<T> { private val qsTileConfigProvider: QSTileConfigProvider, private val systemClock: SystemClock, @Background private val backgroundDispatcher: CoroutineDispatcher, + @UiBackground private val uiBackgroundDispatcher: CoroutineDispatcher, private val customTileComponentBuilder: CustomTileComponent.Builder, ) : QSTileViewModelFactory<CustomTileDataModel> { @@ -86,6 +88,7 @@ sealed interface QSTileViewModelFactory<T> { qsTileLogger, systemClock, backgroundDispatcher, + uiBackgroundDispatcher, component.coroutineScope(), ) } @@ -106,6 +109,7 @@ sealed interface QSTileViewModelFactory<T> { private val qsTileConfigProvider: QSTileConfigProvider, private val systemClock: SystemClock, @Background private val backgroundDispatcher: CoroutineDispatcher, + @UiBackground private val uiBackgroundDispatcher: CoroutineDispatcher, private val coroutineScopeFactory: QSTileCoroutineScopeFactory, ) : QSTileViewModelFactory<T> { @@ -136,6 +140,7 @@ sealed interface QSTileViewModelFactory<T> { qsTileLogger, systemClock, backgroundDispatcher, + uiBackgroundDispatcher, coroutineScopeFactory.create(), ) } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt index 8782524cf250..9e84f01c6bc4 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt @@ -40,6 +40,7 @@ import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.cancel +import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow @@ -59,7 +60,9 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.shareIn import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.flow.transformLatest import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext /** * Provides a hassle-free way to implement new tiles according to current System UI architecture @@ -81,6 +84,7 @@ class QSTileViewModelImpl<DATA_TYPE>( private val qsTileLogger: QSTileLogger, private val systemClock: SystemClock, private val backgroundDispatcher: CoroutineDispatcher, + uiBackgroundDispatcher: CoroutineDispatcher, private val tileScope: CoroutineScope, ) : QSTileViewModel, Dumpable { @@ -93,18 +97,16 @@ class QSTileViewModelImpl<DATA_TYPE>( private val tileData: SharedFlow<DATA_TYPE> = createTileDataFlow() - override val state: SharedFlow<QSTileState> = + override val state: StateFlow<QSTileState?> = tileData .map { data -> - mapper().map(config, data).also { state -> - qsTileLogger.logStateUpdate(spec, state, data) - } + withContext(uiBackgroundDispatcher) { mapper().map(config, data) } + .also { state -> qsTileLogger.logStateUpdate(spec, state, data) } } - .flowOn(backgroundDispatcher) - .shareIn( + .stateIn( tileScope, SharingStarted.WhileSubscribed(), - replay = 1, + null, ) override val isAvailable: StateFlow<Boolean> = users @@ -147,26 +149,26 @@ class QSTileViewModelImpl<DATA_TYPE>( private fun createTileDataFlow(): SharedFlow<DATA_TYPE> = users - .flatMapLatest { user -> - val updateTriggers = - merge( - userInputFlow(user), - forceUpdates - .map { DataUpdateTrigger.ForceUpdate } - .onEach { qsTileLogger.logForceUpdate(spec) }, - ) - .onStart { - emit(DataUpdateTrigger.InitialRequest) - qsTileLogger.logInitialRequest(spec) - } - .shareIn(tileScope, SharingStarted.WhileSubscribed()) - tileDataInteractor() - .tileData(user, updateTriggers) - // combine makes sure updateTriggers is always listened even if - // tileDataInteractor#tileData doesn't flatMapLatest on it - .combine(updateTriggers) { data, _ -> data } - .cancellable() - .flowOn(backgroundDispatcher) + .transformLatest { user -> + coroutineScope { + val updateTriggers: Flow<DataUpdateTrigger> = + merge( + userInputFlow(user), + forceUpdates + .map { DataUpdateTrigger.ForceUpdate } + .onEach { qsTileLogger.logForceUpdate(spec) }, + ) + .onStart { qsTileLogger.logInitialRequest(spec) } + .stateIn(this, SharingStarted.Eagerly, DataUpdateTrigger.InitialRequest) + tileDataInteractor() + .tileData(user, updateTriggers) + // combine makes sure updateTriggers is always listened even if + // tileDataInteractor#tileData doesn't transformLatest on it + .combine(updateTriggers) { data, _ -> data } + .cancellable() + .flowOn(backgroundDispatcher) + .collect { emit(it) } + } } .distinctUntilChanged() .shareIn( diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileDataInteractor.kt index 1e8ce588b4e0..233e913a27aa 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileDataInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileDataInteractor.kt @@ -30,11 +30,9 @@ import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.onStart -import kotlinx.coroutines.flow.stateIn /** Observes one qr scanner state changes providing the [QRCodeScannerTileModel]. */ class QRCodeScannerTileDataInteractor @@ -66,11 +64,6 @@ constructor( } .onStart { emit(generateModel()) } .flowOn(bgCoroutineContext) - .stateIn( - scope, - SharingStarted.WhileSubscribed(), - QRCodeScannerTileModel.TemporarilyUnavailable - ) override fun availability(user: UserHandle): Flow<Boolean> = flowOf(qrController.isCameraAvailable) 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 ae6c0143f603..30247c41200f 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 @@ -54,18 +54,21 @@ data class QSTileState( resources: Resources, theme: Theme, config: QSTileUIConfig, - build: Builder.() -> Unit + builder: Builder.() -> Unit ): QSTileState { val iconDrawable = resources.getDrawable(config.iconRes, theme) return build( { Icon.Loaded(iconDrawable, null) }, resources.getString(config.labelRes), - build, + builder, ) } - fun build(icon: () -> Icon?, label: CharSequence, build: Builder.() -> Unit): QSTileState = - Builder(icon, label).apply(build).build() + fun build( + icon: () -> Icon?, + label: CharSequence, + builder: Builder.() -> Unit + ): QSTileState = Builder(icon, label).apply { builder() }.build() } enum class ActivationState(val legacyState: Int) { @@ -107,7 +110,9 @@ data class QSTileState( sealed interface SideViewIcon { data class Custom(val icon: Icon) : SideViewIcon + data object Chevron : SideViewIcon + data object None : SideViewIcon } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt index 226e2fa0549f..b1b0001b6361 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt @@ -17,7 +17,6 @@ package com.android.systemui.qs.tiles.viewmodel import android.os.UserHandle -import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow /** @@ -31,7 +30,7 @@ import kotlinx.coroutines.flow.StateFlow interface QSTileViewModel { /** State of the tile to be shown by the view. */ - val state: SharedFlow<QSTileState> + val state: StateFlow<QSTileState?> val config: QSTileConfig @@ -62,9 +61,7 @@ interface QSTileViewModel { } /** - * Returns the immediate state of the tile or null if the state haven't been collected yet. Favor - * reactive consumption over the [currentState], because there is no guarantee that current value - * would be available at any time. + * Returns the immediate state of the tile or null if the state haven't been collected yet. */ val QSTileViewModel.currentState: QSTileState? - get() = state.replayCache.lastOrNull() + get() = state.value diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt index 7be13e08f972..ba0a8d694a14 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt @@ -38,6 +38,7 @@ import java.util.function.Supplier import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.flow.collectIndexed +import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map @@ -168,6 +169,7 @@ constructor( if (listeningClients.size == 1) { stateJob = qsTileViewModel.state + .filterNotNull() .map { mapState(context, it, qsTileViewModel.config) } .onEach { legacyState -> synchronized(callbacks) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/StubQSTileViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/StubQSTileViewModel.kt index 64f1fd3d6416..00b7e61eb1c6 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/StubQSTileViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/StubQSTileViewModel.kt @@ -17,12 +17,11 @@ package com.android.systemui.qs.tiles.viewmodel import android.os.UserHandle -import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow object StubQSTileViewModel : QSTileViewModel { - override val state: SharedFlow<QSTileState> + override val state: StateFlow<QSTileState?> get() = error("Don't call stubs") override val config: QSTileConfig diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index 23faf7d52fed..4b82e0f96560 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -62,6 +62,7 @@ import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; +import android.os.UserManager; import android.util.Log; import android.view.InputDevice; import android.view.KeyCharacterMap; @@ -177,7 +178,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis private boolean mBound; private boolean mIsEnabled; - private boolean mIsNonPrimaryUser; + private boolean mIsSystemOrVisibleBgUser; private int mCurrentBoundedUserId = -1; private boolean mInputFocusTransferStarted; private float mInputFocusTransferStartY; @@ -629,6 +630,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis SysUiState sysUiState, Provider<SceneInteractor> sceneInteractor, UserTracker userTracker, + UserManager userManager, WakefulnessLifecycle wakefulnessLifecycle, UiEventLogger uiEventLogger, DisplayTracker displayTracker, @@ -639,10 +641,18 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis Optional<UnfoldTransitionProgressForwarder> unfoldTransitionProgressForwarder, BroadcastDispatcher broadcastDispatcher ) { - // b/241601880: This component shouldn't be running for a non-primary user - mIsNonPrimaryUser = !Process.myUserHandle().equals(UserHandle.SYSTEM); - if (mIsNonPrimaryUser) { - Log.wtf(TAG_OPS, "Unexpected initialization for non-primary user", new Throwable()); + // b/241601880: This component should only be running for primary users or + // secondaryUsers when visibleBackgroundUsers are supported. + boolean isSystemUser = Process.myUserHandle().equals(UserHandle.SYSTEM); + boolean isVisibleBackgroundUser = + userManager.isVisibleBackgroundUsersSupported() && !userManager.isUserForeground(); + if (!isSystemUser && isVisibleBackgroundUser) { + Log.d(TAG_OPS, "Initialization for visibleBackgroundUser"); + } + mIsSystemOrVisibleBgUser = isSystemUser || isVisibleBackgroundUser; + if (!mIsSystemOrVisibleBgUser) { + Log.wtf(TAG_OPS, "Unexpected initialization for non-system foreground user", + new Throwable()); } mContext = context; @@ -833,11 +843,12 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis } private void internalConnectToCurrentUser(String reason) { - if (mIsNonPrimaryUser) { + if (!mIsSystemOrVisibleBgUser) { // This should not happen, but if any per-user SysUI component has a dependency on OPS, // then this could get triggered - Log.w(TAG_OPS, "Skipping connection to overview service due to non-primary user " - + "caller"); + Log.w(TAG_OPS, + "Skipping connection to overview service due to non-system foreground user " + + "caller"); return; } disconnectFromLauncherService(reason); diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt index 25a9e9e6f9f2..ef011945b5c8 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt @@ -167,6 +167,10 @@ constructor( initialValue = isVisibleInternal() ) + /** Whether there's an ongoing remotely-initiated user interaction. */ + val isRemoteUserInteractionOngoing: StateFlow<Boolean> = + repository.isRemoteUserInteractionOngoing + /** * The amount of transition into or out of the given [scene]. * diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt index d380251d8b7e..a28222e9cea0 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt @@ -26,17 +26,12 @@ import com.android.systemui.classifier.domain.interactor.FalsingInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.scene.domain.interactor.SceneInteractor -import com.android.systemui.scene.shared.model.Scene import com.android.systemui.scene.shared.model.Scenes -import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated import javax.inject.Inject -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.stateIn /** Models UI state for the scene container. */ @SysUISingleton @@ -46,7 +41,6 @@ constructor( private val sceneInteractor: SceneInteractor, private val falsingInteractor: FalsingInteractor, private val powerInteractor: PowerInteractor, - scenes: Set<@JvmSuppressWildcards Scene>, ) { /** * Keys of all scenes in the container. @@ -62,25 +56,6 @@ constructor( /** Whether the container is visible. */ val isVisible: StateFlow<Boolean> = sceneInteractor.isVisible - private val destinationScenesBySceneKey = - scenes.associate { scene -> - scene.key to scene.destinationScenes.flatMapLatestConflated { replaceSceneFamilies(it) } - } - - fun currentDestinationScenes( - scope: CoroutineScope, - ): StateFlow<Map<UserAction, UserActionResult>> { - return currentScene - .flatMapLatestConflated { currentSceneKey -> - checkNotNull(destinationScenesBySceneKey[currentSceneKey]) - } - .stateIn( - scope = scope, - started = SharingStarted.WhileSubscribed(), - initialValue = emptyMap(), - ) - } - /** * Binds the given flow so the system remembers it. * diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index 16d10abb7d99..6b4e44fed0cd 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -1095,7 +1095,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump initBottomArea(); mWakeUpCoordinator.setStackScroller(mNotificationStackScrollLayoutController); - mPulseExpansionHandler.setUp(mNotificationStackScrollLayoutController); mWakeUpCoordinator.addListener(new NotificationWakeUpCoordinator.WakeUpListener() { @Override public void onFullyHiddenChanged(boolean isFullyHidden) { diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt index ce321dcc03fd..5065baa04623 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt @@ -119,7 +119,7 @@ constructor( if (delayed) { scope.launch { delay(125) - animateCollapseShadeInternal() + withContext(mainDispatcher) { animateCollapseShadeInternal() } } } else { animateCollapseShadeInternal() diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/startable/ShadeStartable.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/startable/ShadeStartable.kt index 3f4bcba288b7..354d379d2ea2 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/domain/startable/ShadeStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/domain/startable/ShadeStartable.kt @@ -32,6 +32,8 @@ import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor import com.android.systemui.shade.shared.flag.DualShade import com.android.systemui.shade.shared.model.ShadeMode import com.android.systemui.shade.transition.ScrimShadeTransitionController +import com.android.systemui.statusbar.PulseExpansionHandler +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController import com.android.systemui.statusbar.policy.SplitShadeStateController import javax.inject.Inject import javax.inject.Provider @@ -56,6 +58,8 @@ constructor( private val sceneInteractorProvider: Provider<SceneInteractor>, private val panelExpansionInteractorProvider: Provider<PanelExpansionInteractor>, private val shadeExpansionStateManager: ShadeExpansionStateManager, + private val pulseExpansionHandler: PulseExpansionHandler, + private val nsslc: NotificationStackScrollLayoutController, ) : CoreStartable { override fun start() { @@ -63,6 +67,7 @@ constructor( hydrateShadeExpansionStateManager() logTouchesTo(touchLog) scrimShadeTransitionController.init() + pulseExpansionHandler.setUp(nsslc) } private fun hydrateShadeExpansionStateManager() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index 2f3fc729464e..c1eb8bcfa493 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -1276,7 +1276,7 @@ public class KeyguardIndicationController { mPowerPluggedInWired = status.isPluggedInWired() && isChargingOrFull; mPowerPluggedInWireless = status.isPluggedInWireless() && isChargingOrFull; mPowerPluggedInDock = status.isPluggedInDock() && isChargingOrFull; - mPowerPluggedIn = status.isPluggedIn() && isChargingOrFull; + mPowerPluggedIn = isPowerPluggedIn(status, isChargingOrFull); mPowerCharged = status.isCharged(); mChargingWattage = status.maxChargingWattage; mChargingSpeed = status.getChargingSpeed(mContext); @@ -1562,6 +1562,11 @@ public class KeyguardIndicationController { return status.isBatteryDefender(); } + /** Return true if the device has power plugged in. */ + protected boolean isPowerPluggedIn(BatteryStatus status, boolean isChargingOrFull) { + return status.isPluggedIn() && isChargingOrFull; + } + private boolean isCurrentUser(int userId) { return getCurrentUser() == userId; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt index fae0a4681493..97266c57ba09 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt @@ -126,6 +126,7 @@ constructor( animationController: ActivityTransitionAnimator.Controller?, fillInIntent: Intent?, extraOptions: Bundle?, + customMessage: String?, ) { activityStarterInternal.startPendingIntentDismissingKeyguard( intent = intent, @@ -135,6 +136,7 @@ constructor( dismissShade = dismissShade, fillInIntent = fillInIntent, extraOptions = extraOptions, + customMessage = customMessage, ) } @@ -319,11 +321,13 @@ constructor( intent: Intent, onlyProvisioned: Boolean, dismissShade: Boolean, + customMessage: String?, ) { activityStarterInternal.startActivityDismissingKeyguard( intent = intent, onlyProvisioned = onlyProvisioned, dismissShade = dismissShade, + customMessage = customMessage, ) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternal.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternal.kt index cff9f5edad08..93ce6e8561ac 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternal.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternal.kt @@ -42,6 +42,7 @@ interface ActivityStarterInternal { skipLockscreenChecks: Boolean = false, fillInIntent: Intent? = null, extraOptions: Bundle? = null, + customMessage: String? = null, ) /** Starts an activity after dismissing keyguard. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt index dbb95e602e43..ae98e1d60589 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt @@ -42,7 +42,8 @@ class ActivityStarterInternalImpl @Inject constructor() : ActivityStarterInterna showOverLockscreen: Boolean, skipLockscreenChecks: Boolean, fillInIntent: Intent?, - extraOptions: Bundle? + extraOptions: Bundle?, + customMessage: String?, ) { TODO("Not yet implemented b/308819693") } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java index 28117e9c9f93..491db307ae63 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -1329,7 +1329,9 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { .putExtra(Intent.EXTRA_TEXT, message.toString()), "Share rejected touch report") .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK), - true /* onlyProvisioned */, true /* dismissShade */); + true /* onlyProvisioned */, + true /* dismissShade */, + null /* customMessage */); }); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt index e96326a945b4..bcb613fe2b8c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt @@ -232,6 +232,7 @@ constructor( skipLockscreenChecks: Boolean, fillInIntent: Intent?, extraOptions: Bundle?, + customMessage: String?, ) { val animationController = if (associatedView is ExpandableNotificationRow) { @@ -340,6 +341,7 @@ constructor( afterKeyguardGone = willLaunchResolverActivity, dismissShade = collapse, willAnimateOnKeyguard = animate, + customMessage = customMessage, ) } } else { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java index 02187844d6da..45aee5b8c22f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java @@ -29,6 +29,7 @@ import android.view.ViewTreeObserver; import android.view.ViewTreeObserver.OnComputeInternalInsetsListener; import android.view.WindowInsets; +import androidx.annotation.VisibleForTesting; import com.android.compose.animation.scene.ObservableTransitionState; import com.android.internal.policy.SystemBarUtils; import com.android.systemui.Dumpable; @@ -69,6 +70,7 @@ public final class StatusBarTouchableRegionManager implements Dumpable { private boolean mIsStatusBarExpanded = false; private boolean mIsIdleOnGone = true; + private boolean mIsRemoteUserInteractionOngoing = false; private boolean mShouldAdjustInsets = false; private View mNotificationShadeWindowView; private View mNotificationPanelView; @@ -133,6 +135,9 @@ public final class StatusBarTouchableRegionManager implements Dumpable { javaAdapter.alwaysCollectFlow( sceneInteractor.get().getTransitionState(), this::onSceneChanged); + javaAdapter.alwaysCollectFlow( + sceneInteractor.get().isRemoteUserInteractionOngoing(), + this::onRemoteUserInteractionOngoingChanged); } else { javaAdapter.alwaysCollectFlow( shadeInteractor.isAnyExpanded(), @@ -179,6 +184,13 @@ public final class StatusBarTouchableRegionManager implements Dumpable { } } + private void onRemoteUserInteractionOngoingChanged(Boolean ongoing) { + if (ongoing != mIsRemoteUserInteractionOngoing) { + mIsRemoteUserInteractionOngoing = ongoing; + updateTouchableRegion(); + } + } + /** * Calculates the touch region needed for heads up notifications, taking into consideration * any existing display cutouts (notch) @@ -276,13 +288,15 @@ public final class StatusBarTouchableRegionManager implements Dumpable { * Helper to let us know when calculating the region is not needed because we know the entire * screen needs to be touchable. */ - private boolean shouldMakeEntireScreenTouchable() { + @VisibleForTesting + boolean shouldMakeEntireScreenTouchable() { // The touchable region is always the full area when expanded, whether we're showing the // shade or the bouncer. It's also fully touchable when the screen off animation is playing // since we don't want stray touches to go through the light reveal scrim to whatever is // underneath. return mIsStatusBarExpanded - || (SceneContainerFlag.isEnabled() && !mIsIdleOnGone) + || (SceneContainerFlag.isEnabled() + && (!mIsIdleOnGone || mIsRemoteUserInteractionOngoing)) || mPrimaryBouncerInteractor.isShowing().getValue() || mAlternateBouncerInteractor.isVisibleState() || mUnlockedScreenOffAnimationController.isAnimationPlaying(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java index 3d8090d70faa..aced0be4cc46 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java @@ -14,6 +14,8 @@ package com.android.systemui.statusbar.phone.fragment; +import static com.android.systemui.statusbar.phone.fragment.StatusBarVisibilityModel.createHiddenModel; + import android.annotation.Nullable; import android.annotation.SuppressLint; import android.app.Fragment; @@ -46,6 +48,7 @@ import com.android.systemui.demomode.DemoModeController; import com.android.systemui.dump.DumpManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.res.R; +import com.android.systemui.scene.shared.flag.SceneContainerFlag; import com.android.systemui.shade.ShadeExpansionStateManager; import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor; import com.android.systemui.statusbar.CommandQueue; @@ -205,6 +208,13 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue private boolean mTransitionFromLockscreenToDreamStarted = false; /** + * True if the current scene allows the home status bar (aka this status bar) to be shown, and + * false if the current scene should never show the home status bar. Only used if the scene + * container is enabled. + */ + private boolean mHomeStatusBarAllowedByScene = true; + + /** * True if there's an active ongoing activity that should be showing a chip and false otherwise. */ private boolean mHasOngoingActivity; @@ -522,6 +532,13 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue mHasOngoingActivity = hasOngoingActivity; updateStatusBarVisibilities(/* animate= */ true); } + + @Override + public void onIsHomeStatusBarAllowedBySceneChanged( + boolean isHomeStatusBarAllowedByScene) { + mHomeStatusBarAllowedByScene = isHomeStatusBarAllowedByScene; + updateStatusBarVisibilities(/* animate= */ true); + } }; @Override @@ -580,17 +597,22 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue boolean headsUpVisible = mStatusBarFragmentComponent.getHeadsUpAppearanceController().shouldBeVisible(); - if (!mKeyguardStateController.isLaunchTransitionFadingAway() - && !mKeyguardStateController.isKeyguardFadingAway() - && shouldHideStatusBar() - && !(mStatusBarStateController.getState() == StatusBarState.KEYGUARD - && headsUpVisible)) { - // Hide everything - return new StatusBarVisibilityModel( - /* showClock= */ false, - /* showNotificationIcons= */ false, - /* showOngoingActivityChip= */ false, - /* showSystemInfo= */ false); + if (SceneContainerFlag.isEnabled()) { + // With the scene container, only use the value calculated by the view model to + // determine if the status bar needs hiding. + if (!mHomeStatusBarAllowedByScene) { + return createHiddenModel(); + } + } else { + // Without the scene container, use our old, mildly-hacky logic to determine if the + // status bar needs hiding. + if (!mKeyguardStateController.isLaunchTransitionFadingAway() + && !mKeyguardStateController.isKeyguardFadingAway() + && shouldHideStatusBar() + && !(mStatusBarStateController.getState() == StatusBarState.KEYGUARD + && headsUpVisible)) { + return createHiddenModel(); + } } boolean showClock = externalModel.getShowClock() && !headsUpVisible; @@ -698,7 +720,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue } private void hideEndSideContent(boolean animate) { - if (!animate) { + if (!animate || !mAnimationsEnabled) { mEndSideAlphaController.setAlpha(/*alpha*/ 0f, SOURCE_OTHER); } else { mEndSideAlphaController.animateToAlpha(/*alpha*/ 0f, SOURCE_OTHER, FADE_OUT_DURATION, @@ -707,7 +729,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue } private void showEndSideContent(boolean animate) { - if (!animate) { + if (!animate || !mAnimationsEnabled) { mEndSideAlphaController.setAlpha(1f, SOURCE_OTHER); return; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarVisibilityModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarVisibilityModel.kt index fe24faece1d3..9255e6323d31 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarVisibilityModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarVisibilityModel.kt @@ -36,6 +36,17 @@ data class StatusBarVisibilityModel( return createModelFromFlags(DISABLE_NONE, DISABLE2_NONE) } + /** Creates a model that hides every piece of the status bar. */ + @JvmStatic + fun createHiddenModel(): StatusBarVisibilityModel { + return StatusBarVisibilityModel( + showClock = false, + showNotificationIcons = false, + showOngoingActivityChip = false, + showSystemInfo = false, + ) + } + /** * Given a set of disabled flags, converts them into the correct visibility statuses. * diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt index 944965950dce..62bd8ad4317c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt @@ -349,6 +349,7 @@ class MobileConnectionRepositoryImpl( false } } + .flowOn(bgDispatcher) .stateIn(scope, SharingStarted.WhileSubscribed(), false) override val carrierId = diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt index de36e407da84..ae1898bc479c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt @@ -30,6 +30,7 @@ import com.android.systemui.common.ui.binder.IconViewBinder import com.android.systemui.dagger.SysUISingleton import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.res.R +import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.statusbar.chips.ui.binder.ChipChronometerBinder import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer @@ -136,6 +137,14 @@ class CollapsedStatusBarViewBinderImpl @Inject constructor() : CollapsedStatusBa } } } + + if (SceneContainerFlag.isEnabled) { + launch { + viewModel.isHomeStatusBarAllowedByScene.collect { + listener.onIsHomeStatusBarAllowedBySceneChanged(it) + } + } + } } } } @@ -259,4 +268,10 @@ interface StatusBarVisibilityChangeListener { /** Called when the status of the ongoing activity chip (active or not active) has changed. */ fun onOngoingActivityStatusChanged(hasOngoingActivity: Boolean) + + /** + * Called when the scene state has changed such that the home status bar is newly allowed or no + * longer allowed. See [CollapsedStatusBarViewModel.isHomeStatusBarAllowedByScene]. + */ + fun onIsHomeStatusBarAllowedBySceneChanged(isHomeStatusBarAllowedByScene: Boolean) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt index 95d924889301..d6c3834c1bf1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt @@ -24,6 +24,9 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED import com.android.systemui.keyguard.shared.model.TransitionState +import com.android.systemui.scene.domain.interactor.SceneContainerOcclusionInteractor +import com.android.systemui.scene.domain.interactor.SceneInteractor +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModel import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor @@ -65,6 +68,12 @@ interface CollapsedStatusBarViewModel { val ongoingActivityChip: StateFlow<OngoingActivityChipModel> /** + * True if the current scene can show the home status bar (aka this status bar), and false if + * the current scene should never show the home status bar. + */ + val isHomeStatusBarAllowedByScene: StateFlow<Boolean> + + /** * Apps can request a low profile mode [android.view.View.SYSTEM_UI_FLAG_LOW_PROFILE] where * status bar and navigation icons dim. In this mode, a notification dot appears where the * notification icons would appear if they would be shown outside of this mode. @@ -83,6 +92,8 @@ constructor( private val lightsOutInteractor: LightsOutInteractor, private val notificationsInteractor: ActiveNotificationsInteractor, keyguardTransitionInteractor: KeyguardTransitionInteractor, + sceneInteractor: SceneInteractor, + sceneContainerOcclusionInteractor: SceneContainerOcclusionInteractor, ongoingActivityChipsViewModel: OngoingActivityChipsViewModel, @Application coroutineScope: CoroutineScope, ) : CollapsedStatusBarViewModel { @@ -99,6 +110,20 @@ constructor( override val ongoingActivityChip = ongoingActivityChipsViewModel.chip + override val isHomeStatusBarAllowedByScene: StateFlow<Boolean> = + combine( + sceneInteractor.currentScene, + sceneContainerOcclusionInteractor.invisibleDueToOcclusion, + ) { currentScene, isOccluded -> + // All scenes have their own status bars, so we should only show the home status bar + // if we're not in a scene. The one exception: If the scene is occluded, then the + // occluding app needs to show the status bar. (Fullscreen apps actually won't show + // the status bar but that's handled with the rest of our fullscreen app logic, + // which lives elsewhere.) + currentScene == Scenes.Gone || isOccluded + } + .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), initialValue = false) + override fun areNotificationsLightsOut(displayId: Int): Flow<Boolean> = if (NotificationsLiveDataStoreRefactor.isUnexpectedlyInLegacyMode()) { emptyFlow() diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/SysUICoroutinesModule.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/SysUICoroutinesModule.kt index d10554f9c254..b836016eb2ef 100644 --- a/packages/SystemUI/src/com/android/systemui/util/kotlin/SysUICoroutinesModule.kt +++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/SysUICoroutinesModule.kt @@ -20,13 +20,16 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Tracing +import com.android.systemui.dagger.qualifiers.UiBackground import dagger.Module import dagger.Provides +import java.util.concurrent.Executor import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.asCoroutineDispatcher import kotlinx.coroutines.newFixedThreadPoolContext import kotlinx.coroutines.plus @@ -83,4 +86,25 @@ class SysUICoroutinesModule { ): CoroutineContext { return bgCoroutineDispatcher + tracingCoroutineContext } + + /** Coroutine dispatcher for background operations on for UI. */ + @Provides + @SysUISingleton + @UiBackground + @Deprecated( + "Use @UiBackground CoroutineContext instead", + ReplaceWith("uiBgCoroutineContext()", "kotlin.coroutines.CoroutineContext") + ) + fun uiBgDispatcher(@UiBackground uiBgExecutor: Executor): CoroutineDispatcher = + uiBgExecutor.asCoroutineDispatcher() + + @Provides + @UiBackground + @SysUISingleton + fun uiBgCoroutineContext( + @Tracing tracingCoroutineContext: CoroutineContext, + @UiBackground uiBgCoroutineDispatcher: CoroutineDispatcher, + ): CoroutineContext { + return uiBgCoroutineDispatcher + tracingCoroutineContext + } } diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java index cb61534b2255..8457bdb2d0ff 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java @@ -51,7 +51,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.flags.FeatureFlags; diff --git a/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java index 64c162f3d983..639b53bfb95b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java @@ -46,6 +46,7 @@ import android.view.WindowMetrics; import androidx.annotation.NonNull; import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleCoroutineScope; import androidx.lifecycle.LifecycleObserver; import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.LifecycleRegistry; @@ -159,7 +160,9 @@ public class TouchMonitorTest extends SysuiTestCase { ArgumentCaptor<LifecycleObserver> observerCaptor = ArgumentCaptor.forClass(LifecycleObserver.class); verify(mLifecycleRegistry, atLeast(1)).addObserver(observerCaptor.capture()); - mLifecycleObservers.addAll(observerCaptor.getAllValues()); + mLifecycleObservers.addAll(observerCaptor.getAllValues().stream().filter( + lifecycleObserver -> !(lifecycleObserver instanceof LifecycleCoroutineScope)) + .toList()); updateLifecycle(Lifecycle.State.RESUMED); diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt index a77072217873..fbfe41f57927 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt @@ -1799,6 +1799,7 @@ public class MediaControlPanelTest : SysuiTestCase() { any(), eq(null), eq(null), + eq(null), ) verify(activityStarter, never()).postStartActivityDismissingKeyguard(eq(clickIntent), any()) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImplTest.kt index 42b81de92af7..c918ed82604c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImplTest.kt @@ -97,6 +97,7 @@ class QSTileViewModelImplTest : SysuiTestCase() { qsTileLogger, FakeSystemClock(), testCoroutineDispatcher, + testCoroutineDispatcher, testScope.backgroundScope, ) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt index 6e6e31177564..e1c39117f6c8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt @@ -23,6 +23,7 @@ import android.content.pm.ResolveInfo import android.os.PowerManager import android.os.Process import android.os.UserHandle +import android.os.UserManager import android.testing.TestableContext import android.testing.TestableLooper import androidx.test.ext.junit.runners.AndroidJUnit4 @@ -108,6 +109,7 @@ class OverviewProxyServiceTest : SysuiTestCase() { @Mock private lateinit var navModeController: NavigationModeController @Mock private lateinit var statusBarWinController: NotificationShadeWindowController @Mock private lateinit var userTracker: UserTracker + @Mock private lateinit var userManager: UserManager @Mock private lateinit var uiEventLogger: UiEventLogger @Mock private lateinit var sysuiUnlockAnimationController: KeyguardUnlockAnimationController @Mock @@ -199,11 +201,12 @@ class OverviewProxyServiceTest : SysuiTestCase() { } @Test - fun connectToOverviewService_primaryUser_expectBindService() { + fun connectToOverviewService_primaryUserNoVisibleBgUsersSupported_expectBindService() { val mockitoSession = ExtendedMockito.mockitoSession().spyStatic(Process::class.java).startMocking() try { `when`(Process.myUserHandle()).thenReturn(UserHandle.SYSTEM) + `when`(userManager.isVisibleBackgroundUsersSupported()).thenReturn(false) val spyContext = spy(context) val ops = createOverviewProxyService(spyContext) ops.startConnectionToCurrentUser() @@ -214,11 +217,46 @@ class OverviewProxyServiceTest : SysuiTestCase() { } @Test - fun connectToOverviewService_nonPrimaryUser_expectNoBindService() { + fun connectToOverviewService_nonPrimaryUserNoVisibleBgUsersSupported_expectNoBindService() { val mockitoSession = ExtendedMockito.mockitoSession().spyStatic(Process::class.java).startMocking() try { `when`(Process.myUserHandle()).thenReturn(UserHandle.of(12345)) + `when`(userManager.isVisibleBackgroundUsersSupported()).thenReturn(false) + val spyContext = spy(context) + val ops = createOverviewProxyService(spyContext) + ops.startConnectionToCurrentUser() + verify(spyContext, times(0)).bindServiceAsUser(any(), any(), anyInt(), any()) + } finally { + mockitoSession.finishMocking() + } + } + + @Test + fun connectToOverviewService_nonPrimaryBgUserVisibleBgUsersSupported_expectBindService() { + val mockitoSession = + ExtendedMockito.mockitoSession().spyStatic(Process::class.java).startMocking() + try { + `when`(Process.myUserHandle()).thenReturn(UserHandle.of(12345)) + `when`(userManager.isVisibleBackgroundUsersSupported()).thenReturn(true) + `when`(userManager.isUserForeground()).thenReturn(false) + val spyContext = spy(context) + val ops = createOverviewProxyService(spyContext) + ops.startConnectionToCurrentUser() + verify(spyContext, atLeast(1)).bindServiceAsUser(any(), any(), anyInt(), any()) + } finally { + mockitoSession.finishMocking() + } + } + + @Test + fun connectToOverviewService_nonPrimaryFgUserVisibleBgUsersSupported_expectNoBindService() { + val mockitoSession = + ExtendedMockito.mockitoSession().spyStatic(Process::class.java).startMocking() + try { + `when`(Process.myUserHandle()).thenReturn(UserHandle.of(12345)) + `when`(userManager.isVisibleBackgroundUsersSupported()).thenReturn(true) + `when`(userManager.isUserForeground()).thenReturn(true) val spyContext = spy(context) val ops = createOverviewProxyService(spyContext) ops.startConnectionToCurrentUser() @@ -242,6 +280,7 @@ class OverviewProxyServiceTest : SysuiTestCase() { sysUiState, mock(), userTracker, + userManager, wakefulnessLifecycle, uiEventLogger, displayTracker, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManagerTest.kt new file mode 100644 index 000000000000..230ddf9d25db --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManagerTest.kt @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.phone + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.compose.animation.scene.ObservableTransitionState +import com.android.systemui.SysuiTestCase +import com.android.systemui.flags.DisableSceneContainer +import com.android.systemui.flags.EnableSceneContainer +import com.android.systemui.kosmos.testScope +import com.android.systemui.scene.data.repository.sceneContainerRepository +import com.android.systemui.scene.shared.model.Scenes +import com.android.systemui.testKosmos +import com.android.systemui.util.kotlin.getValue +import com.google.common.truth.Truth.assertThat +import dagger.Lazy +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.flowOf +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 StatusBarTouchableRegionManagerTest : SysuiTestCase() { + private val kosmos = testKosmos() + private val testScope = kosmos.testScope + private val sceneRepository = kosmos.sceneContainerRepository + + private val underTest by Lazy { kosmos.statusBarTouchableRegionManager } + + @Test + @EnableSceneContainer + fun entireScreenTouchable_sceneContainerEnabled_isRemoteUserInteractionOngoing() = + testScope.runTest { + sceneRepository.setTransitionState( + flowOf(ObservableTransitionState.Idle(currentScene = Scenes.Gone)) + ) + runCurrent() + assertThat(underTest.shouldMakeEntireScreenTouchable()).isFalse() + + sceneRepository.isRemoteUserInteractionOngoing.value = true + runCurrent() + assertThat(underTest.shouldMakeEntireScreenTouchable()).isTrue() + + sceneRepository.isRemoteUserInteractionOngoing.value = false + runCurrent() + assertThat(underTest.shouldMakeEntireScreenTouchable()).isFalse() + } + + @Test + @DisableSceneContainer + fun entireScreenTouchable_sceneContainerDisabled_isRemoteUserInteractionOngoing() = + testScope.runTest { + assertThat(underTest.shouldMakeEntireScreenTouchable()).isFalse() + + sceneRepository.isRemoteUserInteractionOngoing.value = true + runCurrent() + + assertThat(underTest.shouldMakeEntireScreenTouchable()).isFalse() + } + + @Test + @EnableSceneContainer + fun entireScreenTouchable_sceneContainerEnabled_isIdleOnGone() = + testScope.runTest { + sceneRepository.setTransitionState( + flowOf(ObservableTransitionState.Idle(currentScene = Scenes.Gone)) + ) + runCurrent() + assertThat(underTest.shouldMakeEntireScreenTouchable()).isFalse() + + sceneRepository.setTransitionState( + flowOf(ObservableTransitionState.Idle(currentScene = Scenes.Shade)) + ) + runCurrent() + assertThat(underTest.shouldMakeEntireScreenTouchable()).isTrue() + + sceneRepository.setTransitionState( + flowOf(ObservableTransitionState.Idle(currentScene = Scenes.Gone)) + ) + runCurrent() + assertThat(underTest.shouldMakeEntireScreenTouchable()).isFalse() + } + + @Test + @DisableSceneContainer + fun entireScreenTouchable_sceneContainerDisabled_isIdleOnGone() = + testScope.runTest { + assertThat(underTest.shouldMakeEntireScreenTouchable()).isFalse() + + sceneRepository.setTransitionState( + flowOf(ObservableTransitionState.Idle(currentScene = Scenes.Shade)) + ) + runCurrent() + + assertThat(underTest.shouldMakeEntireScreenTouchable()).isFalse() + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java index ee27cea48565..01540e7584a3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java @@ -49,6 +49,8 @@ import com.android.systemui.SysuiBaseFragmentTest; import com.android.systemui.animation.AnimatorTestRule; import com.android.systemui.demomode.DemoModeController; import com.android.systemui.dump.DumpManager; +import com.android.systemui.flags.DisableSceneContainer; +import com.android.systemui.flags.EnableSceneContainer; import com.android.systemui.log.LogBuffer; import com.android.systemui.log.LogcatEchoTracker; import com.android.systemui.plugins.DarkIconDispatcher; @@ -302,6 +304,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test + @DisableSceneContainer public void disable_shadeOpenAndShouldHide_everythingHidden() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); @@ -318,6 +321,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test + @DisableSceneContainer public void disable_shadeOpenButNotShouldHide_everythingShown() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); @@ -335,6 +339,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { /** Regression test for b/279790651. */ @Test + @DisableSceneContainer public void disable_shadeOpenAndShouldHide_thenShadeNotOpenAndDozingUpdate_everythingShown() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); @@ -376,6 +381,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test + @DisableSceneContainer public void disable_isTransitioningToOccluded_everythingHidden() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); @@ -390,6 +396,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test + @DisableSceneContainer public void disable_wasTransitioningToOccluded_transitionFinished_everythingShown() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); @@ -674,6 +681,80 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test + @EnableSceneContainer + public void isHomeStatusBarAllowedByScene_false_everythingHidden() { + resumeAndGetFragment(); + + mCollapsedStatusBarViewBinder.getListener().onIsHomeStatusBarAllowedBySceneChanged(false); + + // THEN all views are hidden + assertEquals(View.GONE, getClockView().getVisibility()); + assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility()); + assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility()); + } + + @Test + @EnableSceneContainer + public void isHomeStatusBarAllowedByScene_true_everythingShown() { + resumeAndGetFragment(); + + mCollapsedStatusBarViewBinder.getListener().onIsHomeStatusBarAllowedBySceneChanged(true); + + // THEN all views are shown + assertEquals(View.VISIBLE, getClockView().getVisibility()); + assertEquals(View.VISIBLE, getNotificationAreaView().getVisibility()); + assertEquals(View.VISIBLE, getEndSideContentView().getVisibility()); + } + + @Test + @EnableSceneContainer + public void disable_isHomeStatusBarAllowedBySceneFalse_disableValuesIgnored() { + CollapsedStatusBarFragment fragment = resumeAndGetFragment(); + + // WHEN the scene doesn't allow the status bar + mCollapsedStatusBarViewBinder.getListener().onIsHomeStatusBarAllowedBySceneChanged(false); + + // BUT the disable flags want to show the status bar + fragment.disable(DEFAULT_DISPLAY, 0, 0, false); + + // THEN all views are hidden (the disable flags aren't respected) + assertEquals(View.GONE, getClockView().getVisibility()); + assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility()); + assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility()); + } + + @Test + @EnableSceneContainer + public void disable_isHomeStatusBarAllowedBySceneTrue_disableValuesUsed() { + CollapsedStatusBarFragment fragment = resumeAndGetFragment(); + + // WHEN the scene does allow the status bar + mCollapsedStatusBarViewBinder.getListener().onIsHomeStatusBarAllowedBySceneChanged(true); + + // AND the disable flags want to hide the clock + fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_CLOCK, 0, false); + + // THEN all views are shown except the clock (the disable flags are used) + assertEquals(View.GONE, getClockView().getVisibility()); + assertEquals(View.VISIBLE, getNotificationAreaView().getVisibility()); + assertEquals(View.VISIBLE, getEndSideContentView().getVisibility()); + } + + @Test + @DisableSceneContainer + public void isHomeStatusBarAllowedByScene_sceneContainerDisabled_valueNotUsed() { + resumeAndGetFragment(); + + // Even if the scene says to hide the home status bar + mCollapsedStatusBarViewBinder.getListener().onIsHomeStatusBarAllowedBySceneChanged(false); + + // The value isn't used because the scene container flag is disabled, so all views are shown + assertEquals(View.VISIBLE, getClockView().getVisibility()); + assertEquals(View.VISIBLE, getNotificationAreaView().getVisibility()); + assertEquals(View.VISIBLE, getEndSideContentView().getVisibility()); + } + + @Test public void disable_isDozing_clockAndSystemInfoVisible() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); when(mStatusBarStateController.isDozing()).thenReturn(true); @@ -758,6 +839,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test + @DisableSceneContainer public void testStatusBarIcons_hiddenThroughoutCameraLaunch() { final CollapsedStatusBarFragment fragment = resumeAndGetFragment(); @@ -779,6 +861,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test + @DisableSceneContainer public void testStatusBarIcons_hiddenThroughoutLockscreenToDreamTransition() { final CollapsedStatusBarFragment fragment = resumeAndGetFragment(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt index b4c6106150b2..94159bcebf47 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt @@ -24,6 +24,7 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.coroutines.collectValues import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository +import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionState @@ -36,6 +37,10 @@ import com.android.systemui.kosmos.testScope import com.android.systemui.log.assertLogsWtf import com.android.systemui.mediaprojection.data.model.MediaProjectionState import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository +import com.android.systemui.scene.data.repository.sceneContainerRepository +import com.android.systemui.scene.domain.interactor.sceneContainerOcclusionInteractor +import com.android.systemui.scene.domain.interactor.sceneInteractor +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.screenrecord.data.model.ScreenRecordModel import com.android.systemui.screenrecord.data.repository.screenRecordRepository import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.NORMAL_PACKAGE @@ -84,6 +89,8 @@ class CollapsedStatusBarViewModelImplTest : SysuiTestCase() { kosmos.lightsOutInteractor, kosmos.activeNotificationsInteractor, kosmos.keyguardTransitionInteractor, + kosmos.sceneInteractor, + kosmos.sceneContainerOcclusionInteractor, kosmos.ongoingActivityChipsViewModel, kosmos.applicationCoroutineScope, ) @@ -426,6 +433,68 @@ class CollapsedStatusBarViewModelImplTest : SysuiTestCase() { assertIsShareToAppChip(latest) } + @Test + fun isHomeStatusBarAllowedByScene_sceneLockscreen_notOccluded_false() = + testScope.runTest { + val latest by collectLastValue(underTest.isHomeStatusBarAllowedByScene) + + kosmos.sceneContainerRepository.snapToScene(Scenes.Lockscreen) + kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(false, taskInfo = null) + + assertThat(latest).isFalse() + } + + @Test + fun isHomeStatusBarAllowedByScene_sceneLockscreen_occluded_true() = + testScope.runTest { + val latest by collectLastValue(underTest.isHomeStatusBarAllowedByScene) + + kosmos.sceneContainerRepository.snapToScene(Scenes.Lockscreen) + kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(true, taskInfo = null) + + assertThat(latest).isTrue() + } + + @Test + fun isHomeStatusBarAllowedByScene_sceneBouncer_false() = + testScope.runTest { + val latest by collectLastValue(underTest.isHomeStatusBarAllowedByScene) + + kosmos.sceneContainerRepository.snapToScene(Scenes.Bouncer) + + assertThat(latest).isFalse() + } + + @Test + fun isHomeStatusBarAllowedByScene_sceneCommunal_false() = + testScope.runTest { + val latest by collectLastValue(underTest.isHomeStatusBarAllowedByScene) + + kosmos.sceneContainerRepository.snapToScene(Scenes.Communal) + + assertThat(latest).isFalse() + } + + @Test + fun isHomeStatusBarAllowedByScene_sceneShade_false() = + testScope.runTest { + val latest by collectLastValue(underTest.isHomeStatusBarAllowedByScene) + + kosmos.sceneContainerRepository.snapToScene(Scenes.Shade) + + assertThat(latest).isFalse() + } + + @Test + fun isHomeStatusBarAllowedByScene_sceneGone_true() = + testScope.runTest { + val latest by collectLastValue(underTest.isHomeStatusBarAllowedByScene) + + kosmos.sceneContainerRepository.snapToScene(Scenes.Gone) + + assertThat(latest).isTrue() + } + private fun activeNotificationsStore(notifications: List<ActiveNotificationModel>) = ActiveNotificationsStore.Builder() .apply { notifications.forEach(::addIndividualNotif) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewModel.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewModel.kt index b66eed05fa0d..d3f11253fc09 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewModel.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewModel.kt @@ -31,6 +31,8 @@ class FakeCollapsedStatusBarViewModel : CollapsedStatusBarViewModel { override val ongoingActivityChip: MutableStateFlow<OngoingActivityChipModel> = MutableStateFlow(OngoingActivityChipModel.Hidden) + override val isHomeStatusBarAllowedByScene = MutableStateFlow(false) + override fun areNotificationsLightsOut(displayId: Int): Flow<Boolean> = areNotificationLightsOut fun setNotificationLightsOut(lightsOut: Boolean) { diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java index 78d8abeb477a..a124b34cde85 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java @@ -43,7 +43,7 @@ import androidx.core.animation.AndroidXAnimatorIsolationRule; import androidx.test.InstrumentationRegistry; import androidx.test.uiautomator.UiDevice; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.systemui.broadcast.FakeBroadcastDispatcher; import com.android.systemui.flags.SceneContainerRule; diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelKosmos.kt index 3c62b44ed2c4..b5e6f75c7915 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelKosmos.kt @@ -23,7 +23,6 @@ import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.shade.domain.interactor.shadeInteractor import com.android.systemui.statusbar.notification.icon.ui.viewmodel.notificationIconContainerAlwaysOnDisplayViewModel -import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationsKeyguardInteractor import com.android.systemui.statusbar.ui.systemBarUtilsProxy val Kosmos.keyguardClockViewModel by @@ -32,7 +31,6 @@ val Kosmos.keyguardClockViewModel by keyguardClockInteractor = keyguardClockInteractor, applicationScope = applicationCoroutineScope, aodNotificationIconViewModel = notificationIconContainerAlwaysOnDisplayViewModel, - notifsKeyguardInteractor = notificationsKeyguardInteractor, shadeInteractor = shadeInteractor, systemBarUtils = systemBarUtilsProxy, configurationInteractor = configurationInteractor, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/di/NewQSTileFactoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/di/NewQSTileFactoryKosmos.kt index 419e7810b604..dceb8bff0ae7 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/di/NewQSTileFactoryKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/di/NewQSTileFactoryKosmos.kt @@ -33,7 +33,6 @@ import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import javax.inject.Provider import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow var Kosmos.newFactoryTileMap by Kosmos.Fixture { emptyMap<String, Provider<QSTileViewModel>>() } @@ -50,7 +49,7 @@ val Kosmos.customTileViewModelFactory: QSTileViewModelFactory.Component by instanceIdSequenceFake.newInstanceId(), ) object : QSTileViewModel { - override val state: SharedFlow<QSTileState> = + override val state: StateFlow<QSTileState?> = MutableStateFlow(QSTileState.build({ null }, tileSpec.spec) {}) override val config: QSTileConfig = config override val isAvailable: StateFlow<Boolean> = MutableStateFlow(true) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/qr/QRCodeScannerTileKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/qr/QRCodeScannerTileKosmos.kt index dcfcce77942e..537be4fc2527 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/qr/QRCodeScannerTileKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/qr/QRCodeScannerTileKosmos.kt @@ -69,6 +69,7 @@ val Kosmos.qsQRCodeScannerViewModel by qsTileLogger, systemClock, testDispatcher, + testDispatcher, testScope.backgroundScope, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/startable/ShadeStartableKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/startable/ShadeStartableKosmos.kt index b85858d915b5..79b80bc71c58 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/startable/ShadeStartableKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/startable/ShadeStartableKosmos.kt @@ -27,7 +27,9 @@ import com.android.systemui.shade.ShadeExpansionStateManager import com.android.systemui.shade.data.repository.shadeRepository import com.android.systemui.shade.domain.interactor.panelExpansionInteractor import com.android.systemui.shade.transition.ScrimShadeTransitionController +import com.android.systemui.statusbar.notification.stack.notificationStackScrollLayoutController import com.android.systemui.statusbar.policy.splitShadeStateController +import com.android.systemui.statusbar.pulseExpansionHandler import com.android.systemui.util.mockito.mock @Deprecated("ShadeExpansionStateManager is deprecated. Remove your dependency on it instead.") @@ -45,5 +47,7 @@ val Kosmos.shadeStartable by Fixture { sceneInteractorProvider = { sceneInteractor }, panelExpansionInteractorProvider = { panelExpansionInteractor }, shadeExpansionStateManager = shadeExpansionStateManager, + pulseExpansionHandler = pulseExpansionHandler, + nsslc = notificationStackScrollLayoutController, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManagerKosmos.kt new file mode 100644 index 000000000000..8785256de452 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManagerKosmos.kt @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.phone + +import android.content.applicationContext +import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor +import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.testScope +import com.android.systemui.scene.domain.interactor.sceneInteractor +import com.android.systemui.shade.domain.interactor.shadeInteractor +import com.android.systemui.statusbar.notificationShadeWindowController +import com.android.systemui.statusbar.policy.configurationController +import com.android.systemui.statusbar.policy.headsUpManager +import com.android.systemui.util.kotlin.JavaAdapter +import com.android.systemui.util.mockito.mock +import org.mockito.Mockito.mock + +var Kosmos.statusBarTouchableRegionManager by + Kosmos.Fixture { + StatusBarTouchableRegionManager( + applicationContext, + notificationShadeWindowController, + configurationController, + headsUpManager, + shadeInteractor, + { sceneInteractor }, + JavaAdapter(testScope.backgroundScope), + mock<UnlockedScreenOffAnimationController>(), + primaryBouncerInteractor, + alternateBouncerInteractor, + ) + } diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt index fec6ff17a608..0458f535c1eb 100644 --- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt @@ -28,6 +28,9 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import java.util.concurrent.CopyOnWriteArrayList +import java.util.concurrent.atomic.AtomicInteger + +private const val INVALID_ROTATION = -1 /** * Allows to subscribe to rotation changes. Updates are provided for the display associated to @@ -45,7 +48,7 @@ constructor( private val listeners = CopyOnWriteArrayList<RotationListener>() private val displayListener = RotationDisplayListener() - private var lastRotation: Int? = null + private val lastRotation = AtomicInteger(INVALID_ROTATION) override fun addCallback(listener: RotationListener) { bgHandler.post { @@ -61,7 +64,7 @@ constructor( listeners -= listener if (listeners.isEmpty()) { unsubscribeToRotation() - lastRotation = null + lastRotation.set(INVALID_ROTATION) } } } @@ -100,9 +103,8 @@ constructor( if (displayId == display.displayId) { val currentRotation = display.rotation - if (lastRotation == null || lastRotation != currentRotation) { + if (lastRotation.compareAndSet(lastRotation.get(), currentRotation)) { listeners.forEach { it.onRotationChanged(currentRotation) } - lastRotation = currentRotation } } } finally { diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java index f9196f3e0e0e..d3efa21a2311 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java @@ -610,7 +610,7 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo } } - void notifyAccessibilityButtonClicked(int displayId) { + void notifyMagnificationShortcutTriggered(int displayId) { if (mMagnificationGestureHandler.size() != 0) { final MagnificationGestureHandler handler = mMagnificationGestureHandler.get(displayId); if (handler != null) { diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 1dc3fb421545..36d97f659f6c 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -2220,10 +2220,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } - private void sendAccessibilityButtonToInputFilter(int displayId) { + private void notifyMagnificationShortcutTriggered(int displayId) { synchronized (mLock) { if (mHasInputFilter && mInputFilter != null) { - mInputFilter.notifyAccessibilityButtonClicked(displayId); + mInputFilter.notifyMagnificationShortcutTriggered(displayId); } } } @@ -3898,7 +3898,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub .isActivated(displayId); logAccessibilityShortcutActivated(mContext, MAGNIFICATION_COMPONENT_NAME, shortcutType, enabled); - sendAccessibilityButtonToInputFilter(displayId); + notifyMagnificationShortcutTriggered(displayId); return; } final ComponentName targetComponentName = ComponentName.unflattenFromString(targetName); diff --git a/services/autofill/java/com/android/server/autofill/RequestId.java b/services/autofill/java/com/android/server/autofill/RequestId.java index 29ad786dbd4b..d8069a840156 100644 --- a/services/autofill/java/com/android/server/autofill/RequestId.java +++ b/services/autofill/java/com/android/server/autofill/RequestId.java @@ -16,8 +16,14 @@ package com.android.server.autofill; -import java.util.List; +import static com.android.server.autofill.Helper.sDebug; + +import android.util.Slog; +import android.util.SparseArray; + import java.util.concurrent.atomic.AtomicInteger; +import java.util.List; +import java.util.Random; // Helper class containing various methods to deal with FillRequest Ids. // For authentication flows, there needs to be a way to know whether to retrieve the Fill @@ -25,56 +31,97 @@ import java.util.concurrent.atomic.AtomicInteger; // way to achieve this is by assigning odd number request ids to secondary provider and // even numbers to primary provider. public class RequestId { + private AtomicInteger sIdCounter; + + // The minimum request id is 2 to avoid possible authentication issues. + static final int MIN_REQUEST_ID = 2; + // The maximum request id is 0x7FFF to make sure the 16th bit is 0. + // This is to make sure the authentication id is always positive. + static final int MAX_REQUEST_ID = 0x7FFF; // 32767 + + // The maximum start id is made small to best avoid wrapping around. + static final int MAX_START_ID = 1000; + // The magic number is used to determine if a wrap has happened. + // The underlying assumption of MAGIC_NUMBER is that there can't be as many as MAGIC_NUMBER + // of fill requests in one session. so there can't be as many as MAGIC_NUMBER of fill requests + // getting dropped. + static final int MAGIC_NUMBER = 5000; + + static final int MIN_PRIMARY_REQUEST_ID = 2; + static final int MAX_PRIMARY_REQUEST_ID = 0x7FFE; // 32766 + + static final int MIN_SECONDARY_REQUEST_ID = 3; + static final int MAX_SECONDARY_REQUEST_ID = 0x7FFF; // 32767 + + private static final String TAG = "RequestId"; + + // WARNING: This constructor should only be used for testing + RequestId(int startId) { + if (startId < MIN_REQUEST_ID || startId > MAX_REQUEST_ID) { + throw new IllegalArgumentException("startId must be between " + MIN_REQUEST_ID + + " and " + MAX_REQUEST_ID); + } + if (sDebug) { + Slog.d(TAG, "RequestId(int): startId= " + startId); + } + sIdCounter = new AtomicInteger(startId); + } - private AtomicInteger sIdCounter; - - // Mainly used for tests - RequestId(int start) { - sIdCounter = new AtomicInteger(start); - } - - public RequestId() { - this((int) (Math.floor(Math.random() * 0xFFFF))); - } - - public static int getLastRequestIdIndex(List<Integer> requestIds) { - int lastId = -1; - int indexOfBiggest = -1; - // Biggest number is usually the latest request, since IDs only increase - // The only exception is when the request ID wraps around back to 0 - for (int i = requestIds.size() - 1; i >= 0; i--) { - if (requestIds.get(i) > lastId) { - lastId = requestIds.get(i); - indexOfBiggest = i; - } + // WARNING: This get method should only be used for testing + int getRequestId() { + return sIdCounter.get(); } - // 0xFFFE + 2 == 0x1 (for secondary) - // 0xFFFD + 2 == 0x0 (for primary) - // Wrap has occurred - if (lastId >= 0xFFFD) { - // Calculate the biggest size possible - // If list only has one kind of request ids - we need to multiple by 2 - // (since they skip odd ints) - // Also subtract one from size because at least one integer exists pre-wrap - int calcSize = (requestIds.size()) * 2; - //Biggest possible id after wrapping - int biggestPossible = (lastId + calcSize) % 0xFFFF; - lastId = -1; - indexOfBiggest = -1; - for (int i = 0; i < requestIds.size(); i++) { - int currentId = requestIds.get(i); - if (currentId <= biggestPossible && currentId > lastId) { - lastId = currentId; - indexOfBiggest = i; + public RequestId() { + Random random = new Random(); + int low = MIN_REQUEST_ID; + int high = MAX_START_ID + 1; // nextInt is exclusive on upper limit + + // Generate a random start request id that >= MIN_REQUEST_ID and <= MAX_START_ID + int startId = random.nextInt(high - low) + low; + if (sDebug) { + Slog.d(TAG, "RequestId(): startId= " + startId); } - } + sIdCounter = new AtomicInteger(startId); } - return indexOfBiggest; - } + // Given a list of request ids, find the index of the last request id. + // Note: Since the request id wraps around, the largest request id may not be + // the latest request id. + // + // @param requestIds List of request ids in ascending order with at least one element. + // @return Index of the last request id. + public static int getLastRequestIdIndex(List<Integer> requestIds) { + // If there is only one request id, return index as 0. + if (requestIds.size() == 1) { + return 0; + } + + // We have to use a magical number to determine if a wrap has happened because + // the request id could be lost. The underlying assumption of MAGIC_NUMBER is that + // there can't be as many as MAGIC_NUMBER of fill requests in one session. + boolean wrapHasHappened = false; + int latestRequestIdIndex = -1; + + for (int i = 0; i < requestIds.size() - 1; i++) { + if (requestIds.get(i+1) - requestIds.get(i) > MAGIC_NUMBER) { + wrapHasHappened = true; + latestRequestIdIndex = i; + break; + } + } + + // If there was no wrap, the last request index is the last index. + if (!wrapHasHappened) { + latestRequestIdIndex = requestIds.size() - 1; + } + if (sDebug) { + Slog.d(TAG, "getLastRequestIdIndex(): latestRequestIdIndex = " + latestRequestIdIndex); + } + return latestRequestIdIndex; + } - public int nextId(boolean isSecondary) { + public int nextId(boolean isSecondary) { // For authentication flows, there needs to be a way to know whether to retrieve the Fill // Response from the primary provider or the secondary provider from the requestId. A simple // way to achieve this is by assigning odd number request ids to secondary provider and @@ -82,13 +129,20 @@ public class RequestId { int requestId; do { - requestId = sIdCounter.incrementAndGet() % 0xFFFF; + requestId = sIdCounter.incrementAndGet() % (MAX_REQUEST_ID + 1); + // Skip numbers smaller than MIN_REQUEST_ID to avoid possible authentication issue + if (requestId < MIN_REQUEST_ID) { + requestId = MIN_REQUEST_ID; + } sIdCounter.set(requestId); } while (isSecondaryProvider(requestId) != isSecondary); + if (sDebug) { + Slog.d(TAG, "nextId(): requestId = " + requestId); + } return requestId; - } + } - public static boolean isSecondaryProvider(int requestId) { - return requestId % 2 == 1; - } + public static boolean isSecondaryProvider(int requestId) { + return requestId % 2 == 1; + } } diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 494e956c413f..c6ddc16211bc 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -6902,17 +6902,18 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return mPendingSaveUi != null && mPendingSaveUi.getState() == PendingUi.STATE_PENDING; } + // Return latest response index in mResponses SparseArray. @GuardedBy("mLock") private int getLastResponseIndexLocked() { - if (mResponses != null) { - List<Integer> requestIdList = new ArrayList<>(); - final int responseCount = mResponses.size(); - for (int i = 0; i < responseCount; i++) { - requestIdList.add(mResponses.keyAt(i)); - } - return mRequestId.getLastRequestIdIndex(requestIdList); + if (mResponses == null || mResponses.size() == 0) { + return -1; + } + List<Integer> requestIdList = new ArrayList<>(); + final int responseCount = mResponses.size(); + for (int i = 0; i < responseCount; i++) { + requestIdList.add(mResponses.keyAt(i)); } - return -1; + return mRequestId.getLastRequestIdIndex(requestIdList); } private LogMaker newLogMaker(int category) { diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java index 3c323f9ce69c..657e261a345f 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java @@ -24,6 +24,7 @@ import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_DEFAUL import static android.companion.virtual.VirtualDeviceParams.NAVIGATION_POLICY_DEFAULT_ALLOWED; import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_ACTIVITY; import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_AUDIO; +import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_BLOCKED_ACTIVITY_BEHAVIOR; import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_CAMERA; import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_CLIPBOARD; import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_RECENTS; @@ -720,6 +721,13 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub } } break; + case POLICY_TYPE_BLOCKED_ACTIVITY_BEHAVIOR: + if (android.companion.virtualdevice.flags.Flags.activityControlApi()) { + synchronized (mVirtualDeviceLock) { + mDevicePolicies.put(policyType, devicePolicy); + } + } + break; default: throw new IllegalArgumentException("Device policy " + policyType + " cannot be changed at runtime. "); @@ -1321,14 +1329,15 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) private void onActivityBlocked(int displayId, ActivityInfo activityInfo) { Intent intent = BlockedAppStreamingActivity.createIntent(activityInfo, getDisplayName()); - if (!android.companion.virtualdevice.flags.Flags.activityControlApi() - || !Objects.equals(activityInfo.getComponentName(), intent.getComponent())) { + if (shouldShowBlockedActivityDialog( + activityInfo.getComponentName(), intent.getComponent())) { mContext.startActivityAsUser( intent.addFlags( Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK), ActivityOptions.makeBasic().setLaunchDisplayId(displayId).toBundle(), UserHandle.SYSTEM); } + if (android.companion.virtualdevice.flags.Flags.activityControlApi()) { mActivityListenerAdapter.onActivityLaunchBlocked( displayId, @@ -1338,6 +1347,20 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub } } + private boolean shouldShowBlockedActivityDialog(ComponentName blockedComponent, + ComponentName blockedAppStreamingActivityComponent) { + if (!android.companion.virtualdevice.flags.Flags.activityControlApi()) { + return true; + } + if (Objects.equals(blockedComponent, blockedAppStreamingActivityComponent)) { + // Do not show the dialog if it was blocked for some reason already to avoid + // infinite blocking loop. + return false; + } + // Do not show the dialog if disabled by policy. + return getDevicePolicy(POLICY_TYPE_BLOCKED_ACTIVITY_BEHAVIOR) == DEVICE_POLICY_DEFAULT; + } + private void onSecureWindowShown(int displayId, int uid) { synchronized (mVirtualDeviceLock) { if (!mVirtualDisplays.contains(displayId)) { diff --git a/services/core/Android.bp b/services/core/Android.bp index d9962c7b02d5..1cd20ed0f7cd 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -47,7 +47,7 @@ genrule { ], tools: ["protologtool"], cmd: "$(location protologtool) transform-protolog-calls " + - "--protolog-class com.android.internal.protolog.common.ProtoLog " + + "--protolog-class com.android.internal.protolog.ProtoLog " + "--loggroups-class com.android.internal.protolog.ProtoLogGroup " + "--loggroups-jar $(location :protolog-groups) " + "--viewer-config-file-path /etc/core.protolog.pb " + @@ -66,7 +66,7 @@ genrule { ], tools: ["protologtool"], cmd: "$(location protologtool) generate-viewer-config " + - "--protolog-class com.android.internal.protolog.common.ProtoLog " + + "--protolog-class com.android.internal.protolog.ProtoLog " + "--loggroups-class com.android.internal.protolog.ProtoLogGroup " + "--loggroups-jar $(location :protolog-groups) " + "--viewer-config-type json " + @@ -83,7 +83,7 @@ genrule { ], tools: ["protologtool"], cmd: "$(location protologtool) generate-viewer-config " + - "--protolog-class com.android.internal.protolog.common.ProtoLog " + + "--protolog-class com.android.internal.protolog.ProtoLog " + "--loggroups-class com.android.internal.protolog.ProtoLogGroup " + "--loggroups-jar $(location :protolog-groups) " + "--viewer-config-type proto " + diff --git a/services/core/java/com/android/server/SystemServerInitThreadPool.java b/services/core/java/com/android/server/SystemServerInitThreadPool.java index 3d3535d2dbd2..73d8384395d2 100644 --- a/services/core/java/com/android/server/SystemServerInitThreadPool.java +++ b/services/core/java/com/android/server/SystemServerInitThreadPool.java @@ -197,8 +197,8 @@ public final class SystemServerInitThreadPool implements Dumpable { /* processCpuTracker= */null, /* lastPids= */null, CompletableFuture.completedFuture(Watchdog.getInterestingNativePids()), /* logExceptionCreatingFile= */null, /* subject= */null, - /* criticalEventSection= */null, Runnable::run, - /* latencyTracker= */null); + /* criticalEventSection= */null, /* extraHeaders= */ null, + Runnable::run, /* latencyTracker= */null); } @Override diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java index 72a55dbea481..21947bac137b 100644 --- a/services/core/java/com/android/server/Watchdog.java +++ b/services/core/java/com/android/server/Watchdog.java @@ -74,6 +74,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.List; import java.util.Optional; import java.util.UUID; @@ -991,6 +992,9 @@ public class Watchdog implements Dumpable { FrameworkStatsLog.write(FrameworkStatsLog.SYSTEM_SERVER_WATCHDOG_OCCURRED, subject); } + final LinkedHashMap headersMap = + com.android.server.am.Flags.enableDropboxWatchdogHeaders() + ? new LinkedHashMap<>(Collections.singletonMap("Watchdog-Type", dropboxTag)) : null; long anrTime = SystemClock.uptimeMillis(); StringBuilder report = new StringBuilder(); report.append(ResourcePressureUtil.currentPsiState()); @@ -998,8 +1002,9 @@ public class Watchdog implements Dumpable { StringWriter tracesFileException = new StringWriter(); final File stack = StackTracesDumpHelper.dumpStackTraces( pids, processCpuTracker, new SparseBooleanArray(), - CompletableFuture.completedFuture(getInterestingNativePids()), tracesFileException, - subject, criticalEvents, Runnable::run, /* latencyTracker= */null); + CompletableFuture.completedFuture(getInterestingNativePids()), + tracesFileException, subject, criticalEvents, headersMap, + Runnable::run, /* latencyTracker= */null); // Give some extra time to make sure the stack traces get written. // The system's been hanging for a whlie, another second or two won't hurt much. SystemClock.sleep(5000); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index f6b3b3972f36..25fb729e7e7c 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -429,7 +429,7 @@ import com.android.internal.os.TransferPipe; import com.android.internal.os.Zygote; import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; import com.android.internal.policy.AttributeCache; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.internal.util.DumpUtils; import com.android.internal.util.FastPrintWriter; import com.android.internal.util.FrameworkStatsLog; diff --git a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java index d4d49654ad29..ba4b71cd7540 100644 --- a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java +++ b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java @@ -60,7 +60,6 @@ import com.android.server.ResourcePressureUtil; import com.android.server.criticalevents.CriticalEventLog; import com.android.server.stats.pull.ProcfsMemoryUtil.MemorySnapshot; import com.android.server.wm.WindowProcessController; -import com.android.server.utils.AnrTimer; import java.io.File; import java.io.PrintWriter; @@ -69,6 +68,7 @@ import java.time.Instant; import java.time.ZoneId; import java.time.ZonedDateTime; import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.UUID; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; @@ -429,7 +429,7 @@ class ProcessErrorStateRecord { } } // Build memory headers for the ANRing process. - String memoryHeaders = buildMemoryHeadersFor(pid); + LinkedHashMap<String, String> memoryHeaders = buildMemoryHeadersFor(pid); // Get critical event log before logging the ANR so that it doesn't occur in the log. latencyTracker.criticalEventLogStarted(); @@ -753,7 +753,7 @@ class ProcessErrorStateRecord { resolver.getUserId()) != 0; } - private @Nullable String buildMemoryHeadersFor(int pid) { + private @Nullable LinkedHashMap<String, String> buildMemoryHeadersFor(int pid) { if (pid <= 0) { Slog.i(TAG, "Memory header requested with invalid pid: " + pid); return null; @@ -764,15 +764,13 @@ class ProcessErrorStateRecord { return null; } - StringBuilder memoryHeaders = new StringBuilder(); - memoryHeaders.append("RssHwmKb: ") - .append(snapshot.rssHighWaterMarkInKilobytes) - .append("\n"); - memoryHeaders.append("RssKb: ").append(snapshot.rssInKilobytes).append("\n"); - memoryHeaders.append("RssAnonKb: ").append(snapshot.anonRssInKilobytes).append("\n"); - memoryHeaders.append("RssShmemKb: ").append(snapshot.rssShmemKilobytes).append("\n"); - memoryHeaders.append("VmSwapKb: ").append(snapshot.swapInKilobytes).append("\n"); - return memoryHeaders.toString(); + LinkedHashMap<String, String> memoryHeaders = new LinkedHashMap<>(); + memoryHeaders.put("RssHwmKb", Integer.toString(snapshot.rssHighWaterMarkInKilobytes)); + memoryHeaders.put("RssKb", Integer.toString(snapshot.rssInKilobytes)); + memoryHeaders.put("RssAnonKb", Integer.toString(snapshot.anonRssInKilobytes)); + memoryHeaders.put("RssShmemKb", Integer.toString(snapshot.rssShmemKilobytes)); + memoryHeaders.put("VmSwapKb", Integer.toString(snapshot.swapInKilobytes)); + return memoryHeaders; } /** * Unless configured otherwise, swallow ANRs in background processes & kill the process. diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index c0947247de4b..f4b1229696f5 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -855,8 +855,8 @@ public final class ProcessList { Slog.i(TAG, "Failed to connect to lmkd, retry after " + LMKD_RECONNECT_DELAY_MS + " ms"); // retry after LMKD_RECONNECT_DELAY_MS - sKillHandler.sendMessageDelayed(sKillHandler.obtainMessage( - KillHandler.LMKD_RECONNECT_MSG), LMKD_RECONNECT_DELAY_MS); + sendMessageDelayed(obtainMessage( + LMKD_RECONNECT_MSG), LMKD_RECONNECT_DELAY_MS); } break; default: diff --git a/services/core/java/com/android/server/am/StackTracesDumpHelper.java b/services/core/java/com/android/server/am/StackTracesDumpHelper.java index c1374e1b5a05..2021ba4f3b5d 100644 --- a/services/core/java/com/android/server/am/StackTracesDumpHelper.java +++ b/services/core/java/com/android/server/am/StackTracesDumpHelper.java @@ -47,6 +47,8 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.Date; +import java.util.LinkedHashMap; +import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; @@ -100,15 +102,17 @@ public class StackTracesDumpHelper { /** * @param subject the subject of the dumped traces * @param criticalEventSection the critical event log, passed as a string + * @param extraHeaders Optional, Extra headers added by the dumping method */ public static File dumpStackTraces(ArrayList<Integer> firstPids, ProcessCpuTracker processCpuTracker, SparseBooleanArray lastPids, Future<ArrayList<Integer>> nativePidsFuture, StringWriter logExceptionCreatingFile, - String subject, String criticalEventSection, @NonNull Executor auxiliaryTaskExecutor, + String subject, String criticalEventSection, + LinkedHashMap<String, String> extraHeaders, @NonNull Executor auxiliaryTaskExecutor, AnrLatencyTracker latencyTracker) { return dumpStackTraces(firstPids, processCpuTracker, lastPids, nativePidsFuture, logExceptionCreatingFile, null, subject, criticalEventSection, - /* memoryHeaders= */ null, auxiliaryTaskExecutor, null, latencyTracker); + extraHeaders, auxiliaryTaskExecutor, null, latencyTracker); } /** @@ -119,7 +123,7 @@ public class StackTracesDumpHelper { ProcessCpuTracker processCpuTracker, SparseBooleanArray lastPids, Future<ArrayList<Integer>> nativePidsFuture, StringWriter logExceptionCreatingFile, AtomicLong firstPidEndOffset, String subject, String criticalEventSection, - String memoryHeaders, @NonNull Executor auxiliaryTaskExecutor, + LinkedHashMap<String, String> extraHeaders, @NonNull Executor auxiliaryTaskExecutor, Future<File> firstPidFilePromise, AnrLatencyTracker latencyTracker) { try { @@ -159,11 +163,12 @@ public class StackTracesDumpHelper { } return null; } + boolean extraHeadersExist = extraHeaders != null && !extraHeaders.isEmpty(); - if (subject != null || criticalEventSection != null || memoryHeaders != null) { + if (subject != null || criticalEventSection != null || extraHeadersExist) { appendtoANRFile(tracesFile.getAbsolutePath(), (subject != null ? "Subject: " + subject + "\n" : "") - + (memoryHeaders != null ? memoryHeaders + "\n\n" : "") + + (extraHeadersExist ? stringifyHeaders(extraHeaders) + "\n\n" : "") + (criticalEventSection != null ? criticalEventSection : "")); } @@ -614,4 +619,15 @@ public class StackTracesDumpHelper { return pids; } + private static String stringifyHeaders(@NonNull LinkedHashMap<String, String> headers) { + StringBuilder headersString = new StringBuilder(); + for (Map.Entry<String, String> entry : headers.entrySet()) { + headersString.append(entry.getKey()) + .append(": ") + .append(entry.getValue()) + .append("\n"); + } + return headersString.toString(); + } + } diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig index 0dfa3302adfd..9b380ff12e2d 100644 --- a/services/core/java/com/android/server/am/flags.aconfig +++ b/services/core/java/com/android/server/am/flags.aconfig @@ -167,4 +167,12 @@ flag { description: "Allow logcat collection on synchronous dropbox collection" bug: "324222683" is_fixed_read_only: true -}
\ No newline at end of file +} + +flag { + name: "enable_dropbox_watchdog_headers" + namespace: "dropbox" + description: "Add watchdog-specific dropbox headers" + bug: "330682397" + is_fixed_read_only: true +} diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java index 6ff4a617cec4..2986340c684d 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java +++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java @@ -2528,7 +2528,7 @@ public class AudioDeviceInventory { mDeviceBroker.setBluetoothA2dpOnInt(true, false /*fromA2dp*/, eventSource); AudioDeviceAttributes ada = new AudioDeviceAttributes(device, address, name); - final int res = AudioSystem.setDeviceConnectionState(ada, + final int res = mAudioSystem.setDeviceConnectionState(ada, AudioSystem.DEVICE_STATE_AVAILABLE, codec); if (res != AudioSystem.AUDIO_STATUS_OK) { AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent( @@ -2575,7 +2575,7 @@ public class AudioDeviceInventory { AudioDeviceAttributes ada = null; if (device != AudioSystem.DEVICE_NONE) { ada = new AudioDeviceAttributes(device, address); - final int res = AudioSystem.setDeviceConnectionState(ada, + final int res = mAudioSystem.setDeviceConnectionState(ada, AudioSystem.DEVICE_STATE_UNAVAILABLE, codec); diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 39cb5a96f564..97326211d18e 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -461,6 +461,7 @@ public class AudioService extends IAudioService.Stub private static final int MSG_DISPATCH_PREFERRED_MIXER_ATTRIBUTES = 52; private static final int MSG_CONFIGURATION_CHANGED = 54; private static final int MSG_BROADCAST_MASTER_MUTE = 55; + private static final int MSG_UPDATE_CONTEXTUAL_VOLUMES = 56; /** * Messages handled by the {@link SoundDoseHelper}, do not exceed @@ -4466,7 +4467,7 @@ public class AudioService extends IAudioService.Stub } } if (mVoicePlaybackActive.getAndSet(voiceActive) != voiceActive) { - updateHearingAidVolumeOnVoiceActivityUpdate(); + postUpdateContextualVolumes(); } if (mMediaPlaybackActive.getAndSet(mediaActive) != mediaActive && mediaActive) { mSoundDoseHelper.scheduleMusicActiveCheck(); @@ -4604,13 +4605,29 @@ public class AudioService extends IAudioService.Stub } } - private void updateHearingAidVolumeOnVoiceActivityUpdate() { + + // delay between audio playback configuration update and checking + // actual stream activity to take async playback stop into account + private static final int UPDATE_CONTEXTUAL_VOLUME_DELAY_MS = 500; + + /*package*/ void postUpdateContextualVolumes() { + sendMsg(mAudioHandler, MSG_UPDATE_CONTEXTUAL_VOLUMES, SENDMSG_REPLACE, + /*arg1*/ 0, /*arg2*/ 0, TAG, UPDATE_CONTEXTUAL_VOLUME_DELAY_MS); + } + + private void onUpdateContextualVolumes() { final int streamType = getBluetoothContextualVolumeStream(); - final int index = getStreamVolume(streamType); - sVolumeLogger.enqueue(new VolumeEvent(VolumeEvent.VOL_VOICE_ACTIVITY_HEARING_AID, - mVoicePlaybackActive.get(), streamType, index)); - mDeviceBroker.postSetHearingAidVolumeIndex(index * 10, streamType); + final int device = getDeviceForStream(streamType); + final int index = getStreamVolume(streamType, device); + if (AudioSystem.isLeAudioDeviceType(device)) { + mDeviceBroker.postSetLeAudioVolumeIndex(index * 10, + mStreamStates[streamType].getMaxIndex(), streamType); + } else if (device == AudioSystem.DEVICE_OUT_HEARING_AID) { + mDeviceBroker.postSetHearingAidVolumeIndex(index * 10, streamType); + } + sVolumeLogger.enqueue(new VolumeEvent(VolumeEvent.VOL_VOICE_ACTIVITY_CONTEXTUAL_VOLUME, + mVoicePlaybackActive.get(), streamType, index, device)); } /** @@ -9812,6 +9829,10 @@ public class AudioService extends IAudioService.Stub onConfigurationChanged(); break; + case MSG_UPDATE_CONTEXTUAL_VOLUMES: + onUpdateContextualVolumes(); + break; + case MusicFxHelper.MSG_EFFECT_CLIENT_GONE: mMusicFxHelper.handleMessage(msg); break; diff --git a/services/core/java/com/android/server/audio/AudioServiceEvents.java b/services/core/java/com/android/server/audio/AudioServiceEvents.java index 8ea28bed30a2..631d5d80acd7 100644 --- a/services/core/java/com/android/server/audio/AudioServiceEvents.java +++ b/services/core/java/com/android/server/audio/AudioServiceEvents.java @@ -227,7 +227,7 @@ public class AudioServiceEvents { static final int VOL_SET_HEARING_AID_VOL = 3; static final int VOL_SET_AVRCP_VOL = 4; static final int VOL_ADJUST_VOL_UID = 5; - static final int VOL_VOICE_ACTIVITY_HEARING_AID = 6; + static final int VOL_VOICE_ACTIVITY_CONTEXTUAL_VOLUME = 6; static final int VOL_MODE_CHANGE_HEARING_AID = 7; static final int VOL_SET_GROUP_VOL = 8; static final int VOL_MUTE_STREAM_INT = 9; @@ -299,14 +299,14 @@ public class AudioServiceEvents { logMetricEvent(); } - /** used for VOL_VOICE_ACTIVITY_HEARING_AID */ - VolumeEvent(int op, boolean voiceActive, int stream, int index) { + /** used for VOL_VOICE_ACTIVITY_CONTEXTUAL_VOLUME */ + VolumeEvent(int op, boolean voiceActive, int stream, int index, int device) { mOp = op; mStream = stream; mVal1 = index; mVal2 = voiceActive ? 1 : 0; // unused - mVal3 = -1; + mVal3 = device; mCaller = null; mGroupName = null; logMetricEvent(); @@ -445,14 +445,16 @@ public class AudioServiceEvents { .set(MediaMetrics.Property.INDEX, mVal1) .record(); return; - case VOL_VOICE_ACTIVITY_HEARING_AID: + case VOL_VOICE_ACTIVITY_CONTEXTUAL_VOLUME: new MediaMetrics.Item(mMetricsId) - .set(MediaMetrics.Property.EVENT, "voiceActivityHearingAid") + .set(MediaMetrics.Property.EVENT, "voiceActivityContextualVolume") .set(MediaMetrics.Property.INDEX, mVal1) .set(MediaMetrics.Property.STATE, mVal2 == 1 ? "active" : "inactive") .set(MediaMetrics.Property.STREAM_TYPE, AudioSystem.streamToString(mStream)) + .set(MediaMetrics.Property.DEVICE, + AudioSystem.getOutputDeviceName(mVal3)) .record(); return; case VOL_MODE_CHANGE_HEARING_AID: @@ -538,11 +540,12 @@ public class AudioServiceEvents { .append(" flags:0x").append(Integer.toHexString(mVal2)) .append(") from ").append(mCaller) .toString(); - case VOL_VOICE_ACTIVITY_HEARING_AID: + case VOL_VOICE_ACTIVITY_CONTEXTUAL_VOLUME: return new StringBuilder("Voice activity change (") .append(mVal2 == 1 ? "active" : "inactive") - .append(") causes setting HEARING_AID volume to idx:").append(mVal1) + .append(") causes setting volume to idx:").append(mVal1) .append(" stream:").append(AudioSystem.streamToString(mStream)) + .append(" device:").append(AudioSystem.getOutputDeviceName(mVal3)) .toString(); case VOL_MODE_CHANGE_HEARING_AID: return new StringBuilder("setMode(") diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java index e8394d43f266..619aecf4156f 100644 --- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java +++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java @@ -19,6 +19,9 @@ package com.android.server.devicestate; import static android.Manifest.permission.CONTROL_DEVICE_STATE; import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND; import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_REAR_DISPLAY; +import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY; +import static android.hardware.devicestate.DeviceState.PROPERTY_POLICY_AVAILABLE_FOR_APP_REQUEST; import static android.hardware.devicestate.DeviceState.PROPERTY_POLICY_CANCEL_OVERRIDE_REQUESTS; import static android.hardware.devicestate.DeviceState.PROPERTY_POLICY_CANCEL_WHEN_REQUESTER_NOT_ON_TOP; import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE_IDENTIFIER; @@ -47,6 +50,8 @@ import android.hardware.devicestate.DeviceStateManager; import android.hardware.devicestate.DeviceStateManagerInternal; import android.hardware.devicestate.IDeviceStateManager; import android.hardware.devicestate.IDeviceStateManagerCallback; +import android.hardware.devicestate.feature.flags.FeatureFlags; +import android.hardware.devicestate.feature.flags.FeatureFlagsImpl; import android.os.Binder; import android.os.Handler; import android.os.IBinder; @@ -175,7 +180,7 @@ public final class DeviceStateManagerService extends SystemService { private Set<Integer> mDeviceStatesAvailableForAppRequests = new HashSet<>(); - private Set<Integer> mFoldedDeviceStates; + private Set<Integer> mFoldedDeviceStates = new HashSet<>(); @Nullable private DeviceState mRearDisplayState; @@ -185,6 +190,9 @@ public final class DeviceStateManagerService extends SystemService { @Nullable private OverrideRequest mRearDisplayPendingOverrideRequest; + @NonNull + private final FeatureFlags mFlags; + @VisibleForTesting interface SystemPropertySetter { void setDebugTracingDeviceStateProperty(String value); @@ -245,6 +253,7 @@ public final class DeviceStateManagerService extends SystemService { @NonNull SystemPropertySetter systemPropertySetter) { super(context); mSystemPropertySetter = systemPropertySetter; + mFlags = new FeatureFlagsImpl(); // We use the DisplayThread because this service indirectly drives // display (on/off) and window (position) events through its callbacks. DisplayThread displayThread = DisplayThread.get(); @@ -270,9 +279,12 @@ public final class DeviceStateManagerService extends SystemService { publishBinderService(Context.DEVICE_STATE_SERVICE, mBinderService); publishLocalService(DeviceStateManagerInternal.class, new LocalService()); - synchronized (mLock) { - readStatesAvailableForRequestFromApps(); - mFoldedDeviceStates = readFoldedStates(); + if (!mFlags.deviceStatePropertyMigration()) { + synchronized (mLock) { + readStatesAvailableForRequestFromApps(); + mFoldedDeviceStates = readFoldedStates(); + setRearDisplayStateLocked(); + } } mActivityTaskManagerInternal.registerScreenObserver(mOverrideRequestScreenObserver); @@ -461,8 +473,6 @@ public final class DeviceStateManagerService extends SystemService { mOverrideRequestController.handleNewSupportedStates(newStateIdentifiers, reason); updatePendingStateLocked(); - setRearDisplayStateLocked(); - notifyDeviceStateInfoChangedAsync(); mHandler.post(this::notifyPolicyIfNeeded); @@ -838,12 +848,22 @@ public final class DeviceStateManagerService extends SystemService { OverrideRequest request = new OverrideRequest(token, callingPid, callingUid, deviceState.get(), flags, OVERRIDE_REQUEST_TYPE_EMULATED_STATE); - // If we don't have the CONTROL_DEVICE_STATE permission, we want to show the overlay - if (!hasControlDeviceStatePermission && mRearDisplayState != null - && state == mRearDisplayState.getIdentifier()) { - showRearDisplayEducationalOverlayLocked(request); + if (mFlags.deviceStatePropertyMigration()) { + // If we don't have the CONTROL_DEVICE_STATE permission, we want to show the overlay + if (!hasControlDeviceStatePermission && deviceState.get().hasProperty( + PROPERTY_FEATURE_REAR_DISPLAY)) { + showRearDisplayEducationalOverlayLocked(request); + } else { + mOverrideRequestController.addRequest(request); + } } else { - mOverrideRequestController.addRequest(request); + // If we don't have the CONTROL_DEVICE_STATE permission, we want to show the overlay + if (!hasControlDeviceStatePermission && mRearDisplayState != null + && state == mRearDisplayState.getIdentifier()) { + showRearDisplayEducationalOverlayLocked(request); + } else { + mOverrideRequestController.addRequest(request); + } } } } @@ -1034,7 +1054,13 @@ public final class DeviceStateManagerService extends SystemService { private boolean isStateAvailableForAppRequests(int state) { synchronized (mLock) { - return mDeviceStatesAvailableForAppRequests.contains(state); + if (mFlags.deviceStatePropertyMigration()) { + Optional<DeviceState> deviceState = getStateLocked(state); + return deviceState.isPresent() && deviceState.get().hasProperty( + PROPERTY_POLICY_AVAILABLE_FOR_APP_REQUEST); + } else { + return mDeviceStatesAvailableForAppRequests.contains(state); + } } } @@ -1096,9 +1122,20 @@ public final class DeviceStateManagerService extends SystemService { */ @GuardedBy("mLock") private boolean isDeviceOpeningLocked(int newBaseState) { - return mBaseState.filter( - deviceState -> mFoldedDeviceStates.contains(deviceState.getIdentifier()) - && !mFoldedDeviceStates.contains(newBaseState)).isPresent(); + if (mFlags.deviceStatePropertyMigration()) { + final DeviceState currentBaseState = mBaseState.orElse(INVALID_DEVICE_STATE); + final DeviceState newDeviceBaseState = getStateLocked(newBaseState).orElse( + INVALID_DEVICE_STATE); + + return currentBaseState.hasProperty( + PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY) + && !newDeviceBaseState.hasProperty( + PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY); + } else { + return mBaseState.filter( + deviceState -> mFoldedDeviceStates.contains(deviceState.getIdentifier()) + && !mFoldedDeviceStates.contains(newBaseState)).isPresent(); + } } private final class DeviceStateProviderListener implements DeviceStateProvider.Listener { diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index de9715ac812d..7cd9144be77b 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -2098,7 +2098,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private void onDisplayOffloadUnblockScreenOn(DisplayOffloadSession displayOffloadSession) { Message msg = mHandler.obtainMessage(MSG_OFFLOADING_SCREEN_ON_UNBLOCKED, displayOffloadSession); - mHandler.sendMessage(msg); + mHandler.sendMessageAtTime(msg, mClock.uptimeMillis()); } private void unblockScreenOnByDisplayOffload() { @@ -2116,7 +2116,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call if (mDisplayOffloadSession == null) { return; } - if (mPendingScreenOnUnblockerByDisplayOffload != null) { + if (mPendingScreenOnUnblockerByDisplayOffload == null) { // Already unblocked. return; } @@ -2134,7 +2134,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // If the screen is turning on, give displayoffload a chance to do something before the // screen actually turns on. - // TODO(b/316941732): add tests for this displayoffload screen-on blocker. if (isOn && changed && !mScreenTurningOnWasBlockedByDisplayOffload) { blockScreenOnByDisplayOffload(mDisplayOffloadSession); } else if (!isOn && mScreenTurningOnWasBlockedByDisplayOffload) { diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java index 18a9986d34ba..886857c1b880 100644 --- a/services/core/java/com/android/server/dreams/DreamManagerService.java +++ b/services/core/java/com/android/server/dreams/DreamManagerService.java @@ -1079,6 +1079,21 @@ public final class DreamManagerService extends SystemService { } @Override // Binder call + public void finishSelfOneway(IBinder token, boolean immediate) { + // Requires no permission, called by Dream from an arbitrary process. + if (token == null) { + throw new IllegalArgumentException("token must not be null"); + } + + final long ident = Binder.clearCallingIdentity(); + try { + finishSelfInternal(token, immediate); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override // Binder call public void startDozing( IBinder token, int screenState, @Display.StateReason int reason, int screenBrightness) { @@ -1096,6 +1111,23 @@ public final class DreamManagerService extends SystemService { } @Override // Binder call + public void startDozingOneway( + IBinder token, int screenState, @Display.StateReason int reason, + int screenBrightness) { + // Requires no permission, called by Dream from an arbitrary process. + if (token == null) { + throw new IllegalArgumentException("token must not be null"); + } + + final long ident = Binder.clearCallingIdentity(); + try { + startDozingInternal(token, screenState, reason, screenBrightness); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override // Binder call public void stopDozing(IBinder token) { // Requires no permission, called by Dream from an arbitrary process. if (token == null) { diff --git a/services/core/java/com/android/server/health/HealthServiceWrapper.java b/services/core/java/com/android/server/health/HealthServiceWrapper.java index 25d1a885bc18..9c14b5b079b1 100644 --- a/services/core/java/com/android/server/health/HealthServiceWrapper.java +++ b/services/core/java/com/android/server/health/HealthServiceWrapper.java @@ -71,6 +71,21 @@ public abstract class HealthServiceWrapper { public abstract android.hardware.health.HealthInfo getHealthInfo() throws RemoteException; /** + * Calls into getBatteryHealthData() in the health HAL. + * This function does not have a corresponding HIDL implementation, so + * returns null by default, unless there is an AIDL class that overrides + * this one. + * + * @return battery health data. {@code null} if no health HAL service. + * {@code null} if any service-specific error when calling {@code + * getBatteryHealthData}, e.g. it is unsupported. + * @throws RemoteException for any transaction-level errors + */ + public android.hardware.health.BatteryHealthData getBatteryHealthData() throws RemoteException { + return null; + } + + /** * Create a new HealthServiceWrapper instance. * * @param healthInfoCallback the callback to call when health info changes diff --git a/services/core/java/com/android/server/health/HealthServiceWrapperAidl.java b/services/core/java/com/android/server/health/HealthServiceWrapperAidl.java index fd3a92e97c26..2a3fbc3f2466 100644 --- a/services/core/java/com/android/server/health/HealthServiceWrapperAidl.java +++ b/services/core/java/com/android/server/health/HealthServiceWrapperAidl.java @@ -212,6 +212,17 @@ class HealthServiceWrapperAidl extends HealthServiceWrapper { } } + @Override + public BatteryHealthData getBatteryHealthData() throws RemoteException { + IHealth service = mLastService.get(); + if (service == null) return null; + try { + return service.getBatteryHealthData(); + } catch (UnsupportedOperationException | ServiceSpecificException ex) { + return null; + } + } + public void setChargingPolicy(int policy) throws RemoteException { IHealth service = mLastService.get(); if (service == null) return; diff --git a/services/core/java/com/android/server/input/BatteryController.java b/services/core/java/com/android/server/input/BatteryController.java index 38a0d37c5679..62c21bda1fd1 100644 --- a/services/core/java/com/android/server/input/BatteryController.java +++ b/services/core/java/com/android/server/input/BatteryController.java @@ -83,6 +83,7 @@ final class BatteryController { private final Handler mHandler; private final UEventManager mUEventManager; private final BluetoothBatteryManager mBluetoothBatteryManager; + private final Runnable mHandlePollEventCallback = this::handlePollEvent; // Maps a pid to the registered listener record for that process. There can only be one battery // listener per process. @@ -206,7 +207,7 @@ final class BatteryController { if (!mIsInteractive || !anyOf(mDeviceMonitors, DeviceMonitor::requiresPolling)) { // Stop polling. mIsPolling = false; - mHandler.removeCallbacks(this::handlePollEvent); + mHandler.removeCallbacks(mHandlePollEventCallback); return; } @@ -215,7 +216,7 @@ final class BatteryController { } // Start polling. mIsPolling = true; - mHandler.postDelayed(this::handlePollEvent, delayStart ? POLLING_PERIOD_MILLIS : 0); + mHandler.postDelayed(mHandlePollEventCallback, delayStart ? POLLING_PERIOD_MILLIS : 0); } private <R> R processInputDevice(int deviceId, R defaultValue, Function<InputDevice, R> func) { @@ -366,7 +367,7 @@ final class BatteryController { } final long eventTime = SystemClock.uptimeMillis(); mDeviceMonitors.forEach((deviceId, monitor) -> monitor.onPoll(eventTime)); - mHandler.postDelayed(this::handlePollEvent, POLLING_PERIOD_MILLIS); + mHandler.postDelayed(mHandlePollEventCallback, POLLING_PERIOD_MILLIS); } } diff --git a/services/core/java/com/android/server/inputmethod/AutofillSuggestionsController.java b/services/core/java/com/android/server/inputmethod/AutofillSuggestionsController.java index 0749edce97a1..aeace7ac9aee 100644 --- a/services/core/java/com/android/server/inputmethod/AutofillSuggestionsController.java +++ b/services/core/java/com/android/server/inputmethod/AutofillSuggestionsController.java @@ -103,8 +103,8 @@ final class AutofillSuggestionsController { // Note that current user ID is guaranteed to be userId. final var imeId = mBindingController.getSelectedMethodId(); - final InputMethodInfo imi = InputMethodSettingsRepository.get(mBindingController.mUserId) - .getMethodMap().get(imeId); + final InputMethodInfo imi = InputMethodSettingsRepository.get( + mBindingController.getUserId()).getMethodMap().get(imeId); if (imi == null || !isInlineSuggestionsEnabled(imi, touchExplorationEnabled)) { callback.onInlineSuggestionsUnsupported(); return; diff --git a/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java b/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java index aa4b3386e548..356bc40cb985 100644 --- a/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java +++ b/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java @@ -53,10 +53,10 @@ import com.android.server.wm.WindowManagerInternal; import java.util.Objects; /** - * The default implementation of {@link ImeVisibilityApplier} used in - * {@link InputMethodManagerService}. + * A stateless helper class for IME visibility operations like show/hide and update Z-ordering + * relative to the IME targeted window. */ -final class DefaultImeVisibilityApplier implements ImeVisibilityApplier { +final class DefaultImeVisibilityApplier { private static final String TAG = "DefaultImeVisibilityApplier"; @@ -75,9 +75,18 @@ final class DefaultImeVisibilityApplier implements ImeVisibilityApplier { mImeTargetVisibilityPolicy = LocalServices.getService(ImeTargetVisibilityPolicy.class); } + /** + * Performs showing IME on top of the given window. + * + * @param showInputToken a token that represents the requester to show IME + * @param statsToken the token tracking the current IME request + * @param resultReceiver if non-null, this will be called back to the caller when + * it has processed request to tell what it has done + * @param reason yhe reason for requesting to show IME + * @param userId the target user when performing show IME + */ @GuardedBy("ImfLock.class") - @Override - public void performShowIme(IBinder showInputToken, @NonNull ImeTracker.Token statsToken, + void performShowIme(IBinder showInputToken, @NonNull ImeTracker.Token statsToken, @InputMethod.ShowFlags int showFlags, ResultReceiver resultReceiver, @SoftInputShowHideReason int reason, @UserIdInt int userId) { final var bindingController = mService.getInputMethodBindingController(userId); @@ -105,9 +114,18 @@ final class DefaultImeVisibilityApplier implements ImeVisibilityApplier { } } + /** + * Performs hiding IME to the given window + * + * @param hideInputToken a token that represents the requester to hide IME + * @param statsToken the token tracking the current IME request + * @param resultReceiver if non-null, this will be called back to the caller when + * it has processed request to tell what it has done + * @param reason the reason for requesting to hide IME + * @param userId the target user when performing hide IME + */ @GuardedBy("ImfLock.class") - @Override - public void performHideIme(IBinder hideInputToken, @NonNull ImeTracker.Token statsToken, + void performHideIme(IBinder hideInputToken, @NonNull ImeTracker.Token statsToken, ResultReceiver resultReceiver, @SoftInputShowHideReason int reason, @UserIdInt int userId) { final var bindingController = mService.getInputMethodBindingController(userId); @@ -139,14 +157,16 @@ final class DefaultImeVisibilityApplier implements ImeVisibilityApplier { } } - @GuardedBy("ImfLock.class") - @Override - public void applyImeVisibility(IBinder windowToken, @NonNull ImeTracker.Token statsToken, - @ImeVisibilityStateComputer.VisibilityState int state, @UserIdInt int userId) { - applyImeVisibility(windowToken, statsToken, state, - SoftInputShowHideReason.NOT_SET /* ignore reason */, userId); - } - + /** + * Applies the IME visibility from {@link android.inputmethodservice.InputMethodService} with + * according to the given visibility state. + * + * @param windowToken the token of a window for applying the IME visibility + * @param statsToken the token tracking the current IME request + * @param state the new IME visibility state for the applier to handle + * @param reason one of {@link SoftInputShowHideReason} + * @param userId the target user when applying the IME visibility state + */ @GuardedBy("ImfLock.class") void applyImeVisibility(IBinder windowToken, @Nullable ImeTracker.Token statsToken, @ImeVisibilityStateComputer.VisibilityState int state, @@ -218,9 +238,16 @@ final class DefaultImeVisibilityApplier implements ImeVisibilityApplier { } } + /** + * Shows the IME screenshot and attach it to the given IME target window. + * + * @param imeTarget the token of a window to show the IME screenshot + * @param displayId the unique id to identify the display + * @param userId the target user when when showing the IME screenshot + * @return {@code true} if success, {@code false} otherwise + */ @GuardedBy("ImfLock.class") - @Override - public boolean showImeScreenshot(@NonNull IBinder imeTarget, int displayId, + boolean showImeScreenshot(@NonNull IBinder imeTarget, int displayId, @UserIdInt int userId) { if (mImeTargetVisibilityPolicy.showImeScreenshot(imeTarget, displayId)) { mService.onShowHideSoftInputRequested(false /* show */, imeTarget, @@ -230,9 +257,15 @@ final class DefaultImeVisibilityApplier implements ImeVisibilityApplier { return false; } + /** + * Removes the IME screenshot on the given display. + * + * @param displayId the target display of showing IME screenshot + * @param userId the target user of showing IME screenshot + * @return {@code true} if success, {@code false} otherwise + */ @GuardedBy("ImfLock.class") - @Override - public boolean removeImeScreenshot(int displayId, @UserIdInt int userId) { + boolean removeImeScreenshot(int displayId, @UserIdInt int userId) { final var userData = mService.getUserData(userId); if (mImeTargetVisibilityPolicy.removeImeScreenshot(displayId)) { mService.onShowHideSoftInputRequested(false /* show */, diff --git a/services/core/java/com/android/server/inputmethod/ImeTrackerService.java b/services/core/java/com/android/server/inputmethod/ImeTrackerService.java index 56fa8c9364f5..14551a1a0033 100644 --- a/services/core/java/com/android/server/inputmethod/ImeTrackerService.java +++ b/services/core/java/com/android/server/inputmethod/ImeTrackerService.java @@ -23,7 +23,6 @@ import android.annotation.Nullable; import android.os.Binder; import android.os.Handler; import android.os.IBinder; -import android.os.Looper; import android.util.Log; import android.view.inputmethod.ImeTracker; @@ -70,8 +69,8 @@ public final class ImeTrackerService extends IImeTracker.Stub { private final Object mLock = new Object(); - ImeTrackerService(@NonNull Looper looper) { - mHandler = new Handler(looper, null /* callback */, true /* async */); + ImeTrackerService(@NonNull Handler handler) { + mHandler = handler; } @NonNull diff --git a/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java b/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java deleted file mode 100644 index c1069f2a23d7..000000000000 --- a/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.inputmethod; - -import android.annotation.NonNull; -import android.annotation.UserIdInt; -import android.os.IBinder; -import android.os.ResultReceiver; -import android.view.inputmethod.ImeTracker; -import android.view.inputmethod.InputMethod; - -import com.android.internal.inputmethod.SoftInputShowHideReason; - -/** - * Interface for IME visibility operations like show/hide and update Z-ordering relative to the IME - * targeted window. - */ -interface ImeVisibilityApplier { - /** - * Performs showing IME on top of the given window. - * - * @param showInputToken a token that represents the requester to show IME - * @param statsToken the token tracking the current IME request - * @param resultReceiver if non-null, this will be called back to the caller when - * it has processed request to tell what it has done - * @param reason yhe reason for requesting to show IME - * @param userId the target user when performing show IME - */ - default void performShowIme(IBinder showInputToken, @NonNull ImeTracker.Token statsToken, - @InputMethod.ShowFlags int showFlags, ResultReceiver resultReceiver, - @SoftInputShowHideReason int reason, @UserIdInt int userId) { - } - - /** - * Performs hiding IME to the given window - * - * @param hideInputToken a token that represents the requester to hide IME - * @param statsToken the token tracking the current IME request - * @param resultReceiver if non-null, this will be called back to the caller when - * it has processed request to tell what it has done - * @param reason the reason for requesting to hide IME - * @param userId the target user when performing hide IME - */ - default void performHideIme(IBinder hideInputToken, @NonNull ImeTracker.Token statsToken, - ResultReceiver resultReceiver, @SoftInputShowHideReason int reason, - @UserIdInt int userId) { - } - - /** - * Applies the IME visibility from {@link android.inputmethodservice.InputMethodService} with - * according to the given visibility state. - * - * @param windowToken the token of a window for applying the IME visibility - * @param statsToken the token tracking the current IME request - * @param state the new IME visibility state for the applier to handle - * @param userId the target user when applying the IME visibility state - */ - default void applyImeVisibility(IBinder windowToken, @NonNull ImeTracker.Token statsToken, - @ImeVisibilityStateComputer.VisibilityState int state, @UserIdInt int userId) { - } - - /** - * Updates the IME Z-ordering relative to the given window. - * - * This used to adjust the IME relative layer of the window during - * {@link InputMethodManagerService} is in switching IME clients. - * - * @param windowToken the token of a window to update the Z-ordering relative to the IME - */ - default void updateImeLayeringByTarget(IBinder windowToken) { - // TODO: add a method in WindowManagerInternal to call DC#updateImeInputAndControlTarget - // here to end up updating IME layering after IMMS#attachNewInputLocked called. - } - - /** - * Shows the IME screenshot and attach it to the given IME target window. - * - * @param windowToken the token of a window to show the IME screenshot - * @param displayId the unique id to identify the display - * @param userId the target user when when showing the IME screenshot - * @return {@code true} if success, {@code false} otherwise - */ - default boolean showImeScreenshot(@NonNull IBinder windowToken, int displayId, - @UserIdInt int userId) { - return false; - } - - /** - * Removes the IME screenshot on the given display. - * - * @param displayId the target display of showing IME screenshot - * @param userId the target user of showing IME screenshot - * @return {@code true} if success, {@code false} otherwise - */ - default boolean removeImeScreenshot(int displayId, @UserIdInt int userId) { - return false; - } -} diff --git a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java index afc10290ee02..e1aa3a2ee177 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java @@ -70,7 +70,7 @@ final class InputMethodBindingController { /** Time in milliseconds that the IME service has to bind before it is reconnected. */ static final long TIME_TO_RECONNECT = 3 * 1000; - @UserIdInt final int mUserId; + @UserIdInt private final int mUserId; @NonNull private final InputMethodManagerService mService; @NonNull private final Context mContext; @NonNull private final AutofillSuggestionsController mAutofillController; @@ -657,4 +657,9 @@ final class InputMethodBindingController { int getDeviceIdToShowIme() { return mDeviceIdToShowIme; } + + @UserIdInt + int getUserId() { + return mUserId; + } } diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 3c74b23fc8c2..78d501c225f8 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -90,7 +90,6 @@ import android.os.Debug; import android.os.Handler; import android.os.IBinder; import android.os.LocaleList; -import android.os.Looper; import android.os.Message; import android.os.Process; import android.os.RemoteException; @@ -321,6 +320,19 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. && Flags.concurrentInputMethods(); } + /** + * Figures out the target IME user ID for a given {@link Binder} IPC. + * + * @param callingProcessUserId the user ID of the calling process + * @return User ID to be used for this {@link Binder} call. + */ + @GuardedBy("ImfLock.class") + @UserIdInt + @BinderThread + private int resolveImeUserIdLocked(@UserIdInt int callingProcessUserId) { + return mExperimentalConcurrentMultiUserModeEnabled ? callingProcessUserId : mCurrentUserId; + } + final Context mContext; final Resources mRes; private final Handler mHandler; @@ -511,16 +523,6 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. private final WeakHashMap<IBinder, Boolean> mFocusedWindowPerceptible = new WeakHashMap<>(); /** - * The token we have made for the currently active input method, to - * identify it in the future. - */ - @GuardedBy("ImfLock.class") - @Nullable - IBinder getCurTokenLocked() { - return getInputMethodBindingController(mCurrentUserId).getCurToken(); - } - - /** * The displayId of current active input method. */ @GuardedBy("ImfLock.class") @@ -1169,8 +1171,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. mIoHandler = Handler.createAsync(ioThread.getLooper()); } SystemLocaleWrapper.onStart(context, this::onActionLocaleChanged, mHandler); - mImeTrackerService = new ImeTrackerService(serviceThreadForTesting != null - ? serviceThreadForTesting.getLooper() : Looper.getMainLooper()); + mImeTrackerService = new ImeTrackerService(mHandler); // Note: SettingsObserver doesn't register observers in its constructor. mSettingsObserver = new SettingsObserver(mHandler); mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class); @@ -1493,14 +1494,16 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. * Returns true iff the caller is identified to be the current input method with the token. * * @param token the window token given to the input method when it was started + * @param userId userId of the calling IME process * @return true if and only if non-null valid token is specified */ @GuardedBy("ImfLock.class") - private boolean calledWithValidTokenLocked(@NonNull IBinder token) { + private boolean calledWithValidTokenLocked(@NonNull IBinder token, @UserIdInt int userId) { if (token == null) { throw new InvalidParameterException("token must not be null."); } - if (token != getCurTokenLocked()) { + final var bindingController = getInputMethodBindingController(userId); + if (token != bindingController.getCurToken()) { Slog.e(TAG, "Ignoring " + Debug.getCaller() + " due to an invalid token." + " uid:" + Binder.getCallingUid() + " token:" + token); return false; @@ -1878,7 +1881,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. final var statsToken = createStatsTokenForFocusedClient(false /* show */, SoftInputShowHideReason.UNBIND_CURRENT_METHOD, userId); mVisibilityApplier.applyImeVisibility(userData.mImeBindingState.mFocusedWindow, - statsToken, STATE_HIDE_IME, userId); + statsToken, STATE_HIDE_IME, SoftInputShowHideReason.NOT_SET /* ignore reason */, + userId); } } @@ -2051,7 +2055,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @NonNull ImeOnBackInvokedDispatcher imeDispatcher, @NonNull InputMethodBindingController bindingController) { - final int userId = bindingController.mUserId; + final int userId = bindingController.getUserId(); final var userData = getUserData(userId); // Compute the final shown display ID with validated cs.selfReportedDisplayId for this @@ -2068,8 +2072,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. // Potentially override the selected input method if the new display belongs to a virtual // device with a custom IME. String selectedMethodId = bindingController.getSelectedMethodId(); - final String deviceMethodId = computeCurrentDeviceMethodIdLocked(bindingController.mUserId, - selectedMethodId); + final String deviceMethodId = computeCurrentDeviceMethodIdLocked( + bindingController.getUserId(), selectedMethodId); if (deviceMethodId == null) { mVisibilityStateComputer.getImePolicy().setImeHiddenByDisplayPolicy(true); } else if (!Objects.equals(deviceMethodId, selectedMethodId)) { @@ -2567,7 +2571,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @GuardedBy("ImfLock.class") void clearClientSessionsLocked(@NonNull InputMethodBindingController bindingController) { - final int userId = bindingController.mUserId; + final int userId = bindingController.getUserId(); final var userData = getUserData(userId); if (bindingController.getCurMethod() != null) { // TODO(b/324907325): Remove the suppress warnings once b/324907325 is fixed. @@ -2599,9 +2603,9 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @BinderThread private void updateStatusIcon(@NonNull IBinder token, String packageName, - @DrawableRes int iconId) { + @DrawableRes int iconId, @UserIdInt int userId) { synchronized (ImfLock.class) { - if (!calledWithValidTokenLocked(token)) { + if (!calledWithValidTokenLocked(token, userId)) { return; } final long ident = Binder.clearCallingIdentity(); @@ -2743,24 +2747,26 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @BinderThread @SuppressWarnings("deprecation") - private void setImeWindowStatus(@NonNull IBinder token, int vis, int backDisposition) { + private void setImeWindowStatus(@NonNull IBinder token, int vis, int backDisposition, + @UserIdInt int userId) { final int topFocusedDisplayId = mWindowManagerInternal.getTopFocusedDisplayId(); synchronized (ImfLock.class) { - if (!calledWithValidTokenLocked(token)) { + if (!calledWithValidTokenLocked(token, userId)) { return; } + final var bindingController = getInputMethodBindingController(userId); // Skip update IME status when current token display is not same as focused display. // Note that we still need to update IME status when focusing external display // that does not support system decoration and fallback to show IME on default // display since it is intentional behavior. - final int tokenDisplayId = getCurTokenDisplayIdLocked(); + final int tokenDisplayId = bindingController.getCurTokenDisplayId(); if (tokenDisplayId != topFocusedDisplayId && tokenDisplayId != FALLBACK_DISPLAY_ID) { return; } mImeWindowVis = vis; mBackDisposition = backDisposition; - updateSystemUiLocked(vis, backDisposition); + updateSystemUiLocked(vis, backDisposition, userId); } final boolean dismissImeOnBackKeyPressed; @@ -2780,9 +2786,10 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } @BinderThread - private void reportStartInput(@NonNull IBinder token, IBinder startInputToken) { + private void reportStartInput(@NonNull IBinder token, IBinder startInputToken, + @UserIdInt int userId) { synchronized (ImfLock.class) { - if (!calledWithValidTokenLocked(token)) { + if (!calledWithValidTokenLocked(token, userId)) { return; } final IBinder targetWindow = mImeTargetWindowMap.get(startInputToken); @@ -2922,6 +2929,9 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. final var userData = getUserData(userId); userData.mSwitchingController.resetCircularListLocked(mContext, settings); userData.mHardwareKeyboardShortcutController.update(settings); + + final var bindingController = getInputMethodBindingController(userId); + bindingController.setSelectedMethodId(id); } @GuardedBy("ImfLock.class") @@ -3107,19 +3117,19 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. int lastClickToolType, ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.showSoftInput"); - int uid = Binder.getCallingUid(); + final int uid = Binder.getCallingUid(); + final int callingUserId = UserHandle.getUserId(uid); ImeTracing.getInstance().triggerManagerServiceDump( "InputMethodManagerService#showSoftInput", mDumper); synchronized (ImfLock.class) { - if (!canInteractWithImeLocked(uid, client, "showSoftInput", statsToken)) { + final int userId = resolveImeUserIdLocked(callingUserId); + if (!canInteractWithImeLocked(uid, client, "showSoftInput", statsToken, + userId)) { ImeTracker.forLogging().onFailed( statsToken, ImeTracker.PHASE_SERVER_CLIENT_FOCUSED); Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); return false; } - // TODO(b/305849394): Create a utility method for the following policy. - final int userId = mExperimentalConcurrentMultiUserModeEnabled - ? UserHandle.getCallingUserId() : mCurrentUserId; final long ident = Binder.clearCallingIdentity(); final var userData = getUserData(userId); try { @@ -3271,13 +3281,15 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. try { ImeTracing.getInstance().triggerManagerServiceDump( "InputMethodManagerService#startStylusHandwriting", mDumper); - int uid = Binder.getCallingUid(); + final int uid = Binder.getCallingUid(); + final int callingUserId = UserHandle.getUserId(uid); synchronized (ImfLock.class) { + final int userId = resolveImeUserIdLocked(callingUserId); if (!acceptingDelegation) { mHwController.clearPendingHandwritingDelegation(); } if (!canInteractWithImeLocked(uid, client, "startStylusHandwriting", - null /* statsToken */)) { + null /* statsToken */, userId)) { return false; } if (!hasSupportedStylusLocked()) { @@ -3287,7 +3299,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } final long ident = Binder.clearCallingIdentity(); try { - final var bindingController = getInputMethodBindingController(mCurrentUserId); + final var bindingController = getInputMethodBindingController(userId); if (!bindingController.supportsStylusHandwriting()) { Slog.w(TAG, "Stylus HW unsupported by IME. Ignoring startStylusHandwriting()"); @@ -3537,11 +3549,13 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. public boolean hideSoftInput(IInputMethodClient client, IBinder windowToken, @NonNull ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags, ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) { - int uid = Binder.getCallingUid(); + final int uid = Binder.getCallingUid(); + final int callingUserId = UserHandle.getUserId(uid); ImeTracing.getInstance().triggerManagerServiceDump( "InputMethodManagerService#hideSoftInput", mDumper); synchronized (ImfLock.class) { - if (!canInteractWithImeLocked(uid, client, "hideSoftInput", statsToken)) { + final int userId = resolveImeUserIdLocked(callingUserId); + if (!canInteractWithImeLocked(uid, client, "hideSoftInput", statsToken, userId)) { if (isInputShownLocked()) { ImeTracker.forLogging().onFailed( statsToken, ImeTracker.PHASE_SERVER_CLIENT_FOCUSED); @@ -3551,9 +3565,6 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } return false; } - // TODO(b/305849394): Create a utility method for the following policy. - final int userId = mExperimentalConcurrentMultiUserModeEnabled - ? UserHandle.getCallingUserId() : mCurrentUserId; final long ident = Binder.clearCallingIdentity(); final var userData = getUserData(userId); try { @@ -3588,9 +3599,9 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @Override @IInputMethodManagerImpl.PermissionVerified(Manifest.permission.TEST_INPUT_METHOD) public void hideSoftInputFromServerForTest() { + final int callingUserId = UserHandle.getCallingUserId(); synchronized (ImfLock.class) { - // TODO(b/305849394): Get userId from caller. - final int userId = mCurrentUserId; + final int userId = resolveImeUserIdLocked(callingUserId); final var userData = getUserData(userId); hideCurrentInputLocked(userData.mImeBindingState.mFocusedWindow, 0 /* flags */, SoftInputShowHideReason.HIDE_SOFT_INPUT, userId); @@ -3856,7 +3867,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. + " cs=" + cs); } - final int userId = bindingController.mUserId; + final int userId = bindingController.getUserId(); final var userData = getUserData(userId); final boolean sameWindowFocused = userData.mImeBindingState.mFocusedWindow == windowToken; final boolean isTextEditor = (startInputFlags & StartInputFlags.IS_TEXT_EDITOR) != 0; @@ -3890,7 +3901,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. null, null, null, null, -1, false); } - userData.mImeBindingState = new ImeBindingState(bindingController.mUserId, windowToken, + userData.mImeBindingState = new ImeBindingState(bindingController.getUserId(), windowToken, softInputMode, cs, editorInfo); mFocusedWindowPerceptible.put(windowToken, true); @@ -3951,9 +3962,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @GuardedBy("ImfLock.class") private boolean canInteractWithImeLocked(int uid, IInputMethodClient client, String methodName, - @Nullable ImeTracker.Token statsToken) { - // TODO(b/305849394): Get userId from callers. - final int userId = mCurrentUserId; + @Nullable ImeTracker.Token statsToken, @UserIdInt int userId) { final var userData = getUserData(userId); if (userData.mCurClient == null || client == null || userData.mCurClient.mClient.asBinder() != client.asBinder()) { @@ -4000,15 +4009,14 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @Override public void showInputMethodPickerFromClient(IInputMethodClient client, int auxiliarySubtypeMode) { + final int callingUserId = UserHandle.getCallingUserId(); synchronized (ImfLock.class) { if (!canShowInputMethodPickerLocked(client)) { Slog.w(TAG, "Ignoring showInputMethodPickerFromClient of uid " + Binder.getCallingUid() + ": " + client); return; } - // TODO(b/305849394): Create a utility method for the following policy. - final int userId = mExperimentalConcurrentMultiUserModeEnabled - ? UserHandle.getCallingUserId() : mCurrentUserId; + final int userId = resolveImeUserIdLocked(callingUserId); final var userData = getUserData(userId); // Always call subtype picker, because subtype picker is a superset of input method // picker. @@ -4048,10 +4056,10 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. private void setInputMethod(@NonNull IBinder token, String id, @UserIdInt int userId) { final int callingUid = Binder.getCallingUid(); synchronized (ImfLock.class) { - if (!calledWithValidTokenLocked(token)) { + if (!calledWithValidTokenLocked(token, userId)) { return; } - final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId); + final InputMethodSettings settings = InputMethodSettingsRepository.get(userId); final InputMethodInfo imi = settings.getMethodMap().get(id); if (imi == null || !canCallerAccessInputMethod( imi.getPackageName(), callingUid, userId, settings)) { @@ -4066,10 +4074,10 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. InputMethodSubtype subtype, @UserIdInt int userId) { final int callingUid = Binder.getCallingUid(); synchronized (ImfLock.class) { - if (!calledWithValidTokenLocked(token)) { + if (!calledWithValidTokenLocked(token, userId)) { return; } - final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId); + final InputMethodSettings settings = InputMethodSettingsRepository.get(userId); final InputMethodInfo imi = settings.getMethodMap().get(id); if (imi == null || !canCallerAccessInputMethod( imi.getPackageName(), callingUid, userId, settings)) { @@ -4087,7 +4095,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @BinderThread private boolean switchToPreviousInputMethod(@NonNull IBinder token, @UserIdInt int userId) { synchronized (ImfLock.class) { - if (!calledWithValidTokenLocked(token)) { + if (!calledWithValidTokenLocked(token, userId)) { return false; } final var bindingController = getInputMethodBindingController(userId); @@ -4169,7 +4177,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. private boolean switchToNextInputMethod(@NonNull IBinder token, boolean onlyCurrentIme, @UserIdInt int userId) { synchronized (ImfLock.class) { - if (!calledWithValidTokenLocked(token)) { + if (!calledWithValidTokenLocked(token, userId)) { return false; } return switchToNextInputMethodLocked(token, onlyCurrentIme, userId); @@ -4196,7 +4204,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. private boolean shouldOfferSwitchingToNextInputMethod(@NonNull IBinder token, @UserIdInt int userId) { synchronized (ImfLock.class) { - if (!calledWithValidTokenLocked(token)) { + if (!calledWithValidTokenLocked(token, userId)) { return false; } final var bindingController = getInputMethodBindingController(userId); @@ -4324,13 +4332,11 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. return Binder.withCleanCallingIdentity(() -> { final int curTokenDisplayId; synchronized (ImfLock.class) { + final int userId = resolveImeUserIdLocked(callingUserId); if (!canInteractWithImeLocked(callingUid, client, - "getInputMethodWindowVisibleHeight", null /* statsToken */)) { + "getInputMethodWindowVisibleHeight", null /* statsToken */, userId)) { return 0; } - // TODO(b/305849394): Create a utility method for the following policy. - final int userId = mExperimentalConcurrentMultiUserModeEnabled - ? callingUserId : mCurrentUserId; final var bindingController = getInputMethodBindingController(userId); // This should probably use the caller's display id, but because this is unsupported // and maintained only for compatibility, there's no point in fixing it. @@ -4459,10 +4465,12 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @IInputMethodManagerImpl.PermissionVerified(Manifest.permission.TEST_INPUT_METHOD) @Override public void addVirtualStylusIdForTestSession(IInputMethodClient client) { - int uid = Binder.getCallingUid(); + final int uid = Binder.getCallingUid(); + final int callingUserId = UserHandle.getUserId(uid); synchronized (ImfLock.class) { + final int userId = resolveImeUserIdLocked(callingUserId); if (!canInteractWithImeLocked(uid, client, "addVirtualStylusIdForTestSession", - null /* statsToken */)) { + null /* statsToken */, userId)) { return; } final long ident = Binder.clearCallingIdentity(); @@ -4486,10 +4494,12 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @Override public void setStylusWindowIdleTimeoutForTest( IInputMethodClient client, @DurationMillisLong long timeout) { - int uid = Binder.getCallingUid(); + final int uid = Binder.getCallingUid(); + final int callingUserId = UserHandle.getUserId(uid); synchronized (ImfLock.class) { + final int userId = resolveImeUserIdLocked(callingUserId); if (!canInteractWithImeLocked(uid, client, "setStylusWindowIdleTimeoutForTest", - null /* statsToken */)) { + null /* statsToken */, userId)) { return; } final long ident = Binder.clearCallingIdentity(); @@ -4658,7 +4668,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. try { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.applyImeVisibility"); synchronized (ImfLock.class) { - if (!calledWithValidTokenLocked(token)) { + if (!calledWithValidTokenLocked(token, userId)) { ImeTracker.forLogging().onFailed(statsToken, ImeTracker.PHASE_SERVER_CURRENT_ACTIVE_IME); return; @@ -4669,7 +4679,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. windowToken, userId); mVisibilityApplier.applyImeVisibility(requestToken, statsToken, setVisible ? ImeVisibilityStateComputer.STATE_SHOW_IME - : ImeVisibilityStateComputer.STATE_HIDE_IME, userId); + : ImeVisibilityStateComputer.STATE_HIDE_IME, + SoftInputShowHideReason.NOT_SET /* ignore reason */, userId); } } finally { Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); @@ -4757,7 +4768,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. try { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.hideMySoftInput"); synchronized (ImfLock.class) { - if (!calledWithValidTokenLocked(token)) { + if (!calledWithValidTokenLocked(token, userId)) { ImeTracker.forLogging().onFailed(statsToken, ImeTracker.PHASE_SERVER_CURRENT_ACTIVE_IME); return; @@ -4796,7 +4807,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. try { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.showMySoftInput"); synchronized (ImfLock.class) { - if (!calledWithValidTokenLocked(token)) { + if (!calledWithValidTokenLocked(token, userId)) { ImeTracker.forLogging().onFailed(statsToken, ImeTracker.PHASE_SERVER_CURRENT_ACTIVE_IME); return; @@ -4829,11 +4840,10 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } } + @GuardedBy("ImfLock.class") @VisibleForTesting - ImeVisibilityApplier getVisibilityApplier() { - synchronized (ImfLock.class) { - return mVisibilityApplier; - } + DefaultImeVisibilityApplier getVisibilityApplierLocked() { + return mVisibilityApplier; } void onApplyImeVisibilityFromComputer(IBinder windowToken, @NonNull ImeTracker.Token statsToken, @@ -6012,7 +6022,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. private void reportFullscreenMode(@NonNull IBinder token, boolean fullscreen, @UserIdInt int userId) { synchronized (ImfLock.class) { - if (!calledWithValidTokenLocked(token)) { + if (!calledWithValidTokenLocked(token, userId)) { return; } final var userData = getUserData(userId); @@ -6842,13 +6852,13 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @BinderThread @Override public void setImeWindowStatusAsync(int vis, int backDisposition) { - mImms.setImeWindowStatus(mToken, vis, backDisposition); + mImms.setImeWindowStatus(mToken, vis, backDisposition, mUserId); } @BinderThread @Override public void reportStartInputAsync(IBinder startInputToken) { - mImms.reportStartInput(mToken, startInputToken); + mImms.reportStartInput(mToken, startInputToken, mUserId); } @BinderThread @@ -6932,7 +6942,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @BinderThread @Override public void updateStatusIconAsync(String packageName, @DrawableRes int iconId) { - mImms.updateStatusIcon(mToken, packageName, iconId); + mImms.updateStatusIcon(mToken, packageName, iconId, mUserId); } @BinderThread @@ -6999,7 +7009,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @Override public void switchKeyboardLayoutAsync(int direction) { synchronized (ImfLock.class) { - if (!mImms.calledWithValidTokenLocked(mToken)) { + if (!mImms.calledWithValidTokenLocked(mToken, mUserId)) { return; } final long ident = Binder.clearCallingIdentity(); diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java index 381b66735e9a..a0aad5215f00 100644 --- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java +++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java @@ -225,19 +225,20 @@ public class ContextHubService extends IContextHubService.Stub { @Override public void handleNanoappMessage(short hostEndpointId, NanoAppMessage message, List<String> nanoappPermissions, List<String> messagePermissions) { - if (Flags.reliableMessageImplementation() + // Only process the message normally if not using test mode manager or if + // the test mode manager call returned false as this indicates it did not + // process the message. + boolean useTestModeManager = Flags.reliableMessageImplementation() && Flags.reliableMessageTestModeBehavior() - && mIsTestModeEnabled.get() - && mTestModeManager.handleNanoappMessage(() -> { - handleClientMessageCallback(mContextHubId, hostEndpointId, message, - nanoappPermissions, messagePermissions); + && mIsTestModeEnabled.get(); + if (!useTestModeManager + || !mTestModeManager.handleNanoappMessage(() -> { + handleClientMessageCallback(mContextHubId, hostEndpointId, + message, nanoappPermissions, messagePermissions); }, message)) { - // The ContextHubTestModeManager handled the nanoapp message, so return here. - return; + handleClientMessageCallback(mContextHubId, hostEndpointId, + message, nanoappPermissions, messagePermissions); } - - handleClientMessageCallback(mContextHubId, hostEndpointId, message, - nanoappPermissions, messagePermissions); } @Override diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java b/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java index 3051379d7b35..1a449e024ee1 100644 --- a/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java +++ b/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java @@ -618,12 +618,14 @@ import java.util.concurrent.atomic.AtomicInteger; /** * Processes message transactions, starting and completing them as needed. + * <p> * This function is called when adding a message transaction or when a timer * expires for an existing message transaction's retry or timeout. The * internal processing loop will iterate at most twice as if one iteration * completes a transaction, the next iteration can only start new transactions. * If the first iteration does not complete any transaction, the loop will * only iterate once. + * <p> */ private synchronized void processMessageTransactions() { if (!Flags.reliableMessageRetrySupportService()) { diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java index 6eac72d84fd4..173fc5c86dd3 100644 --- a/services/core/java/com/android/server/pm/InstallPackageHelper.java +++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java @@ -2504,13 +2504,13 @@ final class InstallPackageHelper { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } - private void enableRestrictedSettings(String pkgName, int appId, int userId) { + private void setAccessRestrictedSettingsMode(String pkgName, int appId, int userId, int mode) { final AppOpsManager appOpsManager = mPm.mContext.getSystemService(AppOpsManager.class); final int uid = UserHandle.getUid(userId, appId); appOpsManager.setMode(AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS, uid, pkgName, - AppOpsManager.MODE_ERRORED); + mode); } /** @@ -2888,8 +2888,21 @@ final class InstallPackageHelper { mPm.notifyPackageChanged(packageName, request.getAppId()); } - if (!android.permission.flags.Flags.enhancedConfirmationModeApisEnabled() - || !android.security.Flags.extendEcmToAllSettings()) { + // Set the OP_ACCESS_RESTRICTED_SETTINGS op, which is used by ECM (see {@link + // EnhancedConfirmationManager}) as a persistent state denoting whether an app is + // currently guarded by ECM, not guarded by ECM, or (in Android V+) that this should + // be decided later. + if (android.permission.flags.Flags.enhancedConfirmationModeApisEnabled() + && android.security.Flags.extendEcmToAllSettings()) { + final int appId = request.getAppId(); + mPm.mHandler.post(() -> { + for (int userId : firstUserIds) { + // MODE_DEFAULT means that the app's guardedness will be decided lazily + setAccessRestrictedSettingsMode(packageName, appId, userId, + AppOpsManager.MODE_DEFAULT); + } + }); + } else { // Apply restricted settings on potentially dangerous packages. Needs to happen // after appOpsManager is notified of the new package if (request.getPackageSource() == PackageInstaller.PACKAGE_SOURCE_LOCAL_FILE @@ -2898,7 +2911,9 @@ final class InstallPackageHelper { final int appId = request.getAppId(); mPm.mHandler.post(() -> { for (int userId : firstUserIds) { - enableRestrictedSettings(packageName, appId, userId); + // MODE_ERRORED means that the app is explicitly guarded + setAccessRestrictedSettingsMode(packageName, appId, userId, + AppOpsManager.MODE_ERRORED); } }); } diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java index 57ea233c0a2b..4d07ab5cbb30 100644 --- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java @@ -807,7 +807,7 @@ final class DefaultPermissionGrantPolicy { getDefaultSystemHandlerActivityPackage(pm, SearchManager.INTENT_ACTION_GLOBAL_SEARCH, userId), userId, MICROPHONE_PERMISSIONS, ALWAYS_LOCATION_PERMISSIONS, - NOTIFICATION_PERMISSIONS, PHONE_PERMISSIONS, CALENDAR_PERMISSIONS); + NOTIFICATION_PERMISSIONS); } // Voice recognition @@ -875,6 +875,12 @@ final class DefaultPermissionGrantPolicy { getDefaultSystemHandlerActivityPackage(pm, ACTION_TRACK, userId), userId, SENSORS_PERMISSIONS); } + + // Allow voice search on wear + grantPermissionsToSystemPackage(pm, + getDefaultSystemHandlerActivityPackage(pm, + SearchManager.INTENT_ACTION_GLOBAL_SEARCH, userId), + userId, PHONE_PERMISSIONS, CALENDAR_PERMISSIONS); } // Print Spooler diff --git a/services/core/java/com/android/server/stats/pull/BatteryHealthUtility.java b/services/core/java/com/android/server/stats/pull/BatteryHealthUtility.java new file mode 100644 index 000000000000..e0768fe1f0e5 --- /dev/null +++ b/services/core/java/com/android/server/stats/pull/BatteryHealthUtility.java @@ -0,0 +1,89 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.stats.pull; + +import android.util.StatsEvent; + +import com.android.internal.util.FrameworkStatsLog; + +import java.math.BigInteger; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Locale; + +/** + * Utility class to redact Battery Health data from HealthServiceWrapper + * + * @hide + */ +public abstract class BatteryHealthUtility { + /** + * Create a StatsEvent corresponding to the Battery Health data, the fields + * of which are redacted to preserve users' privacy. + * The redaction consists in truncating the timestamps to the Monday of the + * corresponding week, and reducing the battery serial into the last byte + * of its MD5. + */ + public static StatsEvent buildStatsEvent(int atomTag, + android.hardware.health.BatteryHealthData data, int chargeStatus, int chargePolicy) + throws NoSuchAlgorithmException { + int manufacturingDate = secondsToWeekYYYYMMDD(data.batteryManufacturingDateSeconds); + int firstUsageDate = secondsToWeekYYYYMMDD(data.batteryFirstUsageSeconds); + long stateOfHealth = data.batteryStateOfHealth; + int partStatus = data.batteryPartStatus; + int serialHashTruncated = stringToIntHash(data.batterySerialNumber) & 0xFF; // Last byte + + return FrameworkStatsLog.buildStatsEvent(atomTag, manufacturingDate, firstUsageDate, + (int) stateOfHealth, serialHashTruncated, partStatus, chargeStatus, chargePolicy); + } + + private static int secondsToWeekYYYYMMDD(long seconds) { + Calendar calendar = Calendar.getInstance(); + long millis = seconds * 1000L; + + calendar.setTimeInMillis(millis); + + // Truncate all date information, up to week, which is rounded to + // MONDAY + calendar.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY); + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MILLISECOND, 0); + + SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd", Locale.US); + + String formattedDate = sdf.format(calendar.getTime()); + + return Integer.parseInt(formattedDate); + } + + private static int stringToIntHash(String data) throws NoSuchAlgorithmException { + if (data == null || data.isEmpty()) { + return 0; + } + + MessageDigest digest = MessageDigest.getInstance("MD5"); + byte[] hashBytes = digest.digest(data.getBytes()); + + // Convert to integer (simplest way, but potential for loss of information) + BigInteger bigInt = new BigInteger(1, hashBytes); + return bigInt.intValue(); + } +} diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java index c1b825b3f8d1..0041d39f4b2b 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -119,6 +119,8 @@ import android.net.NetworkStats; import android.net.NetworkTemplate; import android.net.wifi.WifiManager; import android.os.AsyncTask; +import android.os.BatteryManager; +import android.os.BatteryProperty; import android.os.BatteryStats; import android.os.BatteryStatsInternal; import android.os.BatteryStatsManager; @@ -243,6 +245,7 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.security.NoSuchAlgorithmException; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.ArrayList; @@ -769,6 +772,7 @@ public class StatsPullAtomService extends SystemService { case FrameworkStatsLog.FULL_BATTERY_CAPACITY: case FrameworkStatsLog.BATTERY_VOLTAGE: case FrameworkStatsLog.BATTERY_CYCLE_COUNT: + case FrameworkStatsLog.BATTERY_HEALTH: synchronized (mHealthHalLock) { return pullHealthHalLocked(atomTag, data); } @@ -999,6 +1003,7 @@ public class StatsPullAtomService extends SystemService { registerFullBatteryCapacity(); registerBatteryVoltage(); registerBatteryCycleCount(); + registerBatteryHealth(); registerSettingsStats(); registerInstalledIncrementalPackages(); registerKeystoreStorageStats(); @@ -4365,7 +4370,15 @@ public class StatsPullAtomService extends SystemService { ); } - int pullHealthHalLocked(int atomTag, List<StatsEvent> pulledData) { + private void registerBatteryHealth() { + int tagId = FrameworkStatsLog.BATTERY_HEALTH; + mStatsManager.setPullAtomCallback(tagId, + null, // use default PullAtomMetadata values + DIRECT_EXECUTOR, mStatsCallbackImpl); + } + + @GuardedBy("mHealthHalLock") + private int pullHealthHalLocked(int atomTag, List<StatsEvent> pulledData) { if (mHealthService == null) { return StatsManager.PULL_SKIP; } @@ -4396,6 +4409,44 @@ public class StatsPullAtomService extends SystemService { case FrameworkStatsLog.BATTERY_CYCLE_COUNT: pulledValue = healthInfo.batteryCycleCount; break; + case FrameworkStatsLog.BATTERY_HEALTH: + android.hardware.health.BatteryHealthData bhd; + try { + bhd = mHealthService.getBatteryHealthData(); + } catch (RemoteException | IllegalStateException e) { + return StatsManager.PULL_SKIP; + } + if (bhd == null) { + return StatsManager.PULL_SKIP; + } + + StatsEvent batteryHealthEvent; + try { + BatteryProperty chargeStatusProperty = new BatteryProperty(); + BatteryProperty chargePolicyProperty = new BatteryProperty(); + + if (0 > mHealthService.getProperty( + BatteryManager.BATTERY_PROPERTY_STATUS, chargeStatusProperty)) { + return StatsManager.PULL_SKIP; + } + if (0 > mHealthService.getProperty( + BatteryManager.BATTERY_PROPERTY_CHARGING_POLICY, + chargePolicyProperty)) { + return StatsManager.PULL_SKIP; + } + int chargeStatus = (int) chargeStatusProperty.getLong(); + int chargePolicy = (int) chargePolicyProperty.getLong(); + batteryHealthEvent = BatteryHealthUtility.buildStatsEvent( + atomTag, bhd, chargeStatus, chargePolicy); + pulledData.add(batteryHealthEvent); + + return StatsManager.PULL_SUCCESS; + } catch (RemoteException | IllegalStateException e) { + Slog.e(TAG, "Failed to add pulled data", e); + } catch (NoSuchAlgorithmException e) { + Slog.e(TAG, "Could not find message digest algorithm", e); + } + return StatsManager.PULL_SKIP; default: return StatsManager.PULL_SKIP; } diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java index 3e177c9fe8c6..a8dcaa8a90e1 100644 --- a/services/core/java/com/android/server/wm/ActivityClientController.java +++ b/services/core/java/com/android/server/wm/ActivityClientController.java @@ -102,7 +102,7 @@ import android.window.TransitionInfo; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.AssistUtils; import com.android.internal.policy.IKeyguardDismissCallback; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.server.LocalServices; import com.android.server.Watchdog; import com.android.server.pm.KnownPackages; diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index dce496d42585..7d7592fa0838 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -386,7 +386,7 @@ import com.android.internal.content.ReferrerIntent; import com.android.internal.os.TimeoutRecord; import com.android.internal.os.TransferPipe; import com.android.internal.policy.AttributeCache; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.internal.util.XmlUtils; import com.android.modules.utils.TypedXmlPullParser; import com.android.modules.utils.TypedXmlSerializer; diff --git a/services/core/java/com/android/server/wm/ActivityRefresher.java b/services/core/java/com/android/server/wm/ActivityRefresher.java index 23a97089fd60..056c09eb0ce9 100644 --- a/services/core/java/com/android/server/wm/ActivityRefresher.java +++ b/services/core/java/com/android/server/wm/ActivityRefresher.java @@ -27,7 +27,7 @@ import android.content.res.Configuration; import android.os.Handler; import android.os.RemoteException; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.internal.util.ArrayUtils; import java.util.ArrayList; diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 717c3998aba4..5bfe9d744975 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -127,7 +127,7 @@ import android.window.RemoteTransition; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.HeavyWeightSwitcherActivity; import com.android.internal.app.IVoiceInteractor; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.server.am.PendingIntentRecord; import com.android.server.pm.InstantAppResolver; import com.android.server.pm.PackageArchiver; diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 2109f5d5ab8f..dded1ca93270 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -260,7 +260,7 @@ import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.os.TransferPipe; import com.android.internal.policy.AttributeCache; import com.android.internal.policy.KeyguardDismissCallback; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastPrintWriter; import com.android.internal.util.FrameworkStatsLog; diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java index cd5576fca2f9..d65a106a1079 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java @@ -148,7 +148,7 @@ import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.content.ReferrerIntent; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.internal.util.ArrayUtils; import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.LocalServices; diff --git a/services/core/java/com/android/server/wm/AnrController.java b/services/core/java/com/android/server/wm/AnrController.java index 0013c5c63798..80671effdc98 100644 --- a/services/core/java/com/android/server/wm/AnrController.java +++ b/services/core/java/com/android/server/wm/AnrController.java @@ -345,7 +345,7 @@ class AnrController { null /* processCpuTracker */, null /* lastPids */, CompletableFuture.completedFuture(nativePids), null /* logExceptionCreatingFile */, "Pre-dump", criticalEvents, - Runnable::run, null/* AnrLatencyTracker */); + null /* extraHeaders */, Runnable::run, null/* AnrLatencyTracker */); if (tracesFile != null) { tracesFile.renameTo( new File(tracesFile.getParent(), tracesFile.getName() + "_pre")); diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java index c2b9128daac5..bc7e84ae9f86 100644 --- a/services/core/java/com/android/server/wm/AppTransition.java +++ b/services/core/java/com/android/server/wm/AppTransition.java @@ -135,7 +135,7 @@ import android.view.animation.TranslateAnimation; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.policy.TransitionAnimation; import com.android.internal.protolog.common.LogLevel; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.internal.util.DumpUtils.Dump; import com.android.internal.util.function.pooled.PooledLambda; import com.android.internal.util.function.pooled.PooledPredicate; diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java index c55a1003f402..44b414f1ea7e 100644 --- a/services/core/java/com/android/server/wm/AppTransitionController.java +++ b/services/core/java/com/android/server/wm/AppTransitionController.java @@ -92,7 +92,7 @@ import android.view.WindowManager.TransitionType; import android.window.ITaskFragmentOrganizer; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; diff --git a/services/core/java/com/android/server/wm/BLASTSyncEngine.java b/services/core/java/com/android/server/wm/BLASTSyncEngine.java index a8cc2ae161cf..4554b210e3fd 100644 --- a/services/core/java/com/android/server/wm/BLASTSyncEngine.java +++ b/services/core/java/com/android/server/wm/BLASTSyncEngine.java @@ -30,7 +30,7 @@ import android.util.Slog; import android.view.SurfaceControl; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import java.util.ArrayList; diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java index 14ae918129f7..cb690394480e 100644 --- a/services/core/java/com/android/server/wm/BackNavigationController.java +++ b/services/core/java/com/android/server/wm/BackNavigationController.java @@ -61,7 +61,7 @@ import android.window.TaskSnapshot; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.policy.TransitionAnimation; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.window.flags.Flags; import java.io.PrintWriter; diff --git a/services/core/java/com/android/server/wm/BlackFrame.java b/services/core/java/com/android/server/wm/BlackFrame.java index 0b2c851c4366..ba4ab7df8270 100644 --- a/services/core/java/com/android/server/wm/BlackFrame.java +++ b/services/core/java/com/android/server/wm/BlackFrame.java @@ -22,7 +22,7 @@ import android.graphics.Rect; import android.view.Surface.OutOfResourcesException; import android.view.SurfaceControl; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import java.io.PrintWriter; import java.util.function.Supplier; diff --git a/services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java b/services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java index 0c751cfe4f46..92040177ad9e 100644 --- a/services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java +++ b/services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java @@ -32,7 +32,7 @@ import android.content.res.Configuration; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.ProtoLogGroup; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.window.flags.Flags; /** diff --git a/services/core/java/com/android/server/wm/CameraStateMonitor.java b/services/core/java/com/android/server/wm/CameraStateMonitor.java index ea7edea7d4b3..a54141ce5230 100644 --- a/services/core/java/com/android/server/wm/CameraStateMonitor.java +++ b/services/core/java/com/android/server/wm/CameraStateMonitor.java @@ -26,7 +26,7 @@ import android.os.Handler; import android.util.ArraySet; import android.util.Slog; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import java.util.ArrayList; import java.util.List; diff --git a/services/core/java/com/android/server/wm/CompatModePackages.java b/services/core/java/com/android/server/wm/CompatModePackages.java index b795987f1d11..44202a2ae58e 100644 --- a/services/core/java/com/android/server/wm/CompatModePackages.java +++ b/services/core/java/com/android/server/wm/CompatModePackages.java @@ -48,7 +48,7 @@ import android.util.SparseArray; import android.util.SparseBooleanArray; import android.util.Xml; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.modules.utils.TypedXmlPullParser; import com.android.modules.utils.TypedXmlSerializer; diff --git a/services/core/java/com/android/server/wm/ContentRecorder.java b/services/core/java/com/android/server/wm/ContentRecorder.java index d70a88062ebd..7e7073c4a52c 100644 --- a/services/core/java/com/android/server/wm/ContentRecorder.java +++ b/services/core/java/com/android/server/wm/ContentRecorder.java @@ -42,7 +42,7 @@ import android.view.DisplayInfo; import android.view.SurfaceControl; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.server.display.feature.DisplayManagerFlags; /** diff --git a/services/core/java/com/android/server/wm/ContentRecordingController.java b/services/core/java/com/android/server/wm/ContentRecordingController.java index b5890856fa6f..283f819fc6ae 100644 --- a/services/core/java/com/android/server/wm/ContentRecordingController.java +++ b/services/core/java/com/android/server/wm/ContentRecordingController.java @@ -23,7 +23,7 @@ import android.annotation.Nullable; import android.view.ContentRecordingSession; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; /** * Orchestrates the handoff between displays if the recording session changes, and keeps track of diff --git a/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java b/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java index be44629a1fcf..3b999549b302 100644 --- a/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java +++ b/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java @@ -36,7 +36,7 @@ import android.window.WindowContainerTransaction; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.display.BrightnessSynchronizer; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.server.wm.utils.DisplayInfoOverrides.DisplayInfoFieldsUpdater; import com.android.window.flags.Flags; diff --git a/services/core/java/com/android/server/wm/DimmerAnimationHelper.java b/services/core/java/com/android/server/wm/DimmerAnimationHelper.java index 735c73a990cb..22fa88f12386 100644 --- a/services/core/java/com/android/server/wm/DimmerAnimationHelper.java +++ b/services/core/java/com/android/server/wm/DimmerAnimationHelper.java @@ -29,7 +29,7 @@ import android.util.Log; import android.util.proto.ProtoOutputStream; import android.view.SurfaceControl; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import java.io.PrintWriter; diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java index 0006bd250087..def495f3daa5 100644 --- a/services/core/java/com/android/server/wm/DisplayArea.java +++ b/services/core/java/com/android/server/wm/DisplayArea.java @@ -45,7 +45,7 @@ import android.window.DisplayAreaInfo; import android.window.IDisplayAreaOrganizer; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.server.policy.WindowManagerPolicy; import java.io.PrintWriter; diff --git a/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java b/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java index 3dc3be9abf74..8f471d797904 100644 --- a/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java +++ b/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java @@ -33,7 +33,7 @@ import android.window.IDisplayAreaOrganizer; import android.window.IDisplayAreaOrganizerController; import android.window.WindowContainerToken; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import java.util.ArrayList; import java.util.HashMap; diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java index 8c52288c4be5..bb596cc829c9 100644 --- a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java +++ b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java @@ -42,7 +42,7 @@ import android.window.WindowContainerToken; import com.android.internal.annotations.Keep; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.server.policy.WindowManagerPolicy; import java.util.ArrayList; diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 7ffffe97b646..b5b937775333 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -254,7 +254,7 @@ import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.internal.util.ToBooleanFunction; import com.android.internal.util.function.pooled.PooledLambda; import com.android.internal.util.function.pooled.PooledPredicate; diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 4a59fc2a8f15..b36fbd34866c 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -132,7 +132,7 @@ import com.android.internal.os.BackgroundThread; import com.android.internal.policy.ForceShowNavBarSettingsObserver; import com.android.internal.policy.GestureNavigationSettingsObserver; import com.android.internal.policy.ScreenDecorationsUtils; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.internal.statusbar.LetterboxDetails; import com.android.internal.util.ScreenshotHelper; import com.android.internal.util.ScreenshotRequest; diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java index afcf364d4729..f3ccc3b5aef8 100644 --- a/services/core/java/com/android/server/wm/DisplayRotation.java +++ b/services/core/java/com/android/server/wm/DisplayRotation.java @@ -80,7 +80,7 @@ import android.window.WindowContainerTransaction; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.server.LocalServices; import com.android.server.UiThread; import com.android.server.policy.WindowManagerPolicy; diff --git a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java index 6ecafdb03d20..beb376788367 100644 --- a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java @@ -42,7 +42,7 @@ import android.widget.Toast; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.server.UiThread; /** diff --git a/services/core/java/com/android/server/wm/DisplayRotationReversionController.java b/services/core/java/com/android/server/wm/DisplayRotationReversionController.java index 50b29ec965ea..f94b8c41b24a 100644 --- a/services/core/java/com/android/server/wm/DisplayRotationReversionController.java +++ b/services/core/java/com/android/server/wm/DisplayRotationReversionController.java @@ -26,7 +26,7 @@ import android.annotation.Nullable; import android.content.ActivityInfoProto; import android.view.Surface; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; /** * Defines the behavior of reversion from device rotation overrides. diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java b/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java index c79565ae79fa..8bd8098b6be9 100644 --- a/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java +++ b/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static android.os.UserHandle.USER_SYSTEM; import static android.view.Display.TYPE_VIRTUAL; import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY; import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL; @@ -27,6 +28,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.app.WindowConfiguration; import android.os.Environment; import android.util.ArrayMap; @@ -42,6 +44,7 @@ import com.android.internal.util.XmlUtils; import com.android.modules.utils.TypedXmlPullParser; import com.android.modules.utils.TypedXmlSerializer; import com.android.server.wm.DisplayWindowSettings.SettingsProvider; +import com.android.window.flags.Flags; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -53,6 +56,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Map; +import java.util.Set; /** * Implementation of {@link SettingsProvider} that reads the base settings provided in a display @@ -91,11 +95,11 @@ class DisplayWindowSettingsProvider implements SettingsProvider { @NonNull private ReadableSettings mBaseSettings; @NonNull - private final WritableSettings mOverrideSettings; + private WritableSettings mOverrideSettings; DisplayWindowSettingsProvider() { this(new AtomicFileStorage(getVendorSettingsFile()), - new AtomicFileStorage(getOverrideSettingsFile())); + new AtomicFileStorage(getOverrideSettingsFileForUser(USER_SYSTEM))); } @VisibleForTesting @@ -133,6 +137,48 @@ class DisplayWindowSettingsProvider implements SettingsProvider { mBaseSettings = new ReadableSettings(baseSettingsStorage); } + /** + * Overrides the storage that should be used to save override settings for a user. + * + * @see #DATA_DISPLAY_SETTINGS_FILE_PATH + */ + void setOverrideSettingsForUser(@UserIdInt int userId) { + if (!Flags.perUserDisplayWindowSettings()) { + return; + } + final AtomicFile settingsFile = getOverrideSettingsFileForUser(userId); + setOverrideSettingsStorage(new AtomicFileStorage(settingsFile)); + } + + /** + * Removes display override settings that are no longer associated with active displays. + * This is necessary because displays can be dynamically added or removed during + * the system's lifecycle (e.g., user switch, system server restart). + * + * @param root The root window container used to obtain the currently active displays. + */ + void removeStaleDisplaySettings(@NonNull RootWindowContainer root) { + if (!Flags.perUserDisplayWindowSettings()) { + return; + } + final Set<String> displayIdentifiers = new ArraySet<>(); + root.forAllDisplays(dc -> { + final String identifier = mOverrideSettings.getIdentifier(dc.getDisplayInfo()); + displayIdentifiers.add(identifier); + }); + mOverrideSettings.removeStaleDisplaySettings(displayIdentifiers); + } + + /** + * Overrides the storage that should be used to save override settings. + * + * @see #setOverrideSettingsForUser(int) + */ + @VisibleForTesting + void setOverrideSettingsStorage(@NonNull WritableSettingsStorage overrideSettingsStorage) { + mOverrideSettings = new WritableSettings(overrideSettingsStorage); + } + @Override @NonNull public SettingsEntry getSettings(@NonNull DisplayInfo info) { @@ -302,6 +348,12 @@ class DisplayWindowSettingsProvider implements SettingsProvider { mVirtualDisplayIdentifiers.remove(identifier); } + void removeStaleDisplaySettings(@NonNull Set<String> currentDisplayIdentifiers) { + if (mSettings.retainAll(currentDisplayIdentifiers)) { + writeSettings(); + } + } + private void writeSettings() { final FileData fileData = new FileData(); fileData.mIdentifierType = mIdentifierType; @@ -332,9 +384,14 @@ class DisplayWindowSettingsProvider implements SettingsProvider { } @NonNull - private static AtomicFile getOverrideSettingsFile() { - final File overrideSettingsFile = new File(Environment.getDataDirectory(), - DATA_DISPLAY_SETTINGS_FILE_PATH); + private static AtomicFile getOverrideSettingsFileForUser(@UserIdInt int userId) { + final File directory; + if (userId == USER_SYSTEM || !Flags.perUserDisplayWindowSettings()) { + directory = Environment.getDataDirectory(); + } else { + directory = Environment.getDataSystemCeDirectory(userId); + } + final File overrideSettingsFile = new File(directory, DATA_DISPLAY_SETTINGS_FILE_PATH); return new AtomicFile(overrideSettingsFile, WM_DISPLAY_COMMIT_TAG); } diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java index e3827aa86d9e..4be5bad644f7 100644 --- a/services/core/java/com/android/server/wm/DragState.java +++ b/services/core/java/com/android/server/wm/DragState.java @@ -64,7 +64,7 @@ import android.view.WindowManager; import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.internal.view.IDragAndDropPermissions; import com.android.server.LocalServices; import com.android.server.pm.UserManagerInternal; diff --git a/services/core/java/com/android/server/wm/EmbeddedWindowController.java b/services/core/java/com/android/server/wm/EmbeddedWindowController.java index a21ba2603ec6..c66d6596226a 100644 --- a/services/core/java/com/android/server/wm/EmbeddedWindowController.java +++ b/services/core/java/com/android/server/wm/EmbeddedWindowController.java @@ -35,7 +35,7 @@ import android.view.InputApplicationHandle; import android.view.InputChannel; import android.window.InputTransferToken; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.server.input.InputManagerService; /** diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java index 156e9f98b7a7..91c61b1bd550 100644 --- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java @@ -40,7 +40,7 @@ import android.view.inputmethod.Flags; import android.view.inputmethod.ImeTracker; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import java.io.PrintWriter; diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index 8035a298e45a..74dbd15d1399 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -66,7 +66,7 @@ import android.view.WindowManager; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.inputmethod.SoftInputShowHideReason; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.server.LocalServices; import com.android.server.inputmethod.InputMethodManagerInternal; diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java index f68b67f626f9..33dea54574e6 100644 --- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java @@ -48,7 +48,7 @@ import android.view.SurfaceControl.Transaction; import android.view.WindowInsets; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.internal.util.function.TriFunction; import com.android.server.wm.SurfaceAnimator.AnimationType; import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback; diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java index a967f7af3bb9..348384203fba 100644 --- a/services/core/java/com/android/server/wm/InsetsStateController.java +++ b/services/core/java/com/android/server/wm/InsetsStateController.java @@ -41,7 +41,7 @@ import android.view.InsetsState; import android.view.WindowInsets; import android.view.WindowInsets.Type.InsetsType; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.server.inputmethod.InputMethodManagerInternal; import java.io.PrintWriter; diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java index 0f9998cafc4e..0dadade38ddb 100644 --- a/services/core/java/com/android/server/wm/LockTaskController.java +++ b/services/core/java/com/android/server/wm/LockTaskController.java @@ -63,7 +63,7 @@ import android.util.SparseIntArray; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.policy.IKeyguardDismissCallback; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.telephony.CellBroadcastUtils; import com.android.internal.widget.LockPatternUtils; diff --git a/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java b/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java index 36c092b3f535..1a895ea22f36 100644 --- a/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java +++ b/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java @@ -35,7 +35,7 @@ import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; import android.view.WindowManager; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.server.policy.WindowManagerPolicy; import java.io.PrintWriter; diff --git a/services/core/java/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncher.java b/services/core/java/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncher.java index 4c797f8d17ea..3cf301c1d730 100644 --- a/services/core/java/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncher.java +++ b/services/core/java/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncher.java @@ -36,7 +36,7 @@ import android.window.WindowContainerTransaction; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.ProtoLogGroup; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.server.wm.DeviceStateController.DeviceState; public class PhysicalDisplaySwitchTransitionLauncher { diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java index e07b72a05123..6f2528085d74 100644 --- a/services/core/java/com/android/server/wm/RecentTasks.java +++ b/services/core/java/com/android/server/wm/RecentTasks.java @@ -80,7 +80,7 @@ import android.view.WindowInsets; import android.view.WindowManagerPolicyConstants.PointerEventListener; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.am.ActivityManagerService; diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java index 469cc647b1b5..c592caf488ad 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimation.java +++ b/services/core/java/com/android/server/wm/RecentsAnimation.java @@ -42,7 +42,7 @@ import android.os.Trace; import android.util.Slog; import android.view.IRecentsAnimationRunner; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.internal.util.function.pooled.PooledLambda; import com.android.internal.util.function.pooled.PooledPredicate; import com.android.server.wm.ActivityMetricsLogger.LaunchingState; diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java index 312c4befb0d6..6f947135b789 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimationController.java +++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java @@ -64,7 +64,7 @@ import android.window.WindowAnimationState; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.IResultReceiver; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.server.LocalServices; import com.android.server.inputmethod.InputMethodManagerInternal; import com.android.server.statusbar.StatusBarManagerInternal; diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java index 63bbb3140bfb..a8edaebd24a6 100644 --- a/services/core/java/com/android/server/wm/RemoteAnimationController.java +++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java @@ -45,7 +45,7 @@ import android.view.WindowManager; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.LogLevel; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.internal.util.FastPrintWriter; import com.android.server.wm.SurfaceAnimator.AnimationType; import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback; diff --git a/services/core/java/com/android/server/wm/RemoteDisplayChangeController.java b/services/core/java/com/android/server/wm/RemoteDisplayChangeController.java index c22b07a4efa1..e4962bffc570 100644 --- a/services/core/java/com/android/server/wm/RemoteDisplayChangeController.java +++ b/services/core/java/com/android/server/wm/RemoteDisplayChangeController.java @@ -28,7 +28,7 @@ import android.window.DisplayAreaInfo; import android.window.WindowContainerTransaction; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import java.util.ArrayList; import java.util.List; diff --git a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java index d497d8cbf9cd..243dbc78998d 100644 --- a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java +++ b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java @@ -24,7 +24,7 @@ import android.content.Intent; import android.content.pm.ActivityInfo; import android.os.Debug; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import java.util.ArrayList; import java.util.function.Consumer; diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 6abd4887645b..d1f1cab43b8a 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -141,7 +141,7 @@ import android.window.WindowContainerToken; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.ResolverActivity; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.internal.util.function.pooled.PooledLambda; import com.android.internal.util.function.pooled.PooledPredicate; import com.android.server.LocalServices; @@ -2158,6 +2158,12 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // Use Task#setBoundsUnchecked to skip checking windowing mode as the windowing mode // will be updated later after this is collected in transition. rootTask.setBoundsUnchecked(taskFragment.getBounds()); + // The exit-PIP activity resumes early for seamless transition. In certain + // scenarios, this introduces unintended addition to recents. To address this, + // we mark the root task for automatic removal from recents. This ensures that + // after the pinned activity reparents to its original task, the root task is + // automatically removed from the recents list. + rootTask.autoRemoveRecents = true; // Move the last recents animation transaction from original task to the new one. if (task.mLastRecentsAnimationTransaction != null) { diff --git a/services/core/java/com/android/server/wm/ScreenRecordingCallbackController.java b/services/core/java/com/android/server/wm/ScreenRecordingCallbackController.java index 967f415d2c11..ad4faab1e106 100644 --- a/services/core/java/com/android/server/wm/ScreenRecordingCallbackController.java +++ b/services/core/java/com/android/server/wm/ScreenRecordingCallbackController.java @@ -33,7 +33,7 @@ import android.view.ContentRecordingSession; import android.window.IScreenRecordingCallback; import com.android.internal.annotations.GuardedBy; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import java.io.PrintWriter; import java.util.ArrayList; diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java index e7bffdfd448b..3eb321804520 100644 --- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java +++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java @@ -57,7 +57,7 @@ import android.window.ScreenCapture; import com.android.internal.R; import com.android.internal.policy.TransitionAnimation; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.server.display.DisplayControl; import com.android.server.wm.SurfaceAnimator.AnimationType; import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback; diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index 4a0239bc29b2..9addce6d5f08 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -84,7 +84,7 @@ import android.window.OnBackInvokedCallbackInfo; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.logging.MetricsLoggerWrapper; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.server.LocalServices; import com.android.server.wm.WindowManagerService.H; import com.android.window.flags.Flags; diff --git a/services/core/java/com/android/server/wm/SmoothDimmer.java b/services/core/java/com/android/server/wm/SmoothDimmer.java index b5d94a2efbdf..2b4d901e8231 100644 --- a/services/core/java/com/android/server/wm/SmoothDimmer.java +++ b/services/core/java/com/android/server/wm/SmoothDimmer.java @@ -26,7 +26,7 @@ import android.view.Surface; import android.view.SurfaceControl; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; class SmoothDimmer extends Dimmer { diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java index c632714bd8ce..9cfd39688c12 100644 --- a/services/core/java/com/android/server/wm/SurfaceAnimator.java +++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java @@ -33,7 +33,7 @@ import android.view.SurfaceControl.Transaction; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.LogLevel; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import java.io.PrintWriter; import java.io.StringWriter; diff --git a/services/core/java/com/android/server/wm/SurfaceFreezer.java b/services/core/java/com/android/server/wm/SurfaceFreezer.java index 0c36d27603c8..34abf23daa2a 100644 --- a/services/core/java/com/android/server/wm/SurfaceFreezer.java +++ b/services/core/java/com/android/server/wm/SurfaceFreezer.java @@ -31,7 +31,7 @@ import android.view.SurfaceControl; import android.window.ScreenCapture; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; /** * This class handles "freezing" of an Animatable. The Animatable in question should implement diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index f9c53aa35bd8..52a1ed98a4ce 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -181,7 +181,7 @@ import android.window.WindowContainerToken; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IVoiceInteractor; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.internal.util.XmlUtils; import com.android.internal.util.function.pooled.PooledLambda; import com.android.internal.util.function.pooled.PooledPredicate; diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java index eff831552320..eaf3012a3b11 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -52,7 +52,7 @@ import android.view.SurfaceControl; import android.view.WindowManager; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.internal.util.ArrayUtils; import com.android.internal.util.function.pooled.PooledLambda; import com.android.internal.util.function.pooled.PooledPredicate; diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java index 3cd071bd329e..9b2c022df963 100644 --- a/services/core/java/com/android/server/wm/TaskFragment.java +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -106,7 +106,7 @@ import android.window.TaskFragmentInfo; import android.window.TaskFragmentOrganizerToken; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.internal.util.ToBooleanFunction; import com.android.server.am.HostingRecord; import com.android.server.pm.pkg.AndroidPackage; diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java index 26315f9cc2c3..b6b6cf2dc430 100644 --- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java @@ -59,7 +59,7 @@ import android.window.WindowContainerTransaction; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.ProtoLogGroup; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.window.flags.Flags; import java.lang.annotation.Retention; diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index b24d53b26caa..6e36d427bd13 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -57,7 +57,7 @@ import android.window.TaskSnapshot; import android.window.WindowContainerToken; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.internal.util.ArrayUtils; import java.io.PrintWriter; diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java index 9b3fb6b881c4..972dd2e382cc 100644 --- a/services/core/java/com/android/server/wm/TaskPositioner.java +++ b/services/core/java/com/android/server/wm/TaskPositioner.java @@ -59,7 +59,7 @@ import android.view.WindowManager; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.policy.TaskResizingAlgorithm; import com.android.internal.policy.TaskResizingAlgorithm.CtrlType; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import java.util.concurrent.CompletableFuture; diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index e698a3d95841..35a77022737f 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -106,7 +106,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.graphics.ColorUtils; import com.android.internal.policy.TransitionAnimation; import com.android.internal.protolog.ProtoLogGroup; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.inputmethod.InputMethodManagerInternal; import com.android.server.statusbar.StatusBarManagerInternal; diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java index 08123232866b..f4ff404c2bff 100644 --- a/services/core/java/com/android/server/wm/TransitionController.java +++ b/services/core/java/com/android/server/wm/TransitionController.java @@ -53,7 +53,7 @@ import android.window.WindowContainerTransaction; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.ProtoLogGroup; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.server.FgThread; import com.android.window.flags.Flags; diff --git a/services/core/java/com/android/server/wm/TrustedPresentationListenerController.java b/services/core/java/com/android/server/wm/TrustedPresentationListenerController.java index fa2d9bf21dee..c0dc4247a7c2 100644 --- a/services/core/java/com/android/server/wm/TrustedPresentationListenerController.java +++ b/services/core/java/com/android/server/wm/TrustedPresentationListenerController.java @@ -40,7 +40,7 @@ import android.window.ITrustedPresentationListener; import android.window.TrustedPresentationThresholds; import android.window.WindowInfosListener; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.server.wm.utils.RegionUtils; import java.io.PrintWriter; diff --git a/services/core/java/com/android/server/wm/WallpaperAnimationAdapter.java b/services/core/java/com/android/server/wm/WallpaperAnimationAdapter.java index 4a5a20e57804..fffe692a39dd 100644 --- a/services/core/java/com/android/server/wm/WallpaperAnimationAdapter.java +++ b/services/core/java/com/android/server/wm/WallpaperAnimationAdapter.java @@ -27,7 +27,7 @@ import android.util.proto.ProtoOutputStream; import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.server.wm.SurfaceAnimator.AnimationType; import java.io.PrintWriter; diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java index 43f7ecc6ab1c..3b5a6058e803 100644 --- a/services/core/java/com/android/server/wm/WallpaperController.java +++ b/services/core/java/com/android/server/wm/WallpaperController.java @@ -57,7 +57,7 @@ import android.window.ScreenCapture; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.internal.util.ToBooleanFunction; import com.android.server.wallpaper.WallpaperCropper.WallpaperCropUtils; import com.android.window.flags.Flags; diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java index b7f8505b4a65..31156de5debf 100644 --- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java +++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java @@ -31,7 +31,7 @@ import android.os.IBinder; import android.os.RemoteException; import android.util.SparseArray; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.window.flags.Flags; import java.util.function.Consumer; diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java index 8afcf0e1e05a..03342d316d1c 100644 --- a/services/core/java/com/android/server/wm/WindowAnimator.java +++ b/services/core/java/com/android/server/wm/WindowAnimator.java @@ -34,7 +34,7 @@ import android.util.TimeUtils; import android.view.Choreographer; import android.view.SurfaceControl; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.server.policy.WindowManagerPolicy; import java.io.PrintWriter; diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 1f31af68c693..325ef0d184df 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -111,7 +111,7 @@ import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.graphics.ColorUtils; import com.android.internal.protolog.common.LogLevel; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.internal.util.ToBooleanFunction; import com.android.server.wm.SurfaceAnimator.Animatable; import com.android.server.wm.SurfaceAnimator.AnimationType; diff --git a/services/core/java/com/android/server/wm/WindowContainerThumbnail.java b/services/core/java/com/android/server/wm/WindowContainerThumbnail.java index b000a9841d06..57fc4c7a6860 100644 --- a/services/core/java/com/android/server/wm/WindowContainerThumbnail.java +++ b/services/core/java/com/android/server/wm/WindowContainerThumbnail.java @@ -39,7 +39,7 @@ import android.view.SurfaceControl.Builder; import android.view.SurfaceControl.Transaction; import android.view.animation.Animation; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.server.wm.SurfaceAnimator.Animatable; import com.android.server.wm.SurfaceAnimator.AnimationType; diff --git a/services/core/java/com/android/server/wm/WindowContextListenerController.java b/services/core/java/com/android/server/wm/WindowContextListenerController.java index 21f251fbc736..cd785e5174e5 100644 --- a/services/core/java/com/android/server/wm/WindowContextListenerController.java +++ b/services/core/java/com/android/server/wm/WindowContextListenerController.java @@ -39,7 +39,7 @@ import android.view.WindowManager.LayoutParams.WindowType; import android.window.WindowContext; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import java.util.Objects; diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 521560936d81..57b8040ef0ac 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -335,7 +335,7 @@ import com.android.internal.policy.IShortcutService; import com.android.internal.policy.KeyInterceptionInfo; import com.android.internal.protolog.LegacyProtoLogImpl; import com.android.internal.protolog.ProtoLogGroup; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.internal.util.DumpUtils; import com.android.internal.util.FastPrintWriter; import com.android.internal.util.FrameworkStatsLog; @@ -3743,6 +3743,8 @@ public class WindowManagerService extends IWindowManager.Stub null /* trigger */, null /* remote */, null /* disp */); } mCurrentUserId = newUserId; + mDisplayWindowSettingsProvider.setOverrideSettingsForUser(newUserId); + mDisplayWindowSettingsProvider.removeStaleDisplaySettings(mRoot); mPolicy.setCurrentUserLw(newUserId); mKeyguardDisableHandler.setCurrentUser(newUserId); @@ -5479,6 +5481,9 @@ public class WindowManagerService extends IWindowManager.Stub // DisplayWindowSettings are applied. In addition, wide-color/hdr/isTouchDevice also // affect the Configuration. mRoot.forAllDisplays(DisplayContent::reconfigureDisplayLocked); + // Per-user display settings may leave outdated settings after user switches, especially + // during reboots starting with the default user without setCurrentUser called. + mDisplayWindowSettingsProvider.removeStaleDisplaySettings(mRoot); } } diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java index 1f06bfac5668..6febe80166f4 100644 --- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java +++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java @@ -51,7 +51,7 @@ import com.android.internal.os.ByteTransferPipe; import com.android.internal.protolog.LegacyProtoLogImpl; import com.android.internal.protolog.PerfettoProtoLogImpl; import com.android.internal.protolog.common.IProtoLog; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.server.IoThread; import com.android.server.wm.LetterboxConfiguration.LetterboxBackgroundType; import com.android.server.wm.LetterboxConfiguration.LetterboxHorizontalReachabilityPosition; diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index d26df7a67ad5..de584573fcaf 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -124,7 +124,7 @@ import android.window.WindowContainerTransaction; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.ProtoLogGroup; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.internal.util.ArrayUtils; import com.android.server.LocalServices; import com.android.server.pm.LauncherAppsService.LauncherAppsServiceInternal; @@ -1105,6 +1105,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub break; } if (activity.isVisible() || activity.isVisibleRequested()) { + effects |= TRANSACT_EFFECTS_LIFECYCLE; // Prevent the transition from being executed too early if the activity is // visible. activity.finishIfPossible("finish-activity-op", false /* oomAdj */); @@ -1122,6 +1123,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub launchOpts.remove(WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID); final SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(launchOpts, caller.mPid, caller.mUid); + effects |= TRANSACT_EFFECTS_LIFECYCLE; waitAsyncStart(() -> mService.mTaskSupervisor.startActivityFromRecents( caller.mPid, caller.mUid, taskId, safeOptions)); break; diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index b8780726f992..60d3e787cac4 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -82,7 +82,7 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.HeavyWeightSwitcherActivity; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.Watchdog; import com.android.server.grammaticalinflection.GrammaticalInflectionManagerInternal; diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index d845968767e5..fec1175785ea 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -250,7 +250,7 @@ import android.window.OnBackInvokedCallbackInfo; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.policy.KeyInterceptionInfo; import com.android.internal.protolog.common.LogLevel; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.ToBooleanFunction; import com.android.server.policy.WindowManagerPolicy; diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 9ecd49213237..397a6357fb66 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -62,7 +62,7 @@ import android.view.animation.Animation; import android.view.animation.AnimationUtils; import com.android.internal.protolog.common.LogLevel; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.window.flags.Flags; import com.android.server.policy.WindowManagerPolicy; diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java index 4456a94ef510..d9766e0dfa61 100644 --- a/services/core/java/com/android/server/wm/WindowSurfaceController.java +++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java @@ -36,7 +36,7 @@ import android.util.proto.ProtoOutputStream; import android.view.SurfaceControl; import android.view.WindowContentFrameStats; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import java.io.PrintWriter; diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index 4dca23bc03c7..11ef2cde65a9 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -47,7 +47,7 @@ import android.view.WindowManager; import android.view.WindowManager.LayoutParams.WindowType; import android.window.WindowContext; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.server.policy.WindowManagerPolicy; import java.io.PrintWriter; diff --git a/services/core/java/com/android/server/wm/WindowTracing.java b/services/core/java/com/android/server/wm/WindowTracing.java index b0e71bda787a..ba5323ee1f8f 100644 --- a/services/core/java/com/android/server/wm/WindowTracing.java +++ b/services/core/java/com/android/server/wm/WindowTracing.java @@ -37,7 +37,7 @@ import android.view.Choreographer; import com.android.internal.protolog.LegacyProtoLogImpl; import com.android.internal.protolog.common.IProtoLog; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import com.android.internal.util.TraceBuffer; import java.io.File; diff --git a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt index c558aae5248e..7ed23cd7df3e 100644 --- a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt +++ b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt @@ -230,18 +230,7 @@ class AppIdPermissionPolicy : SchemePolicy() { } val isSoftRestricted = if (permission.isSoftRestricted && !isExempt) { - val targetSdkVersion = - reducePackageInAppId(appId, Build.VERSION_CODES.CUR_DEVELOPMENT) { - targetSdkVersion, - packageState -> - if (permissionName in packageState.androidPackage!!.requestedPermissions) { - targetSdkVersion.coerceAtMost( - packageState.androidPackage!!.targetSdkVersion - ) - } else { - targetSdkVersion - } - } + val targetSdkVersion = getAppIdTargetSdkVersion(appId, permissionName) !anyPackageInAppId(appId) { permissionName in it.androidPackage!!.requestedPermissions && isSoftRestrictedPermissionExemptForPackage( @@ -718,18 +707,8 @@ class AppIdPermissionPolicy : SchemePolicy() { // If the app is updated, and has scoped storage permissions, then it is possible that the // app updated in an attempt to get unscoped storage. If so, revoke all storage permissions. - val oldTargetSdkVersion = - reducePackageInAppId(appId, Build.VERSION_CODES.CUR_DEVELOPMENT, oldState) { - targetSdkVersion, - packageState -> - targetSdkVersion.coerceAtMost(packageState.androidPackage!!.targetSdkVersion) - } - val newTargetSdkVersion = - reducePackageInAppId(appId, Build.VERSION_CODES.CUR_DEVELOPMENT, newState) { - targetSdkVersion, - packageState -> - targetSdkVersion.coerceAtMost(packageState.androidPackage!!.targetSdkVersion) - } + val oldTargetSdkVersion = getAppIdTargetSdkVersion(appId, null, oldState) + val newTargetSdkVersion = getAppIdTargetSdkVersion(appId, null, newState) @Suppress("ConvertTwoComparisonsToRangeCheck") val isTargetSdkVersionDowngraded = oldTargetSdkVersion >= Build.VERSION_CODES.Q && @@ -1115,10 +1094,9 @@ class AppIdPermissionPolicy : SchemePolicy() { } private fun MutateStateScope.inheritImplicitPermissionStates(appId: Int, userId: Int) { - var targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT + val targetSdkVersion = getAppIdTargetSdkVersion(appId, null) val implicitPermissions = MutableIndexedSet<String>() forEachPackageInAppId(appId) { - targetSdkVersion = targetSdkVersion.coerceAtMost(it.androidPackage!!.targetSdkVersion) implicitPermissions += it.androidPackage!!.implicitPermissions } implicitPermissions.forEachIndexed implicitPermissions@{ _, implicitPermissionName -> @@ -1418,6 +1396,22 @@ class AppIdPermissionPolicy : SchemePolicy() { else -> false } + private fun MutateStateScope.getAppIdTargetSdkVersion( + appId: Int, + permissionName: String?, + state: AccessState = newState + ): Int = + reducePackageInAppId(appId, Build.VERSION_CODES.CUR_DEVELOPMENT, state) { + targetSdkVersion, + packageState -> + val androidPackage = packageState.androidPackage!! + if (permissionName == null || permissionName in androidPackage.requestedPermissions) { + targetSdkVersion.coerceAtMost(androidPackage.targetSdkVersion) + } else { + targetSdkVersion + } + } + private inline fun MutateStateScope.anyPackageInAppId( appId: Int, state: AccessState = newState, diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java index 9acebf71ee18..33ea9b459b31 100644 --- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java +++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java @@ -50,6 +50,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import com.android.dx.mockito.inline.extended.ExtendedMockito; import com.android.internal.inputmethod.InputBindResult; +import com.android.internal.inputmethod.SoftInputShowHideReason; import com.android.internal.inputmethod.StartInputFlags; import com.android.internal.inputmethod.StartInputReason; @@ -73,9 +74,8 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe @Before public void setUp() throws RemoteException { super.setUp(); - mVisibilityApplier = - (DefaultImeVisibilityApplier) mInputMethodManagerService.getVisibilityApplier(); synchronized (ImfLock.class) { + mVisibilityApplier = mInputMethodManagerService.getVisibilityApplierLocked(); mUserId = mInputMethodManagerService.getCurrentImeUserIdLocked(); mInputMethodManagerService.setAttachedClientForTesting(requireNonNull( mInputMethodManagerService.getClientStateLocked(mMockInputMethodClient))); @@ -106,7 +106,7 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe assertThrows(IllegalArgumentException.class, () -> { synchronized (ImfLock.class) { mVisibilityApplier.applyImeVisibility(mWindowToken, ImeTracker.Token.empty(), - STATE_INVALID, mUserId); + STATE_INVALID, eq(SoftInputShowHideReason.NOT_SET), mUserId); } }); } @@ -116,7 +116,7 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe final var statsToken = ImeTracker.Token.empty(); synchronized (ImfLock.class) { mVisibilityApplier.applyImeVisibility(mWindowToken, statsToken, STATE_SHOW_IME, - mUserId); + eq(SoftInputShowHideReason.NOT_SET), mUserId); } verify(mMockWindowManagerInternal).showImePostLayout(eq(mWindowToken), eq(statsToken)); } @@ -126,7 +126,7 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe final var statsToken = ImeTracker.Token.empty(); synchronized (ImfLock.class) { mVisibilityApplier.applyImeVisibility(mWindowToken, statsToken, STATE_HIDE_IME, - mUserId); + eq(SoftInputShowHideReason.NOT_SET), mUserId); } verify(mMockWindowManagerInternal).hideIme(eq(mWindowToken), anyInt() /* displayId */, eq(statsToken)); @@ -137,7 +137,7 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe mInputMethodManagerService.mImeWindowVis = IME_ACTIVE; synchronized (ImfLock.class) { mVisibilityApplier.applyImeVisibility(mWindowToken, ImeTracker.Token.empty(), - STATE_HIDE_IME_EXPLICIT, mUserId); + STATE_HIDE_IME_EXPLICIT, eq(SoftInputShowHideReason.NOT_SET), mUserId); } verifyHideSoftInput(true, true); } @@ -147,7 +147,7 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe mInputMethodManagerService.mImeWindowVis = IME_ACTIVE; synchronized (ImfLock.class) { mVisibilityApplier.applyImeVisibility(mWindowToken, ImeTracker.Token.empty(), - STATE_HIDE_IME_NOT_ALWAYS, mUserId); + STATE_HIDE_IME_NOT_ALWAYS, eq(SoftInputShowHideReason.NOT_SET), mUserId); } verifyHideSoftInput(true, true); } @@ -156,7 +156,7 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe public void testApplyImeVisibility_showImeImplicit() throws Exception { synchronized (ImfLock.class) { mVisibilityApplier.applyImeVisibility(mWindowToken, ImeTracker.Token.empty(), - STATE_SHOW_IME_IMPLICIT, mUserId); + STATE_SHOW_IME_IMPLICIT, eq(SoftInputShowHideReason.NOT_SET), mUserId); } verifyShowSoftInput(true, true, 0 /* showFlags */); } @@ -177,7 +177,7 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe // Verify hideIme will apply the expected displayId when the default IME // visibility applier app STATE_HIDE_IME. mVisibilityApplier.applyImeVisibility(mWindowToken, statsToken, STATE_HIDE_IME, - mUserId); + eq(SoftInputShowHideReason.NOT_SET), mUserId); verify(mInputMethodManagerService.mWindowManagerInternal).hideIme( eq(mWindowToken), eq(displayIdToShowIme), eq(statsToken)); } @@ -224,7 +224,8 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe // the IME hidden state. // The unbind will cancel the previous stats token, and create a new one internally. verify(mVisibilityApplier).applyImeVisibility( - eq(mWindowToken), any(), eq(STATE_HIDE_IME), eq(mUserId) /* userId */); + eq(mWindowToken), any(), eq(STATE_HIDE_IME), + eq(SoftInputShowHideReason.NOT_SET), eq(mUserId) /* userId */); verify(mInputMethodManagerService.mWindowManagerInternal).hideIme( eq(mWindowToken), eq(displayIdToShowIme), and(not(eq(statsToken)), notNull())); } diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java index a22cacbcb5df..337d5c1faf94 100644 --- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java +++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java @@ -58,8 +58,8 @@ import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; /** - * Test the behavior of {@link ImeVisibilityStateComputer} and {@link ImeVisibilityApplier} when - * requesting the IME visibility. + * Test the behavior of {@link ImeVisibilityStateComputer} and {@link DefaultImeVisibilityApplier} + * when requesting the IME visibility. * * <p> Build/Install/Run: * atest FrameworksInputMethodSystemServerTests:ImeVisibilityStateComputerTest diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java index 42bd75a7a67e..80eab112d814 100644 --- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java +++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java @@ -189,6 +189,7 @@ public class InputMethodManagerServiceTestBase { // Injecting and mocked InputMethodBindingController and InputMethod. mMockInputMethodInvoker = IInputMethodInvoker.create(mMockInputMethod); mInputManagerGlobalSession = InputManagerGlobal.createTestSession(mMockIInputManager); + when(mMockInputMethodBindingController.getUserId()).thenReturn(mCallingUserId); synchronized (ImfLock.class) { when(mMockInputMethodBindingController.getCurMethod()) .thenReturn(mMockInputMethodInvoker); diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/UserDataRepositoryTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/UserDataRepositoryTest.java index f9f45057f57f..e2f3eec1a20b 100644 --- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/UserDataRepositoryTest.java +++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/UserDataRepositoryTest.java @@ -106,7 +106,7 @@ public final class UserDataRepositoryTest { // Assert UserDataRepository called the InputMethodBindingController creator function. verify(bindingControllerFactorySpy).apply(ANY_USER_ID); - assertThat(allUserData.get(0).mBindingController.mUserId).isEqualTo(ANY_USER_ID); + assertThat(allUserData.get(0).mBindingController.getUserId()).isEqualTo(ANY_USER_ID); } @Test @@ -149,7 +149,7 @@ public final class UserDataRepositoryTest { assertThat(allUserData.get(0).mUserId).isEqualTo(ANY_USER_ID); // Assert UserDataRepository called the InputMethodBindingController creator function. - assertThat(allUserData.get(0).mBindingController.mUserId).isEqualTo(ANY_USER_ID); + assertThat(allUserData.get(0).mBindingController.getUserId()).isEqualTo(ANY_USER_ID); } private List<UserDataRepository.UserData> collectUserData(UserDataRepository repository) { diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java index bb774eec9d4e..7b8b712c1aee 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java @@ -1707,7 +1707,8 @@ public final class DisplayPowerControllerTest { int initState = Display.STATE_OFF; mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession); - when(mDisplayOffloadSession.blockScreenOn(any())).thenReturn(true); + ArgumentCaptor<Runnable> argumentCaptor = ArgumentCaptor.forClass(Runnable.class); + when(mDisplayOffloadSession.blockScreenOn(argumentCaptor.capture())).thenReturn(true); // Start with OFF. when(mHolder.displayPowerState.getScreenState()).thenReturn(initState); @@ -1721,8 +1722,7 @@ public final class DisplayPowerControllerTest { mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); advanceTime(1); // Run updatePowerState - ArgumentCaptor<Runnable> argumentCaptor = ArgumentCaptor.forClass(Runnable.class); - verify(mDisplayOffloadSession).blockScreenOn(argumentCaptor.capture()); + verify(mDisplayOffloadSession).blockScreenOn(any()); // Unblocked argumentCaptor.getValue().run(); diff --git a/services/tests/servicestests/src/com/android/server/autofill/RequestIdTest.java b/services/tests/servicestests/src/com/android/server/autofill/RequestIdTest.java index 6d56c417f789..60c3659202be 100644 --- a/services/tests/servicestests/src/com/android/server/autofill/RequestIdTest.java +++ b/services/tests/servicestests/src/com/android/server/autofill/RequestIdTest.java @@ -17,17 +17,25 @@ package com.android.server.autofill; import static com.google.common.truth.Truth.assertThat; +import android.util.Slog; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; -import java.util.ArrayList; -import java.util.List; - @RunWith(JUnit4.class) public class RequestIdTest { + private static final int TEST_DATASET_SIZE = 300; + private static final int TEST_WRAP_SIZE = 50; // Number of request ids before wrap happens + private static final String TAG = "RequestIdTest"; + List<Integer> datasetPrimaryNoWrap = new ArrayList<>(); List<Integer> datasetPrimaryWrap = new ArrayList<>(); List<Integer> datasetSecondaryNoWrap = new ArrayList<>(); @@ -35,151 +43,200 @@ public class RequestIdTest { List<Integer> datasetMixedNoWrap = new ArrayList<>(); List<Integer> datasetMixedWrap = new ArrayList<>(); - @Before - public void setup() throws Exception { - int datasetSize = 300; + List<Integer> manualWrapRequestIdList = Arrays.asList(3, 9, 15, + RequestId.MAX_SECONDARY_REQUEST_ID - 5, + RequestId.MAX_SECONDARY_REQUEST_ID - 3); + List<Integer> manualNoWrapRequestIdList =Arrays.asList(2, 6, 10, 14, 18, 22, 26, 30); + List<Integer> manualOneElementRequestIdList = Arrays.asList(1); + + @Before + public void setup() throws IllegalArgumentException { + Slog.d(TAG, "setup()"); { // Generate primary only ids that do not wrap - RequestId requestId = new RequestId(0); - for (int i = 0; i < datasetSize; i++) { + RequestId requestId = new RequestId(RequestId.MIN_PRIMARY_REQUEST_ID); + for (int i = 0; i < TEST_DATASET_SIZE; i++) { datasetPrimaryNoWrap.add(requestId.nextId(false)); } + Collections.sort(datasetPrimaryNoWrap); } { // Generate primary only ids that wrap - RequestId requestId = new RequestId(0xff00); - for (int i = 0; i < datasetSize; i++) { + RequestId requestId = new RequestId(RequestId.MAX_PRIMARY_REQUEST_ID - + TEST_WRAP_SIZE * 2); + for (int i = 0; i < TEST_DATASET_SIZE; i++) { datasetPrimaryWrap.add(requestId.nextId(false)); } + Collections.sort(datasetPrimaryWrap); } { // Generate SECONDARY only ids that do not wrap - RequestId requestId = new RequestId(0); - for (int i = 0; i < datasetSize; i++) { + RequestId requestId = new RequestId(RequestId.MIN_SECONDARY_REQUEST_ID); + for (int i = 0; i < TEST_DATASET_SIZE; i++) { datasetSecondaryNoWrap.add(requestId.nextId(true)); } + Collections.sort(datasetSecondaryNoWrap); } { // Generate SECONDARY only ids that wrap - RequestId requestId = new RequestId(0xff00); - for (int i = 0; i < datasetSize; i++) { + RequestId requestId = new RequestId(RequestId.MAX_SECONDARY_REQUEST_ID - + TEST_WRAP_SIZE * 2); + for (int i = 0; i < TEST_DATASET_SIZE; i++) { datasetSecondaryWrap.add(requestId.nextId(true)); } + Collections.sort(datasetSecondaryWrap); } { // Generate MIXED only ids that do not wrap - RequestId requestId = new RequestId(0); - for (int i = 0; i < datasetSize; i++) { + RequestId requestId = new RequestId(RequestId.MIN_REQUEST_ID); + for (int i = 0; i < TEST_DATASET_SIZE; i++) { datasetMixedNoWrap.add(requestId.nextId(i % 2 != 0)); } + Collections.sort(datasetMixedNoWrap); } { // Generate MIXED only ids that wrap - RequestId requestId = new RequestId(0xff00); - for (int i = 0; i < datasetSize; i++) { + RequestId requestId = new RequestId(RequestId.MAX_REQUEST_ID - + TEST_WRAP_SIZE); + for (int i = 0; i < TEST_DATASET_SIZE; i++) { datasetMixedWrap.add(requestId.nextId(i % 2 != 0)); } + Collections.sort(datasetMixedWrap); } + Slog.d(TAG, "finishing setup()"); } @Test public void testRequestIdLists() { + Slog.d(TAG, "testRequestIdLists()"); for (int id : datasetPrimaryNoWrap) { assertThat(RequestId.isSecondaryProvider(id)).isFalse(); - assertThat(id >= 0).isTrue(); - assertThat(id < 0xffff).isTrue(); + assertThat(id).isAtLeast(RequestId.MIN_PRIMARY_REQUEST_ID); + assertThat(id).isAtMost(RequestId.MAX_PRIMARY_REQUEST_ID); } for (int id : datasetPrimaryWrap) { assertThat(RequestId.isSecondaryProvider(id)).isFalse(); - assertThat(id >= 0).isTrue(); - assertThat(id < 0xffff).isTrue(); + assertThat(id).isAtLeast(RequestId.MIN_PRIMARY_REQUEST_ID); + assertThat(id).isAtMost(RequestId.MAX_PRIMARY_REQUEST_ID); } for (int id : datasetSecondaryNoWrap) { assertThat(RequestId.isSecondaryProvider(id)).isTrue(); - assertThat(id >= 0).isTrue(); - assertThat(id < 0xffff).isTrue(); + assertThat(id).isAtLeast(RequestId.MIN_SECONDARY_REQUEST_ID); + assertThat(id).isAtMost(RequestId.MAX_SECONDARY_REQUEST_ID); } for (int id : datasetSecondaryWrap) { assertThat(RequestId.isSecondaryProvider(id)).isTrue(); - assertThat(id >= 0).isTrue(); - assertThat(id < 0xffff).isTrue(); + assertThat(id).isAtLeast(RequestId.MIN_SECONDARY_REQUEST_ID); + assertThat(id).isAtMost(RequestId.MAX_SECONDARY_REQUEST_ID); } } @Test - public void testRequestIdGeneration() { - RequestId requestId = new RequestId(0); + public void testCreateNewRequestId() { + Slog.d(TAG, "testCreateNewRequestId()"); + for (int i = 0; i < 100000; i++) { + RequestId requestId = new RequestId(); + assertThat(requestId.getRequestId()).isAtLeast(RequestId.MIN_REQUEST_ID); + assertThat(requestId.getRequestId()).isAtMost(RequestId.MAX_START_ID); + } + } + @Test + public void testGetNextRequestId() throws IllegalArgumentException{ + Slog.d(TAG, "testGetNextRequestId()"); + RequestId requestId = new RequestId(); // Large Primary for (int i = 0; i < 100000; i++) { int y = requestId.nextId(false); assertThat(RequestId.isSecondaryProvider(y)).isFalse(); - assertThat(y >= 0).isTrue(); - assertThat(y < 0xffff).isTrue(); + assertThat(y).isAtLeast(RequestId.MIN_PRIMARY_REQUEST_ID); + assertThat(y).isAtMost(RequestId.MAX_PRIMARY_REQUEST_ID); } // Large Secondary - requestId = new RequestId(0); + requestId = new RequestId(); for (int i = 0; i < 100000; i++) { int y = requestId.nextId(true); assertThat(RequestId.isSecondaryProvider(y)).isTrue(); - assertThat(y >= 0).isTrue(); - assertThat(y < 0xffff).isTrue(); + assertThat(y).isAtLeast(RequestId.MIN_SECONDARY_REQUEST_ID); + assertThat(y).isAtMost(RequestId.MAX_SECONDARY_REQUEST_ID); } // Large Mixed - requestId = new RequestId(0); + requestId = new RequestId(); for (int i = 0; i < 50000; i++) { int y = requestId.nextId(i % 2 != 0); - assertThat(RequestId.isSecondaryProvider(y)).isEqualTo(i % 2 == 0); - assertThat(y >= 0).isTrue(); - assertThat(y < 0xffff).isTrue(); + assertThat(y).isAtLeast(RequestId.MIN_REQUEST_ID); + assertThat(y).isAtMost(RequestId.MAX_REQUEST_ID); } } @Test public void testGetLastRequestId() { - // In this test, request ids are generated FIFO, so the last entry is also the last - // request + Slog.d(TAG, "testGetLastRequestId()"); - { // Primary no wrap - int lastIdIndex = datasetPrimaryNoWrap.size() - 1; - int lastComputedIdIndex = RequestId.getLastRequestIdIndex(datasetPrimaryNoWrap); - assertThat(lastIdIndex).isEqualTo(lastComputedIdIndex); + { // Primary no wrap + int lastIdIndex = datasetPrimaryNoWrap.size() - 1; + int lastComputedIdIndex = RequestId.getLastRequestIdIndex(datasetPrimaryNoWrap); + assertThat(lastIdIndex).isEqualTo(lastComputedIdIndex); } - { // Primary wrap - int lastIdIndex = datasetPrimaryWrap.size() - 1; + { // Primary wrap + // The last index would be the # of request ids left after wrap + // minus 1 (index starts at 0) + int lastIdIndex = TEST_DATASET_SIZE - TEST_WRAP_SIZE - 1; int lastComputedIdIndex = RequestId.getLastRequestIdIndex(datasetPrimaryWrap); - assertThat(lastIdIndex).isEqualTo(lastComputedIdIndex); + assertThat(lastComputedIdIndex).isEqualTo(lastIdIndex); } - { // Secondary no wrap + { // Secondary no wrap int lastIdIndex = datasetSecondaryNoWrap.size() - 1; int lastComputedIdIndex = RequestId.getLastRequestIdIndex(datasetSecondaryNoWrap); assertThat(lastIdIndex).isEqualTo(lastComputedIdIndex); } - { // Secondary wrap - int lastIdIndex = datasetSecondaryWrap.size() - 1; + { // Secondary wrap + int lastIdIndex = TEST_DATASET_SIZE - TEST_WRAP_SIZE - 1; int lastComputedIdIndex = RequestId.getLastRequestIdIndex(datasetSecondaryWrap); assertThat(lastIdIndex).isEqualTo(lastComputedIdIndex); } - { // Mixed no wrap + { // Mixed no wrap int lastIdIndex = datasetMixedNoWrap.size() - 1; int lastComputedIdIndex = RequestId.getLastRequestIdIndex(datasetMixedNoWrap); assertThat(lastIdIndex).isEqualTo(lastComputedIdIndex); } - { // Mixed wrap - int lastIdIndex = datasetMixedWrap.size() - 1; + { // Mixed wrap + int lastIdIndex = TEST_DATASET_SIZE - TEST_WRAP_SIZE - 1; int lastComputedIdIndex = RequestId.getLastRequestIdIndex(datasetMixedWrap); assertThat(lastIdIndex).isEqualTo(lastComputedIdIndex); } + { // Manual wrap + int lastIdIndex = 2; // [3, 9, 15, + // MAX_SECONDARY_REQUEST_ID - 5, MAX_SECONDARY_REQUEST_ID - 3] + int lastComputedIdIndex = RequestId.getLastRequestIdIndex(manualWrapRequestIdList); + assertThat(lastIdIndex).isEqualTo(lastComputedIdIndex); + } + + { // Manual no wrap + int lastIdIndex = manualNoWrapRequestIdList.size() - 1; // [2, 6, 10, 14, + // 18, 22, 26, 30] + int lastComputedIdIndex = RequestId.getLastRequestIdIndex(manualNoWrapRequestIdList); + assertThat(lastIdIndex).isEqualTo(lastComputedIdIndex); + + } + + { // Manual one element + int lastIdIndex = 0; // [1] + int lastComputedIdIndex = RequestId.getLastRequestIdIndex( + manualOneElementRequestIdList); + assertThat(lastIdIndex).isEqualTo(lastComputedIdIndex); + + } } } diff --git a/services/tests/wmtests/Android.bp b/services/tests/wmtests/Android.bp index b2922945aff9..6ba2c7010cf3 100644 --- a/services/tests/wmtests/Android.bp +++ b/services/tests/wmtests/Android.bp @@ -28,7 +28,7 @@ genrule { ], tools: ["protologtool"], cmd: "$(location protologtool) transform-protolog-calls " + - "--protolog-class com.android.internal.protolog.common.ProtoLog " + + "--protolog-class com.android.internal.protolog.ProtoLog " + "--loggroups-class com.android.internal.protolog.ProtoLogGroup " + "--loggroups-jar $(location :protolog-groups) " + // Used for the ProtoLogIntegrationTest, where don't test decoding or writing to file diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java index 7d9fdd507235..3fcf3042ab94 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java @@ -24,9 +24,12 @@ import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; +import static com.google.common.truth.Truth.assertThat; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeTrue; import static org.testng.Assert.assertFalse; import android.annotation.Nullable; @@ -55,6 +58,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.charset.StandardCharsets; +import java.util.function.Consumer; /** * Tests for the {@link DisplayWindowSettingsProvider} class. @@ -128,9 +132,8 @@ public class DisplayWindowSettingsProviderTests extends WindowTestsBase { // Update settings with new value, should trigger write to injector. DisplayWindowSettingsProvider provider = new DisplayWindowSettingsProvider( mDefaultVendorSettingsStorage, mOverrideSettingsStorage); - SettingsEntry overrideSettings = provider.getOverrideSettings(mPrimaryDisplayInfo); - overrideSettings.mForcedDensity = 200; - provider.updateOverrideSettings(mPrimaryDisplayInfo, overrideSettings); + updateOverrideSettings(provider, mPrimaryDisplayInfo, + overrideSettings -> overrideSettings.mForcedDensity = 200); assertTrue(mOverrideSettingsStorage.wasWriteSuccessful()); // Verify that display identifier was updated. @@ -167,7 +170,7 @@ public class DisplayWindowSettingsProviderTests extends WindowTestsBase { } @Test - public void testReadingDisplaySettingsFromStorage_secondayVendorDisplaySettingsLocation() { + public void testReadingDisplaySettingsFromStorage_secondaryVendorDisplaySettingsLocation() { final String displayIdentifier = mSecondaryDisplay.getDisplayInfo().uniqueId; prepareSecondaryDisplaySettings(displayIdentifier); @@ -216,11 +219,11 @@ public class DisplayWindowSettingsProviderTests extends WindowTestsBase { // Write some settings to storage. DisplayWindowSettingsProvider provider = new DisplayWindowSettingsProvider( mDefaultVendorSettingsStorage, mOverrideSettingsStorage); - SettingsEntry overrideSettings = provider.getOverrideSettings(secondaryDisplayInfo); - overrideSettings.mShouldShowSystemDecors = true; - overrideSettings.mImePolicy = DISPLAY_IME_POLICY_LOCAL; - overrideSettings.mDontMoveToTop = true; - provider.updateOverrideSettings(secondaryDisplayInfo, overrideSettings); + updateOverrideSettings(provider, secondaryDisplayInfo, overrideSettings -> { + overrideSettings.mShouldShowSystemDecors = true; + overrideSettings.mImePolicy = DISPLAY_IME_POLICY_LOCAL; + overrideSettings.mDontMoveToTop = true; + }); assertTrue(mOverrideSettingsStorage.wasWriteSuccessful()); // Verify that settings were stored correctly. @@ -235,6 +238,29 @@ public class DisplayWindowSettingsProviderTests extends WindowTestsBase { } @Test + public void testWritingDisplaySettingsToStorage_secondaryUserDisplaySettingsLocation() { + final DisplayWindowSettingsProvider provider = new DisplayWindowSettingsProvider( + mDefaultVendorSettingsStorage, mOverrideSettingsStorage); + final DisplayInfo displayInfo = mPrimaryDisplay.getDisplayInfo(); + final TestStorage secondaryUserOverrideSettingsStorage = new TestStorage(); + final SettingsEntry expectedSettings = new SettingsEntry(); + expectedSettings.mForcedDensity = 356; + + // Write some settings to storage from default user. + updateOverrideSettings(provider, displayInfo, settings -> settings.mForcedDensity = 356); + assertThat(mOverrideSettingsStorage.wasWriteSuccessful()).isTrue(); + + // Now switch to secondary user override settings and write some settings. + provider.setOverrideSettingsStorage(secondaryUserOverrideSettingsStorage); + updateOverrideSettings(provider, displayInfo, settings -> settings.mForcedDensity = 420); + assertThat(secondaryUserOverrideSettingsStorage.wasWriteSuccessful()).isTrue(); + + // Switch back to primary and assert default user settings remain unchanged. + provider.setOverrideSettingsStorage(mOverrideSettingsStorage); + assertThat(provider.getOverrideSettings(displayInfo)).isEqualTo(expectedSettings); + } + + @Test public void testDoNotWriteVirtualDisplaySettingsToStorage() throws Exception { final DisplayInfo secondaryDisplayInfo = mSecondaryDisplay.getDisplayInfo(); secondaryDisplayInfo.type = TYPE_VIRTUAL; @@ -242,11 +268,11 @@ public class DisplayWindowSettingsProviderTests extends WindowTestsBase { // No write to storage on virtual display change. final DisplayWindowSettingsProvider provider = new DisplayWindowSettingsProvider( mDefaultVendorSettingsStorage, mOverrideSettingsStorage); - final SettingsEntry virtualSettings = provider.getOverrideSettings(secondaryDisplayInfo); - virtualSettings.mShouldShowSystemDecors = true; - virtualSettings.mImePolicy = DISPLAY_IME_POLICY_LOCAL; - virtualSettings.mDontMoveToTop = true; - provider.updateOverrideSettings(secondaryDisplayInfo, virtualSettings); + updateOverrideSettings(provider, secondaryDisplayInfo, virtualSettings -> { + virtualSettings.mShouldShowSystemDecors = true; + virtualSettings.mImePolicy = DISPLAY_IME_POLICY_LOCAL; + virtualSettings.mDontMoveToTop = true; + }); assertFalse(mOverrideSettingsStorage.wasWriteSuccessful()); } @@ -263,10 +289,10 @@ public class DisplayWindowSettingsProviderTests extends WindowTestsBase { // Write some settings to storage. DisplayWindowSettingsProvider provider = new DisplayWindowSettingsProvider( mDefaultVendorSettingsStorage, mOverrideSettingsStorage); - SettingsEntry overrideSettings = provider.getOverrideSettings(secondaryDisplayInfo); - overrideSettings.mShouldShowSystemDecors = true; - overrideSettings.mImePolicy = DISPLAY_IME_POLICY_LOCAL; - provider.updateOverrideSettings(secondaryDisplayInfo, overrideSettings); + updateOverrideSettings(provider, secondaryDisplayInfo, overrideSettings -> { + overrideSettings.mShouldShowSystemDecors = true; + overrideSettings.mImePolicy = DISPLAY_IME_POLICY_LOCAL; + }); assertTrue(mOverrideSettingsStorage.wasWriteSuccessful()); // Verify that settings were stored correctly. @@ -283,16 +309,16 @@ public class DisplayWindowSettingsProviderTests extends WindowTestsBase { final DisplayWindowSettingsProvider provider = new DisplayWindowSettingsProvider( mDefaultVendorSettingsStorage, mOverrideSettingsStorage); final int initialSize = provider.getOverrideSettingsSize(); - - // Size + 1 when query for a new display. final DisplayInfo secondaryDisplayInfo = mSecondaryDisplay.getDisplayInfo(); - final SettingsEntry overrideSettings = provider.getOverrideSettings(secondaryDisplayInfo); - assertEquals(initialSize + 1, provider.getOverrideSettingsSize()); + updateOverrideSettings(provider, secondaryDisplayInfo, overrideSettings -> { + // Size + 1 when query for a new display. + assertEquals(initialSize + 1, provider.getOverrideSettingsSize()); - // When a display is removed, its override Settings is not removed if there is any override. - overrideSettings.mShouldShowSystemDecors = true; - provider.updateOverrideSettings(secondaryDisplayInfo, overrideSettings); + // When a display is removed, its override Settings is not removed if there is any + // override. + overrideSettings.mShouldShowSystemDecors = true; + }); provider.onDisplayRemoved(secondaryDisplayInfo); assertEquals(initialSize + 1, provider.getOverrideSettingsSize()); @@ -309,23 +335,53 @@ public class DisplayWindowSettingsProviderTests extends WindowTestsBase { final DisplayWindowSettingsProvider provider = new DisplayWindowSettingsProvider( mDefaultVendorSettingsStorage, mOverrideSettingsStorage); final int initialSize = provider.getOverrideSettingsSize(); - - // Size + 1 when query for a new display. final DisplayInfo secondaryDisplayInfo = mSecondaryDisplay.getDisplayInfo(); secondaryDisplayInfo.type = TYPE_VIRTUAL; - final SettingsEntry overrideSettings = provider.getOverrideSettings(secondaryDisplayInfo); - assertEquals(initialSize + 1, provider.getOverrideSettingsSize()); + updateOverrideSettings(provider, secondaryDisplayInfo, overrideSettings -> { + // Size + 1 when query for a new display. + assertEquals(initialSize + 1, provider.getOverrideSettingsSize()); - // When a virtual display is removed, its override Settings is removed even if it has - // override. - overrideSettings.mShouldShowSystemDecors = true; - provider.updateOverrideSettings(secondaryDisplayInfo, overrideSettings); + // When a virtual display is removed, its override Settings is removed + // even if it has override. + overrideSettings.mShouldShowSystemDecors = true; + }); provider.onDisplayRemoved(secondaryDisplayInfo); assertEquals(initialSize, provider.getOverrideSettingsSize()); } + @Test + public void testRemovesStaleDisplaySettings() { + assumeTrue(com.android.window.flags.Flags.perUserDisplayWindowSettings()); + + final DisplayWindowSettingsProvider provider = + new DisplayWindowSettingsProvider(mDefaultVendorSettingsStorage, + mOverrideSettingsStorage); + final DisplayInfo displayInfo = mSecondaryDisplay.getDisplayInfo(); + updateOverrideSettings(provider, displayInfo, settings -> settings.mForcedDensity = 356); + mRootWindowContainer.removeChild(mSecondaryDisplay); + + provider.removeStaleDisplaySettings(mRootWindowContainer); + + assertThat(mOverrideSettingsStorage.wasWriteSuccessful()).isTrue(); + assertThat(provider.getOverrideSettingsSize()).isEqualTo(0); + } + + /** + * Updates the override settings for a specific display. + * + * @param provider the provider to obtain and update the settings from. + * @param displayInfo the information about the display to be updated. + * @param modifier a function that modifies the settings for the display. + */ + private static void updateOverrideSettings(DisplayWindowSettingsProvider provider, + DisplayInfo displayInfo, Consumer<SettingsEntry> modifier) { + final SettingsEntry settings = provider.getOverrideSettings(displayInfo); + modifier.accept(settings); + provider.updateOverrideSettings(displayInfo, settings); + } + /** * Prepares display settings and stores in {@link #mOverrideSettingsStorage}. Uses provided * display identifier and stores windowingMode=WINDOWING_MODE_PINNED. diff --git a/services/tests/wmtests/src/com/android/server/wm/ProtoLogIntegrationTest.java b/services/tests/wmtests/src/com/android/server/wm/ProtoLogIntegrationTest.java index c5bf78bb60b5..7efbc88833cf 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ProtoLogIntegrationTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/ProtoLogIntegrationTest.java @@ -29,7 +29,7 @@ import com.android.internal.protolog.ProtoLogGroup; import com.android.internal.protolog.ProtoLogImpl; import com.android.internal.protolog.common.IProtoLog; import com.android.internal.protolog.common.LogLevel; -import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.protolog.ProtoLog; import org.junit.After; import org.junit.Ignore; diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java index eb79118fe1c7..3078df026d8a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java @@ -392,6 +392,8 @@ public class RootWindowContainerTests extends WindowTestsBase { assertEquals(newPipTask, mDisplayContent.getDefaultTaskDisplayArea().getRootPinnedTask()); assertNotEquals(newPipTask, activity1.getTask()); assertFalse("Created PiP task must not be in recents", newPipTask.inRecents); + assertThat(newPipTask.autoRemoveRecents).isTrue(); + assertThat(activity1.getTask().autoRemoveRecents).isFalse(); } /** @@ -427,6 +429,7 @@ public class RootWindowContainerTests extends WindowTestsBase { bounds.scale(0.5f); task.setBounds(bounds); assertFalse(activity.isLetterboxedForFixedOrientationAndAspectRatio()); + assertThat(task.autoRemoveRecents).isFalse(); } /** @@ -451,6 +454,7 @@ public class RootWindowContainerTests extends WindowTestsBase { // Ensure a task has moved over. ensureTaskPlacement(task, activity); assertTrue(task.inPinnedWindowingMode()); + assertThat(task.autoRemoveRecents).isFalse(); } /** @@ -480,6 +484,8 @@ public class RootWindowContainerTests extends WindowTestsBase { ensureTaskPlacement(fullscreenTask, secondActivity); assertTrue(pinnedRootTask.inPinnedWindowingMode()); assertEquals(WINDOWING_MODE_FULLSCREEN, fullscreenTask.getWindowingMode()); + assertThat(pinnedRootTask.autoRemoveRecents).isTrue(); + assertThat(secondActivity.getTask().autoRemoveRecents).isFalse(); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java index e01cea3d62f8..ef0aa9ef7666 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java @@ -42,6 +42,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static com.android.server.policy.WindowManagerPolicy.USER_ROTATION_FREE; +import static com.android.server.wm.ActivityRecord.State.RESUMED; import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG; import static com.android.server.wm.TaskFragment.EMBEDDED_DIM_AREA_PARENT_TASK; import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT; @@ -222,6 +223,27 @@ public class TaskTests extends WindowTestsBase { } @Test + public void testReparentPinnedActivityBackToOriginalTask() { + final ActivityRecord activityMain = new ActivityBuilder(mAtm).setCreateTask(true).build(); + final Task originalTask = activityMain.getTask(); + final ActivityRecord activityPip = new ActivityBuilder(mAtm).setTask(originalTask).build(); + activityPip.setState(RESUMED, "test"); + mAtm.mRootWindowContainer.moveActivityToPinnedRootTask(activityPip, + null /* launchIntoPipHostActivity */, "test"); + final Task pinnedActivityTask = activityPip.getTask(); + + // Simulate pinnedActivityTask unintentionally added to recent during top activity resume. + mAtm.getRecentTasks().getRawTasks().add(pinnedActivityTask); + + // Reparent the activity back to its original task when exiting PIP mode. + pinnedActivityTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + + assertThat(activityPip.getTask()).isEqualTo(originalTask); + assertThat(originalTask.autoRemoveRecents).isFalse(); + assertThat(mAtm.getRecentTasks().getRawTasks()).containsExactly(originalTask); + } + + @Test public void testReparent_BetweenDisplays() { // Create first task on primary display. final Task rootTask1 = createTask(mDisplayContent); diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index 27c383c283a1..bf46154caf3e 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -113,6 +113,7 @@ import com.android.internal.util.IndentingPrintWriter; import com.android.server.IoThread; import com.android.server.LocalServices; import com.android.server.SystemService; +import com.android.server.pm.UserManagerInternal; import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener; import com.android.server.utils.AlarmQueue; @@ -1063,6 +1064,18 @@ public class UsageStatsService extends SystemService implements synchronized (mReportedEvents) { LinkedList<Event> events = mReportedEvents.get(userId); if (events == null) { + // TODO (b/347644400): callers of this API should verify that the userId passed to + // this method exists - there is currently a known case where USER_ALL is passed + // here and it would be added to the queue, never to be flushed correctly. The logic + // below should only remain as a last-resort catch-all fix. + final UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class); + if (umi == null || (umi != null && !umi.exists(userId))) { + // The userId passed is a non-existent user so don't report the event. + Slog.wtf(TAG, "Attempted to report event for non-existent user " + userId + + " (" + event.mPackage + "/" + event.mClass + + " eventType:" + event.mEventType + ")"); + return; + } events = new LinkedList<>(); mReportedEvents.put(userId, events); } diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 09cb464198b5..61698dbebe7c 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -9981,6 +9981,18 @@ public class CarrierConfigManager { "carrier_roaming_satellite_default_services_int_array"; /** + * Indicate whether carrier roaming to satellite is using ESOS (Emergency SOS) which connects + * to an emergency provider instead of PSAP (Public Safety Answering Point) for emergency + * messaging. + * + * This will need agreement with carriers before enabling this flag. + * + * The default value is false. + */ + @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN) + public static final String KEY_SATELLITE_ESOS_SUPPORTED_BOOL = "satellite_esos_supported_bool"; + + /** * Indicating whether DUN APN should be disabled when the device is roaming. In that case, * the default APN (i.e. internet) will be used for tethering. * @@ -11137,6 +11149,7 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_EMERGENCY_MESSAGING_SUPPORTED_BOOL, false); sDefaults.putInt(KEY_EMERGENCY_CALL_TO_SATELLITE_T911_HANDOVER_TIMEOUT_MILLIS_INT, (int) TimeUnit.SECONDS.toMillis(30)); + sDefaults.putBoolean(KEY_SATELLITE_ESOS_SUPPORTED_BOOL, false); sDefaults.putString(KEY_DEFAULT_PREFERRED_APN_NAME_STRING, ""); sDefaults.putBoolean(KEY_SUPPORTS_CALL_COMPOSER_BOOL, false); sDefaults.putBoolean(KEY_SUPPORTS_BUSINESS_CALL_COMPOSER_BOOL, false); diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java index 58488d1900fe..1089602934fd 100644 --- a/telephony/java/android/telephony/SubscriptionInfo.java +++ b/telephony/java/android/telephony/SubscriptionInfo.java @@ -274,6 +274,11 @@ public class SubscriptionInfo implements Parcelable { private final int mServiceCapabilities; /** + * Whether the carrier roaming to satellite is using ESOS for emergency messaging. + */ + private final boolean mIsSatelliteESOSSupported; + + /** * @hide * * @deprecated Use {@link SubscriptionInfo.Builder}. @@ -400,6 +405,7 @@ public class SubscriptionInfo implements Parcelable { this.mIsOnlyNonTerrestrialNetwork = false; this.mServiceCapabilities = 0; this.mTransferStatus = 0; + this.mIsSatelliteESOSSupported = false; } /** @@ -441,6 +447,7 @@ public class SubscriptionInfo implements Parcelable { this.mIsOnlyNonTerrestrialNetwork = builder.mIsOnlyNonTerrestrialNetwork; this.mServiceCapabilities = builder.mServiceCapabilities; this.mTransferStatus = builder.mTransferStatus; + this.mIsSatelliteESOSSupported = builder.mIsSatelliteESOSSupported; } /** @@ -898,6 +905,19 @@ public class SubscriptionInfo implements Parcelable { return mIsOnlyNonTerrestrialNetwork; } + + /** + * Checks if the subscription is supported ESOS over Carrier Roaming NB-IOT Satellite. + * + * @return {@code true} if the subscription supports ESOS over Carrier Roaming NB-IOT Satellite, + * {@code false} otherwise. + * @hide + */ + @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN) + public boolean isSatelliteESOSSupported() { + return mIsSatelliteESOSSupported; + } + // TODO(b/316183370): replace @code with @link in javadoc after feature is released /** * Retrieves the service capabilities for the current subscription. @@ -989,6 +1009,7 @@ public class SubscriptionInfo implements Parcelable { .setServiceCapabilities( SubscriptionManager.getServiceCapabilitiesSet(source.readInt())) .setTransferStatus(source.readInt()) + .setSatelliteESOSSupported(source.readBoolean()) .build(); } @@ -1033,6 +1054,7 @@ public class SubscriptionInfo implements Parcelable { dest.writeBoolean(mIsOnlyNonTerrestrialNetwork); dest.writeInt(mServiceCapabilities); dest.writeInt(mTransferStatus); + dest.writeBoolean(mIsSatelliteESOSSupported); } @Override @@ -1099,6 +1121,7 @@ public class SubscriptionInfo implements Parcelable { + " serviceCapabilities=" + SubscriptionManager.getServiceCapabilitiesSet( mServiceCapabilities).toString() + " transferStatus=" + mTransferStatus + + " isSatelliteESOSSupported=" + mIsSatelliteESOSSupported + "]"; } @@ -1126,7 +1149,8 @@ public class SubscriptionInfo implements Parcelable { && mCountryIso.equals(that.mCountryIso) && mGroupOwner.equals(that.mGroupOwner) && mIsOnlyNonTerrestrialNetwork == that.mIsOnlyNonTerrestrialNetwork && mServiceCapabilities == that.mServiceCapabilities - && mTransferStatus == that.mTransferStatus; + && mTransferStatus == that.mTransferStatus + && mIsSatelliteESOSSupported == that.mIsSatelliteESOSSupported; } @Override @@ -1136,7 +1160,7 @@ public class SubscriptionInfo implements Parcelable { mCardString, mIsOpportunistic, mGroupUuid, mCountryIso, mCarrierId, mProfileClass, mType, mGroupOwner, mAreUiccApplicationsEnabled, mPortIndex, mUsageSetting, mCardId, mIsGroupDisabled, mIsOnlyNonTerrestrialNetwork, mServiceCapabilities, - mTransferStatus); + mTransferStatus, mIsSatelliteESOSSupported); result = 31 * result + Arrays.hashCode(mEhplmns); result = 31 * result + Arrays.hashCode(mHplmns); result = 31 * result + Arrays.hashCode(mNativeAccessRules); @@ -1346,6 +1370,11 @@ public class SubscriptionInfo implements Parcelable { * Service capabilities bitmasks the subscription supports. */ private int mServiceCapabilities = 0; + /** + * {@code true} if the subscription supports ESOS over Carrier Roaming NB-IOT Satellite. + * {@code false} otherwise. + */ + private boolean mIsSatelliteESOSSupported = false; /** * Default constructor. @@ -1392,6 +1421,7 @@ public class SubscriptionInfo implements Parcelable { mIsOnlyNonTerrestrialNetwork = info.mIsOnlyNonTerrestrialNetwork; mServiceCapabilities = info.mServiceCapabilities; mTransferStatus = info.mTransferStatus; + mIsSatelliteESOSSupported = info.mIsSatelliteESOSSupported; } /** @@ -1828,6 +1858,21 @@ public class SubscriptionInfo implements Parcelable { } /** + * Set whether the subscription is supported ESOS over Carrier Roaming NB-IOT Satellite or + * not. + * + * @param isSatelliteESOSSupported {@code true} if the subscription supports ESOS over + * Carrier Roaming NB-IOT Satellite, {@code false} otherwise. + * @return The builder. + */ + @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN) + @NonNull + public Builder setSatelliteESOSSupported(boolean isSatelliteESOSSupported) { + mIsSatelliteESOSSupported = isSatelliteESOSSupported; + return this; + } + + /** * Build the {@link SubscriptionInfo}. * * @return The {@link SubscriptionInfo} instance. diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 76b4e0052792..dea10b70b7b9 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -1127,7 +1127,7 @@ public class SubscriptionManager { * <P>Type: INTEGER (int)</P> * @hide */ - public static final String IS_NTN = SimInfo.COLUMN_IS_NTN; + public static final String IS_ONLY_NTN = SimInfo.COLUMN_IS_ONLY_NTN; /** * TelephonyProvider column name to identify service capabilities. @@ -1167,6 +1167,16 @@ public class SubscriptionManager { public static final String SATELLITE_ENTITLEMENT_PLMNS = SimInfo.COLUMN_SATELLITE_ENTITLEMENT_PLMNS; + /** + * TelephonyProvider column name to indicate the satellite ESOS supported. The value of this + * column is set based on {@link CarrierConfigManager#KEY_SATELLITE_ESOS_SUPPORTED_BOOL}. + * By default, it's disabled. + * <P>Type: INTEGER (int)</P> + * + * @hide + */ + public static final String SATELLITE_ESOS_SUPPORTED = SimInfo.COLUMN_SATELLITE_ESOS_SUPPORTED; + /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = {"USAGE_SETTING_"}, @@ -2783,17 +2793,17 @@ public class SubscriptionManager { return phoneId >= 0 && phoneId < TelephonyManager.getDefault().getActiveModemCount(); } - /** @hide */ + /** + * Puts phone ID and subscription ID into the {@code intent}. + * + * <p>If the subscription ID is not valid, only puts phone ID into the {@code intent}. + * + * @hide + */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) public static void putPhoneIdAndSubIdExtra(Intent intent, int phoneId) { int subId = SubscriptionManager.getSubscriptionId(phoneId); - if (isValidSubscriptionId(subId)) { - putPhoneIdAndSubIdExtra(intent, phoneId, subId); - } else { - logd("putPhoneIdAndSubIdExtra: no valid subs"); - intent.putExtra(PhoneConstants.PHONE_KEY, phoneId); - intent.putExtra(EXTRA_SLOT_INDEX, phoneId); - } + putPhoneIdAndMaybeSubIdExtra(intent, phoneId, subId); } /** @hide */ @@ -2806,6 +2816,23 @@ public class SubscriptionManager { } /** + * Puts phone ID and subscription ID into the {@code intent}. + * + * <p>If the subscription ID is not valid, only puts phone ID into the {@code intent}. + * + * @hide + */ + public static void putPhoneIdAndMaybeSubIdExtra(Intent intent, int phoneId, int subId) { + if (isValidSubscriptionId(subId)) { + putPhoneIdAndSubIdExtra(intent, phoneId, subId); + } else { + if (VDBG) logd("putPhoneIdAndMaybeSubIdExtra: invalid subId"); + intent.putExtra(PhoneConstants.PHONE_KEY, phoneId); + intent.putExtra(EXTRA_SLOT_INDEX, phoneId); + } + } + + /** * Get visible subscription Id(s) of the currently active SIM(s). * * @return the list of subId's that are active, diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java index 2a359cd56d1b..6caed14bc31d 100644 --- a/telephony/java/android/telephony/satellite/SatelliteManager.java +++ b/telephony/java/android/telephony/satellite/SatelliteManager.java @@ -371,6 +371,24 @@ public final class SatelliteManager { @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_RESULT_MODEM_TIMEOUT = 24; + /** + * Telephony framework needs to access the current location of the device to perform the + * request. However, location in the settings is disabled by users. + * + * @hide + */ + @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) + public static final int SATELLITE_RESULT_LOCATION_DISABLED = 25; + + /** + * Telephony framework needs to access the current location of the device to perform the + * request. However, Telephony fails to fetch the current location from location service. + * + * @hide + */ + @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) + public static final int SATELLITE_RESULT_LOCATION_NOT_AVAILABLE = 26; + /** @hide */ @IntDef(prefix = {"SATELLITE_RESULT_"}, value = { SATELLITE_RESULT_SUCCESS, @@ -397,7 +415,9 @@ public final class SatelliteManager { SATELLITE_RESULT_REQUEST_IN_PROGRESS, SATELLITE_RESULT_MODEM_BUSY, SATELLITE_RESULT_ILLEGAL_STATE, - SATELLITE_RESULT_MODEM_TIMEOUT + SATELLITE_RESULT_MODEM_TIMEOUT, + SATELLITE_RESULT_LOCATION_DISABLED, + SATELLITE_RESULT_LOCATION_NOT_AVAILABLE }) @Retention(RetentionPolicy.SOURCE) public @interface SatelliteResult {} diff --git a/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt b/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt index 0f1373c34ce6..2d5b50b528a3 100644 --- a/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt +++ b/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt @@ -17,7 +17,6 @@ package com.android.protolog.tool import com.android.internal.protolog.common.LogLevel -import com.android.internal.protolog.common.ProtoLog import com.android.internal.protolog.common.ProtoLogToolInjected import com.android.protolog.tool.CommandOptions.Companion.USAGE import com.github.javaparser.ParseProblemException @@ -61,6 +60,8 @@ object ProtoLogTool { const val PROTOLOG_IMPL_SRC_PATH = "frameworks/base/core/java/com/android/internal/protolog/ProtoLogImpl.java" + private const val PROTOLOG_CLASS_NAME = "ProtoLog"; // ProtoLog::class.java.simpleName + data class LogCall( val messageString: String, val logLevel: LogLevel, @@ -124,7 +125,7 @@ object ProtoLogTool { val text = injector.readText(file) val outSrc = try { val code = tryParse(text, path) - if (containsProtoLogText(text, ProtoLog::class.java.simpleName)) { + if (containsProtoLogText(text, PROTOLOG_CLASS_NAME)) { transformer.processClass(text, path, packagePath(file, code), code) } else { text diff --git a/tools/protologtool/tests/com/android/protolog/tool/EndToEndTest.kt b/tools/protologtool/tests/com/android/protolog/tool/EndToEndTest.kt index 822118cc5343..2a8367787ba1 100644 --- a/tools/protologtool/tests/com/android/protolog/tool/EndToEndTest.kt +++ b/tools/protologtool/tests/com/android/protolog/tool/EndToEndTest.kt @@ -35,7 +35,7 @@ class EndToEndTest { val output = run( srcs = mapOf("frameworks/base/org/example/Example.java" to """ package org.example; - import com.android.internal.protolog.common.ProtoLog; + import com.android.internal.protolog.ProtoLog; import static com.android.internal.protolog.ProtoLogGroup.GROUP; class Example { @@ -48,7 +48,7 @@ class EndToEndTest { """.trimIndent()), logGroup = LogGroup("GROUP", true, false, "TAG_GROUP"), commandOptions = CommandOptions(arrayOf("transform-protolog-calls", - "--protolog-class", "com.android.internal.protolog.common.ProtoLog", + "--protolog-class", "com.android.internal.protolog.ProtoLog", "--loggroups-class", "com.android.internal.protolog.ProtoLogGroup", "--loggroups-jar", "not_required.jar", "--viewer-config-file-path", "not_required.pb", @@ -69,7 +69,7 @@ class EndToEndTest { val output = run( srcs = mapOf("frameworks/base/org/example/Example.java" to """ package org.example; - import com.android.internal.protolog.common.ProtoLog; + import com.android.internal.protolog.ProtoLog; import static com.android.internal.protolog.ProtoLogGroup.GROUP; class Example { @@ -82,7 +82,7 @@ class EndToEndTest { """.trimIndent()), logGroup = LogGroup("GROUP", true, false, "TAG_GROUP"), commandOptions = CommandOptions(arrayOf("generate-viewer-config", - "--protolog-class", "com.android.internal.protolog.common.ProtoLog", + "--protolog-class", "com.android.internal.protolog.ProtoLog", "--loggroups-class", "com.android.internal.protolog.ProtoLogGroup", "--loggroups-jar", "not_required.jar", "--viewer-config-type", "json", |