diff options
Diffstat (limited to 'services')
58 files changed, 1587 insertions, 1094 deletions
diff --git a/services/companion/java/com/android/server/companion/virtual/InputController.java b/services/companion/java/com/android/server/companion/virtual/InputController.java index ec30369bd099..02053cc7cfd3 100644 --- a/services/companion/java/com/android/server/companion/virtual/InputController.java +++ b/services/companion/java/com/android/server/companion/virtual/InputController.java @@ -31,6 +31,7 @@ import android.hardware.input.VirtualMouseScrollEvent; import android.hardware.input.VirtualTouchEvent; import android.os.Handler; import android.os.IBinder; +import android.os.IInputConstants; import android.os.RemoteException; import android.util.ArrayMap; import android.util.Slog; @@ -75,7 +76,7 @@ class InputController { @interface PhysType { } - private final Object mLock; + final Object mLock; /* Token -> file descriptor associations. */ @VisibleForTesting @@ -220,6 +221,19 @@ class InputController { } } + /** + * @return the device id for a given token (identifiying a device) + */ + int getInputDeviceId(IBinder token) { + synchronized (mLock) { + final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get(token); + if (inputDeviceDescriptor == null) { + throw new IllegalArgumentException("Could not get device id for given token"); + } + return inputDeviceDescriptor.getInputDeviceId(); + } + } + void setShowPointerIcon(boolean visible, int displayId) { mInputManagerInternal.setPointerIconVisible(visible, displayId); } @@ -393,10 +407,22 @@ class InputController { + inputDeviceDescriptor.getCreationOrderNumber()); fout.println(" type: " + inputDeviceDescriptor.getType()); fout.println(" phys: " + inputDeviceDescriptor.getPhys()); + fout.println( + " inputDeviceId: " + inputDeviceDescriptor.getInputDeviceId()); } } } + @VisibleForTesting + void addDeviceForTesting(IBinder deviceToken, int fd, int type, int displayId, + String phys, int inputDeviceId) { + synchronized (mLock) { + mInputDeviceDescriptors.put(deviceToken, + new InputDeviceDescriptor(fd, () -> {}, type, displayId, phys, + inputDeviceId)); + } + } + private static native int nativeOpenUinputDpad(String deviceName, int vendorId, int productId, String phys); private static native int nativeOpenUinputKeyboard(String deviceName, int vendorId, @@ -493,16 +519,20 @@ class InputController { private final @Type int mType; private final int mDisplayId; private final String mPhys; + // The input device id that was associated to the device by the InputReader on device + // creation. + private final int mInputDeviceId; // Monotonically increasing number; devices with lower numbers were created earlier. private final long mCreationOrderNumber; InputDeviceDescriptor(int fd, IBinder.DeathRecipient deathRecipient, @Type int type, - int displayId, String phys) { + int displayId, String phys, int inputDeviceId) { mFd = fd; mDeathRecipient = deathRecipient; mType = type; mDisplayId = displayId; mPhys = phys; + mInputDeviceId = inputDeviceId; mCreationOrderNumber = sNextCreationOrderNumber.getAndIncrement(); } @@ -533,6 +563,10 @@ class InputController { public String getPhys() { return mPhys; } + + public int getInputDeviceId() { + return mInputDeviceId; + } } private final class BinderDeathRecipient implements IBinder.DeathRecipient { @@ -558,6 +592,8 @@ class InputController { private final CountDownLatch mDeviceAddedLatch = new CountDownLatch(1); private final InputManager.InputDeviceListener mListener; + private int mInputDeviceId = IInputConstants.INVALID_INPUT_DEVICE_ID; + WaitForDevice(String deviceName, int vendorId, int productId) { mListener = new InputManager.InputDeviceListener() { @Override @@ -572,6 +608,7 @@ class InputController { if (id.getVendorId() != vendorId || id.getProductId() != productId) { return; } + mInputDeviceId = deviceId; mDeviceAddedLatch.countDown(); } @@ -588,8 +625,13 @@ class InputController { InputManager.getInstance().registerInputDeviceListener(mListener, mHandler); } - /** Note: This must not be called from {@link #mHandler}'s thread. */ - void waitForDeviceCreation() throws DeviceCreationException { + /** + * Note: This must not be called from {@link #mHandler}'s thread. + * @throws DeviceCreationException if the device was not created successfully within the + * timeout. + * @return The id of the created input device. + */ + int waitForDeviceCreation() throws DeviceCreationException { try { if (!mDeviceAddedLatch.await(1, TimeUnit.MINUTES)) { throw new DeviceCreationException( @@ -599,6 +641,12 @@ class InputController { throw new DeviceCreationException( "Interrupted while waiting for virtual device to be created.", e); } + if (mInputDeviceId == IInputConstants.INVALID_INPUT_DEVICE_ID) { + throw new IllegalStateException( + "Virtual input device was created with an invalid " + + "id=" + mInputDeviceId); + } + return mInputDeviceId; } @Override @@ -643,6 +691,8 @@ class InputController { final int fd; final BinderDeathRecipient binderDeathRecipient; + final int inputDeviceId; + setUniqueIdAssociation(displayId, phys); try (WaitForDevice waiter = new WaitForDevice(deviceName, vendorId, productId)) { fd = deviceOpener.get(); @@ -652,7 +702,7 @@ class InputController { } // The fd is valid from here, so ensure that all failures close the fd after this point. try { - waiter.waitForDeviceCreation(); + inputDeviceId = waiter.waitForDeviceCreation(); binderDeathRecipient = new BinderDeathRecipient(deviceToken); try { @@ -672,7 +722,8 @@ class InputController { synchronized (mLock) { mInputDeviceDescriptors.put(deviceToken, - new InputDeviceDescriptor(fd, binderDeathRecipient, type, displayId, phys)); + new InputDeviceDescriptor(fd, binderDeathRecipient, type, displayId, phys, + inputDeviceId)); } } 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 2835b69b3039..5ebbf07526f1 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java @@ -498,6 +498,17 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub } @Override // Binder call + public int getInputDeviceId(IBinder token) { + final long binderToken = Binder.clearCallingIdentity(); + try { + return mInputController.getInputDeviceId(token); + } finally { + Binder.restoreCallingIdentity(binderToken); + } + } + + + @Override // Binder call public boolean sendDpadKeyEvent(IBinder token, VirtualKeyEvent event) { final long binderToken = Binder.clearCallingIdentity(); try { diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index f7833b0f36fd..2652ebec5255 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -2581,33 +2581,39 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { if (!checkNotifyPermission("notifyBarringInfo()")) { return; } - if (barringInfo == null) { - log("Received null BarringInfo for subId=" + subId + ", phoneId=" + phoneId); - mBarringInfo.set(phoneId, new BarringInfo()); + if (!validatePhoneId(phoneId)) { + loge("Received invalid phoneId for BarringInfo = " + phoneId); return; } synchronized (mRecords) { - if (validatePhoneId(phoneId)) { - mBarringInfo.set(phoneId, barringInfo); - // Barring info is non-null - BarringInfo biNoLocation = barringInfo.createLocationInfoSanitizedCopy(); - if (VDBG) log("listen: call onBarringInfoChanged=" + barringInfo); - for (Record r : mRecords) { - if (r.matchTelephonyCallbackEvent( - TelephonyCallback.EVENT_BARRING_INFO_CHANGED) - && idMatch(r, subId, phoneId)) { - try { - if (DBG_LOC) { - log("notifyBarringInfo: mBarringInfo=" - + barringInfo + " r=" + r); - } - r.callback.onBarringInfoChanged( - checkFineLocationAccess(r, Build.VERSION_CODES.BASE) - ? barringInfo : biNoLocation); - } catch (RemoteException ex) { - mRemoveList.add(r.binder); + if (barringInfo == null) { + loge("Received null BarringInfo for subId=" + subId + ", phoneId=" + phoneId); + mBarringInfo.set(phoneId, new BarringInfo()); + return; + } + if (barringInfo.equals(mBarringInfo.get(phoneId))) { + if (VDBG) log("Ignoring duplicate barring info."); + return; + } + mBarringInfo.set(phoneId, barringInfo); + // Barring info is non-null + BarringInfo biNoLocation = barringInfo.createLocationInfoSanitizedCopy(); + if (VDBG) log("listen: call onBarringInfoChanged=" + barringInfo); + for (Record r : mRecords) { + if (r.matchTelephonyCallbackEvent( + TelephonyCallback.EVENT_BARRING_INFO_CHANGED) + && idMatch(r, subId, phoneId)) { + try { + if (DBG_LOC) { + log("notifyBarringInfo: mBarringInfo=" + + barringInfo + " r=" + r); } + r.callback.onBarringInfoChanged( + checkFineLocationAccess(r, Build.VERSION_CODES.BASE) + ? barringInfo : biNoLocation); + } catch (RemoteException ex) { + mRemoveList.add(r.binder); } } } diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java index 202f47759272..5d46de335781 100644 --- a/services/core/java/com/android/server/UiModeManagerService.java +++ b/services/core/java/com/android/server/UiModeManagerService.java @@ -152,6 +152,8 @@ final class UiModeManagerService extends SystemService { // flag set by resource, whether to start dream immediately upon docking even if unlocked. private boolean mStartDreamImmediatelyOnDock = true; + // flag set by resource, whether to disable dreams when ambient mode suppression is enabled. + private boolean mDreamsDisabledByAmbientModeSuppression = false; // flag set by resource, whether to enable Car dock launch when starting car mode. private boolean mEnableCarDockLaunch = true; // flag set by resource, whether to lock UI mode to the default one or not. @@ -364,6 +366,11 @@ final class UiModeManagerService extends SystemService { mStartDreamImmediatelyOnDock = startDreamImmediatelyOnDock; } + @VisibleForTesting + void setDreamsDisabledByAmbientModeSuppression(boolean disabledByAmbientModeSuppression) { + mDreamsDisabledByAmbientModeSuppression = disabledByAmbientModeSuppression; + } + @Override public void onUserSwitching(@Nullable TargetUser from, @NonNull TargetUser to) { mCurrentUser = to.getUserIdentifier(); @@ -424,6 +431,8 @@ final class UiModeManagerService extends SystemService { final Resources res = context.getResources(); mStartDreamImmediatelyOnDock = res.getBoolean( com.android.internal.R.bool.config_startDreamImmediatelyOnDock); + mDreamsDisabledByAmbientModeSuppression = res.getBoolean( + com.android.internal.R.bool.config_dreamsDisabledByAmbientModeSuppressionConfig); mNightMode = res.getInteger( com.android.internal.R.integer.config_defaultNightMode); mDefaultUiModeType = res.getInteger( @@ -1827,10 +1836,14 @@ final class UiModeManagerService extends SystemService { // Send the new configuration. applyConfigurationExternallyLocked(); + final boolean dreamsSuppressed = mDreamsDisabledByAmbientModeSuppression + && mLocalPowerManager.isAmbientDisplaySuppressed(); + // If we did not start a dock app, then start dreaming if appropriate. - if (category != null && !dockAppStarted && (mStartDreamImmediatelyOnDock - || mWindowManager.isKeyguardShowingAndNotOccluded() - || !mPowerManager.isInteractive())) { + if (category != null && !dockAppStarted && !dreamsSuppressed && ( + mStartDreamImmediatelyOnDock + || mWindowManager.isKeyguardShowingAndNotOccluded() + || !mPowerManager.isInteractive())) { mInjector.startDreamWhenDockedIfAppropriate(getContext()); } } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 7a09109e377b..63f81822e867 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -13373,27 +13373,19 @@ public class ActivityManagerService extends IActivityManager.Stub int callingPid; boolean instantApp; synchronized(this) { - if (caller != null) { - callerApp = getRecordForAppLOSP(caller); - if (callerApp == null) { - throw new SecurityException( - "Unable to find app for caller " + caller - + " (pid=" + Binder.getCallingPid() - + ") when registering receiver " + receiver); - } - if (callerApp.info.uid != SYSTEM_UID - && !callerApp.getPkgList().containsKey(callerPackage) - && !"android".equals(callerPackage)) { - throw new SecurityException("Given caller package " + callerPackage - + " is not running in process " + callerApp); - } - callingUid = callerApp.info.uid; - callingPid = callerApp.getPid(); - } else { - callerPackage = null; - callingUid = Binder.getCallingUid(); - callingPid = Binder.getCallingPid(); + callerApp = getRecordForAppLOSP(caller); + if (callerApp == null) { + Slog.w(TAG, "registerReceiverWithFeature: no app for " + caller); + return null; + } + if (callerApp.info.uid != SYSTEM_UID + && !callerApp.getPkgList().containsKey(callerPackage) + && !"android".equals(callerPackage)) { + throw new SecurityException("Given caller package " + callerPackage + + " is not running in process " + callerApp); } + callingUid = callerApp.info.uid; + callingPid = callerApp.getPid(); instantApp = isInstantApp(callerApp, callerPackage, callingUid); userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, true, @@ -14700,13 +14692,14 @@ public class ActivityManagerService extends IActivityManager.Stub // Non-system callers can't declare that a broadcast is alarm-related. // The PendingIntent invocation case is handled in PendingIntentRecord. if (bOptions != null && callingUid != SYSTEM_UID) { - if (bOptions.containsKey(BroadcastOptions.KEY_ALARM_BROADCAST)) { + if (bOptions.containsKey(BroadcastOptions.KEY_ALARM_BROADCAST) + || bOptions.containsKey(BroadcastOptions.KEY_INTERACTIVE_BROADCAST)) { if (DEBUG_BROADCAST) { Slog.w(TAG, "Non-system caller " + callingUid - + " may not flag broadcast as alarm-related"); + + " may not flag broadcast as alarm or interactive"); } throw new SecurityException( - "Non-system callers may not flag broadcasts as alarm-related"); + "Non-system callers may not flag broadcasts as alarm or interactive"); } } diff --git a/services/core/java/com/android/server/am/BroadcastConstants.java b/services/core/java/com/android/server/am/BroadcastConstants.java index a4a1c2f0d87c..ebcd6239afd3 100644 --- a/services/core/java/com/android/server/am/BroadcastConstants.java +++ b/services/core/java/com/android/server/am/BroadcastConstants.java @@ -133,7 +133,7 @@ public class BroadcastConstants { */ public boolean MODERN_QUEUE_ENABLED = DEFAULT_MODERN_QUEUE_ENABLED; private static final String KEY_MODERN_QUEUE_ENABLED = "modern_queue_enabled"; - private static final boolean DEFAULT_MODERN_QUEUE_ENABLED = false; + private static final boolean DEFAULT_MODERN_QUEUE_ENABLED = true; /** * For {@link BroadcastQueueModernImpl}: Maximum number of process queues to @@ -167,7 +167,7 @@ public class BroadcastConstants { */ public long DELAY_NORMAL_MILLIS = DEFAULT_DELAY_NORMAL_MILLIS; private static final String KEY_DELAY_NORMAL_MILLIS = "bcast_delay_normal_millis"; - private static final long DEFAULT_DELAY_NORMAL_MILLIS = 10_000 * Build.HW_TIMEOUT_MULTIPLIER; + private static final long DEFAULT_DELAY_NORMAL_MILLIS = 1_000; /** * For {@link BroadcastQueueModernImpl}: Delay to apply to broadcasts @@ -175,7 +175,7 @@ public class BroadcastConstants { */ public long DELAY_CACHED_MILLIS = DEFAULT_DELAY_CACHED_MILLIS; private static final String KEY_DELAY_CACHED_MILLIS = "bcast_delay_cached_millis"; - private static final long DEFAULT_DELAY_CACHED_MILLIS = 30_000 * Build.HW_TIMEOUT_MULTIPLIER; + private static final long DEFAULT_DELAY_CACHED_MILLIS = 10_000; /** * For {@link BroadcastQueueModernImpl}: Maximum number of complete diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java index 0d6ac1d57387..868c3ae6da50 100644 --- a/services/core/java/com/android/server/am/BroadcastProcessQueue.java +++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java @@ -103,6 +103,13 @@ class BroadcastProcessQueue { private final ArrayDeque<SomeArgs> mPending = new ArrayDeque<>(); /** + * Ordered collection of "urgent" broadcasts that are waiting to be + * dispatched to this process, in the same representation as + * {@link #mPending}. + */ + private final ArrayDeque<SomeArgs> mPendingUrgent = new ArrayDeque<>(); + + /** * Broadcast actively being dispatched to this process. */ private @Nullable BroadcastRecord mActive; @@ -140,12 +147,16 @@ class BroadcastProcessQueue { private int mCountOrdered; private int mCountAlarm; private int mCountPrioritized; + private int mCountInteractive; + private int mCountResultTo; + private int mCountInstrumented; private @UptimeMillisLong long mRunnableAt = Long.MAX_VALUE; private @Reason int mRunnableAtReason = REASON_EMPTY; private boolean mRunnableAtInvalidated; private boolean mProcessCached; + private boolean mProcessInstrumented; private String mCachedToString; private String mCachedToShortString; @@ -172,40 +183,65 @@ class BroadcastProcessQueue { */ public void enqueueOrReplaceBroadcast(@NonNull BroadcastRecord record, int recordIndex, int blockedUntilTerminalCount) { - // If caller wants to replace, walk backwards looking for any matches if (record.isReplacePending()) { - final Iterator<SomeArgs> it = mPending.descendingIterator(); - final Object receiver = record.receivers.get(recordIndex); - while (it.hasNext()) { - final SomeArgs args = it.next(); - final BroadcastRecord testRecord = (BroadcastRecord) args.arg1; - final Object testReceiver = testRecord.receivers.get(args.argi1); - if ((record.callingUid == testRecord.callingUid) - && (record.userId == testRecord.userId) - && record.intent.filterEquals(testRecord.intent) - && isReceiverEquals(receiver, testReceiver)) { - // Exact match found; perform in-place swap - args.arg1 = record; - args.argi1 = recordIndex; - args.argi2 = blockedUntilTerminalCount; - onBroadcastDequeued(testRecord); - onBroadcastEnqueued(record); - return; - } + boolean didReplace = replaceBroadcastInQueue(mPending, + record, recordIndex, blockedUntilTerminalCount) + || replaceBroadcastInQueue(mPendingUrgent, + record, recordIndex, blockedUntilTerminalCount); + if (didReplace) { + return; } } // Caller isn't interested in replacing, or we didn't find any pending // item to replace above, so enqueue as a new broadcast - SomeArgs args = SomeArgs.obtain(); - args.arg1 = record; - args.argi1 = recordIndex; - args.argi2 = blockedUntilTerminalCount; - mPending.addLast(args); + SomeArgs newBroadcastArgs = SomeArgs.obtain(); + newBroadcastArgs.arg1 = record; + newBroadcastArgs.argi1 = recordIndex; + newBroadcastArgs.argi2 = blockedUntilTerminalCount; + + // Cross-broadcast prioritization policy: some broadcasts might warrant being + // issued ahead of others that are already pending, for example if this new + // broadcast is in a different delivery class or is tied to a direct user interaction + // with implicit responsiveness expectations. + final ArrayDeque<SomeArgs> queue = record.isUrgent() ? mPendingUrgent : mPending; + queue.addLast(newBroadcastArgs); onBroadcastEnqueued(record); } /** + * Searches from newest to oldest, and at the first matching pending broadcast + * it finds, replaces it in-place and returns -- does not attempt to handle + * "duplicate" broadcasts in the queue. + * <p> + * @return {@code true} if it found and replaced an existing record in the queue; + * {@code false} otherwise. + */ + private boolean replaceBroadcastInQueue(@NonNull ArrayDeque<SomeArgs> queue, + @NonNull BroadcastRecord record, int recordIndex, int blockedUntilTerminalCount) { + final Iterator<SomeArgs> it = queue.descendingIterator(); + final Object receiver = record.receivers.get(recordIndex); + while (it.hasNext()) { + final SomeArgs args = it.next(); + final BroadcastRecord testRecord = (BroadcastRecord) args.arg1; + final Object testReceiver = testRecord.receivers.get(args.argi1); + if ((record.callingUid == testRecord.callingUid) + && (record.userId == testRecord.userId) + && record.intent.filterEquals(testRecord.intent) + && isReceiverEquals(receiver, testReceiver)) { + // Exact match found; perform in-place swap + args.arg1 = record; + args.argi1 = recordIndex; + args.argi2 = blockedUntilTerminalCount; + onBroadcastDequeued(testRecord); + onBroadcastEnqueued(record); + return true; + } + } + return false; + } + + /** * Functional interface that tests a {@link BroadcastRecord} that has been * previously enqueued in {@link BroadcastProcessQueue}. */ @@ -233,8 +269,18 @@ class BroadcastProcessQueue { */ public boolean forEachMatchingBroadcast(@NonNull BroadcastPredicate predicate, @NonNull BroadcastConsumer consumer, boolean andRemove) { + boolean didSomething = forEachMatchingBroadcastInQueue(mPending, + predicate, consumer, andRemove); + didSomething |= forEachMatchingBroadcastInQueue(mPendingUrgent, + predicate, consumer, andRemove); + return didSomething; + } + + private boolean forEachMatchingBroadcastInQueue(@NonNull ArrayDeque<SomeArgs> queue, + @NonNull BroadcastPredicate predicate, @NonNull BroadcastConsumer consumer, + boolean andRemove) { boolean didSomething = false; - final Iterator<SomeArgs> it = mPending.iterator(); + final Iterator<SomeArgs> it = queue.iterator(); while (it.hasNext()) { final SomeArgs args = it.next(); final BroadcastRecord record = (BroadcastRecord) args.arg1; @@ -255,6 +301,18 @@ class BroadcastProcessQueue { } /** + * Update the actively running "warm" process for this process. + */ + public void setProcess(@Nullable ProcessRecord app) { + this.app = app; + if (app != null) { + setProcessInstrumented(app.getActiveInstrumentation() != null); + } else { + setProcessInstrumented(false); + } + } + + /** * Update if this process is in the "cached" state, typically signaling that * broadcast dispatch should be paused or delayed. */ @@ -266,6 +324,18 @@ class BroadcastProcessQueue { } /** + * Update if this process is in the "instrumented" state, typically + * signaling that broadcast dispatch should bypass all pauses or delays, to + * avoid holding up test suites. + */ + public void setProcessInstrumented(boolean instrumented) { + if (mProcessInstrumented != instrumented) { + mProcessInstrumented = instrumented; + invalidateRunnableAt(); + } + } + + /** * Return if we know of an actively running "warm" process for this queue. */ public boolean isProcessWarm() { @@ -273,13 +343,12 @@ class BroadcastProcessQueue { } public int getPreferredSchedulingGroupLocked() { - if (mCountForeground > 0 || mCountOrdered > 0 || mCountAlarm > 0) { - // We have an important broadcast somewhere down the queue, so + if (mCountForeground > 0) { + // We have a foreground broadcast somewhere down the queue, so // boost priority until we drain them all return ProcessList.SCHED_GROUP_DEFAULT; - } else if ((mActive != null) - && (mActive.isForeground() || mActive.ordered || mActive.alarm)) { - // We have an important broadcast right now, so boost priority + } else if ((mActive != null) && mActive.isForeground()) { + // We have a foreground broadcast right now, so boost priority return ProcessList.SCHED_GROUP_DEFAULT; } else if (!isIdle()) { return ProcessList.SCHED_GROUP_BACKGROUND; @@ -309,7 +378,7 @@ class BroadcastProcessQueue { */ public void makeActiveNextPending() { // TODO: what if the next broadcast isn't runnable yet? - final SomeArgs next = mPending.removeFirst(); + final SomeArgs next = removeNextBroadcast(); mActive = (BroadcastRecord) next.arg1; mActiveIndex = next.argi1; mActiveBlockedUntilTerminalCount = next.argi2; @@ -347,6 +416,15 @@ class BroadcastProcessQueue { if (record.prioritized) { mCountPrioritized++; } + if (record.interactive) { + mCountInteractive++; + } + if (record.resultTo != null) { + mCountResultTo++; + } + if (record.callerInstrumented) { + mCountInstrumented++; + } invalidateRunnableAt(); } @@ -366,6 +444,15 @@ class BroadcastProcessQueue { if (record.prioritized) { mCountPrioritized--; } + if (record.interactive) { + mCountInteractive--; + } + if (record.resultTo != null) { + mCountResultTo--; + } + if (record.callerInstrumented) { + mCountInstrumented--; + } invalidateRunnableAt(); } @@ -413,7 +500,7 @@ class BroadcastProcessQueue { } public boolean isEmpty() { - return mPending.isEmpty(); + return mPending.isEmpty() && mPendingUrgent.isEmpty(); } public boolean isActive() { @@ -421,6 +508,38 @@ class BroadcastProcessQueue { } /** + * Will thrown an exception if there are no pending broadcasts; relies on + * {@link #isEmpty()} being false. + */ + SomeArgs removeNextBroadcast() { + ArrayDeque<SomeArgs> queue = queueForNextBroadcast(); + return queue.removeFirst(); + } + + @Nullable ArrayDeque<SomeArgs> queueForNextBroadcast() { + if (!mPendingUrgent.isEmpty()) { + return mPendingUrgent; + } else if (!mPending.isEmpty()) { + return mPending; + } + return null; + } + + /** + * Returns null if there are no pending broadcasts + */ + @Nullable SomeArgs peekNextBroadcast() { + ArrayDeque<SomeArgs> queue = queueForNextBroadcast(); + return (queue != null) ? queue.peekFirst() : null; + } + + @VisibleForTesting + @Nullable BroadcastRecord peekNextBroadcastRecord() { + ArrayDeque<SomeArgs> queue = queueForNextBroadcast(); + return (queue != null) ? (BroadcastRecord) queue.peekFirst().arg1 : null; + } + + /** * Quickly determine if this queue has broadcasts that are still waiting to * be delivered at some point in the future. */ @@ -437,11 +556,13 @@ class BroadcastProcessQueue { return mActive.enqueueTime > barrierTime; } final SomeArgs next = mPending.peekFirst(); - if (next != null) { - return ((BroadcastRecord) next.arg1).enqueueTime > barrierTime; - } - // Nothing running or runnable means we're past the barrier - return true; + final SomeArgs nextUrgent = mPendingUrgent.peekFirst(); + // Empty queue is past any barrier + final boolean nextLater = next == null + || ((BroadcastRecord) next.arg1).enqueueTime > barrierTime; + final boolean nextUrgentLater = nextUrgent == null + || ((BroadcastRecord) nextUrgent.arg1).enqueueTime > barrierTime; + return nextLater && nextUrgentLater; } public boolean isRunnable() { @@ -477,25 +598,33 @@ class BroadcastProcessQueue { } static final int REASON_EMPTY = 0; - static final int REASON_CONTAINS_FOREGROUND = 1; - static final int REASON_CONTAINS_ORDERED = 2; - static final int REASON_CONTAINS_ALARM = 3; - static final int REASON_CONTAINS_PRIORITIZED = 4; - static final int REASON_CACHED = 5; - static final int REASON_NORMAL = 6; - static final int REASON_MAX_PENDING = 7; - static final int REASON_BLOCKED = 8; + static final int REASON_CACHED = 1; + static final int REASON_NORMAL = 2; + static final int REASON_MAX_PENDING = 3; + static final int REASON_BLOCKED = 4; + static final int REASON_INSTRUMENTED = 5; + static final int REASON_CONTAINS_FOREGROUND = 10; + static final int REASON_CONTAINS_ORDERED = 11; + static final int REASON_CONTAINS_ALARM = 12; + static final int REASON_CONTAINS_PRIORITIZED = 13; + static final int REASON_CONTAINS_INTERACTIVE = 14; + static final int REASON_CONTAINS_RESULT_TO = 15; + static final int REASON_CONTAINS_INSTRUMENTED = 16; @IntDef(flag = false, prefix = { "REASON_" }, value = { REASON_EMPTY, - REASON_CONTAINS_FOREGROUND, - REASON_CONTAINS_ORDERED, - REASON_CONTAINS_ALARM, - REASON_CONTAINS_PRIORITIZED, REASON_CACHED, REASON_NORMAL, REASON_MAX_PENDING, REASON_BLOCKED, + REASON_INSTRUMENTED, + REASON_CONTAINS_FOREGROUND, + REASON_CONTAINS_ORDERED, + REASON_CONTAINS_ALARM, + REASON_CONTAINS_PRIORITIZED, + REASON_CONTAINS_INTERACTIVE, + REASON_CONTAINS_RESULT_TO, + REASON_CONTAINS_INSTRUMENTED, }) @Retention(RetentionPolicy.SOURCE) public @interface Reason {} @@ -503,14 +632,18 @@ class BroadcastProcessQueue { static @NonNull String reasonToString(@Reason int reason) { switch (reason) { case REASON_EMPTY: return "EMPTY"; - case REASON_CONTAINS_FOREGROUND: return "CONTAINS_FOREGROUND"; - case REASON_CONTAINS_ORDERED: return "CONTAINS_ORDERED"; - case REASON_CONTAINS_ALARM: return "CONTAINS_ALARM"; - case REASON_CONTAINS_PRIORITIZED: return "CONTAINS_PRIORITIZED"; case REASON_CACHED: return "CACHED"; case REASON_NORMAL: return "NORMAL"; case REASON_MAX_PENDING: return "MAX_PENDING"; case REASON_BLOCKED: return "BLOCKED"; + case REASON_INSTRUMENTED: return "INSTRUMENTED"; + case REASON_CONTAINS_FOREGROUND: return "CONTAINS_FOREGROUND"; + case REASON_CONTAINS_ORDERED: return "CONTAINS_ORDERED"; + case REASON_CONTAINS_ALARM: return "CONTAINS_ALARM"; + case REASON_CONTAINS_PRIORITIZED: return "CONTAINS_PRIORITIZED"; + case REASON_CONTAINS_INTERACTIVE: return "CONTAINS_INTERACTIVE"; + case REASON_CONTAINS_RESULT_TO: return "CONTAINS_RESULT_TO"; + case REASON_CONTAINS_INSTRUMENTED: return "CONTAINS_INSTRUMENTED"; default: return Integer.toString(reason); } } @@ -519,7 +652,7 @@ class BroadcastProcessQueue { * Update {@link #getRunnableAt()} if it's currently invalidated. */ private void updateRunnableAt() { - final SomeArgs next = mPending.peekFirst(); + final SomeArgs next = peekNextBroadcast(); if (next != null) { final BroadcastRecord r = (BroadcastRecord) next.arg1; final int index = next.argi1; @@ -537,7 +670,7 @@ class BroadcastProcessQueue { // If we have too many broadcasts pending, bypass any delays that // might have been applied above to aid draining - if (mPending.size() >= constants.MAX_PENDING_BROADCASTS) { + if (mPending.size() + mPendingUrgent.size() >= constants.MAX_PENDING_BROADCASTS) { mRunnableAt = runnableAt; mRunnableAtReason = REASON_MAX_PENDING; return; @@ -555,6 +688,18 @@ class BroadcastProcessQueue { } else if (mCountPrioritized > 0) { mRunnableAt = runnableAt; mRunnableAtReason = REASON_CONTAINS_PRIORITIZED; + } else if (mCountInteractive > 0) { + mRunnableAt = runnableAt; + mRunnableAtReason = REASON_CONTAINS_INTERACTIVE; + } else if (mCountResultTo > 0) { + mRunnableAt = runnableAt; + mRunnableAtReason = REASON_CONTAINS_RESULT_TO; + } else if (mCountInstrumented > 0) { + mRunnableAt = runnableAt; + mRunnableAtReason = REASON_CONTAINS_INSTRUMENTED; + } else if (mProcessInstrumented) { + mRunnableAt = runnableAt; + mRunnableAtReason = REASON_INSTRUMENTED; } else if (mProcessCached) { mRunnableAt = runnableAt + constants.DELAY_CACHED_MILLIS; mRunnableAtReason = REASON_CACHED; @@ -574,8 +719,8 @@ class BroadcastProcessQueue { */ public void checkHealthLocked() { if (mRunnableAtReason == REASON_BLOCKED) { - final SomeArgs next = mPending.peekFirst(); - Objects.requireNonNull(next, "peekFirst"); + final SomeArgs next = peekNextBroadcast(); + Objects.requireNonNull(next, "peekNextBroadcast"); // If blocked more than 10 minutes, we're likely wedged final BroadcastRecord r = (BroadcastRecord) next.arg1; diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java index 1e1ebeba5c23..db3ef3d51b16 100644 --- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java +++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java @@ -44,6 +44,7 @@ import android.annotation.Nullable; import android.annotation.UptimeMillisLong; import android.app.Activity; import android.app.ActivityManager; +import android.app.BroadcastOptions; import android.app.IApplicationThread; import android.app.RemoteServiceException.CannotDeliverBroadcastException; import android.app.UidObserver; @@ -440,7 +441,7 @@ class BroadcastQueueModernImpl extends BroadcastQueue { // relevant per-process queue final BroadcastProcessQueue queue = getProcessQueue(app); if (queue != null) { - queue.app = app; + queue.setProcess(app); } boolean didSomething = false; @@ -477,7 +478,7 @@ class BroadcastQueueModernImpl extends BroadcastQueue { // relevant per-process queue final BroadcastProcessQueue queue = getProcessQueue(app); if (queue != null) { - queue.app = null; + queue.setProcess(null); } if ((mRunningColdStart != null) && (mRunningColdStart == queue)) { @@ -534,6 +535,17 @@ class BroadcastQueueModernImpl extends BroadcastQueue { }, mBroadcastConsumerSkipAndCanceled, true); } + final int policy = (r.options != null) + ? r.options.getDeliveryGroupPolicy() : BroadcastOptions.DELIVERY_GROUP_POLICY_ALL; + if (policy == BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT) { + forEachMatchingBroadcast(QUEUE_PREDICATE_ANY, (testRecord, testIndex) -> { + // We only allow caller to remove broadcasts they enqueued + return (r.callingUid == testRecord.callingUid) + && (r.userId == testRecord.userId) + && r.matchesDeliveryGroup(testRecord); + }, mBroadcastConsumerSkipAndCanceled, true); + } + if (r.isReplacePending()) { // Leave the skipped broadcasts intact in queue, so that we can // replace them at their current position during enqueue below @@ -804,19 +816,21 @@ class BroadcastQueueModernImpl extends BroadcastQueue { } final BroadcastRecord r = queue.getActive(); - r.resultCode = resultCode; - r.resultData = resultData; - r.resultExtras = resultExtras; - if (!r.isNoAbort()) { - r.resultAbort = resultAbort; - } - - // When the caller aborted an ordered broadcast, we mark all remaining - // receivers as skipped - if (r.ordered && r.resultAbort) { - for (int i = r.terminalCount + 1; i < r.receivers.size(); i++) { - setDeliveryState(null, null, r, i, r.receivers.get(i), - BroadcastRecord.DELIVERY_SKIPPED); + if (r.ordered) { + r.resultCode = resultCode; + r.resultData = resultData; + r.resultExtras = resultExtras; + if (!r.isNoAbort()) { + r.resultAbort = resultAbort; + } + + // When the caller aborted an ordered broadcast, we mark all + // remaining receivers as skipped + if (r.resultAbort) { + for (int i = r.terminalCount + 1; i < r.receivers.size(); i++) { + setDeliveryState(null, null, r, i, r.receivers.get(i), + BroadcastRecord.DELIVERY_SKIPPED); + } } } @@ -913,7 +927,8 @@ class BroadcastQueueModernImpl extends BroadcastQueue { notifyFinishReceiver(queue, r, index, receiver); // When entire ordered broadcast finished, deliver final result - if (r.ordered && (r.terminalCount == r.receivers.size())) { + final boolean recordFinished = (r.terminalCount == r.receivers.size()); + if (recordFinished) { scheduleResultTo(r); } @@ -1205,7 +1220,7 @@ class BroadcastQueueModernImpl extends BroadcastQueue { private void updateWarmProcess(@NonNull BroadcastProcessQueue queue) { if (!queue.isProcessWarm()) { - queue.app = mService.getProcessRecordLocked(queue.processName, queue.uid); + queue.setProcess(mService.getProcessRecordLocked(queue.processName, queue.uid)); } } diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java index 2d825955ed6c..d7dc8b80931b 100644 --- a/services/core/java/com/android/server/am/BroadcastRecord.java +++ b/services/core/java/com/android/server/am/BroadcastRecord.java @@ -78,11 +78,13 @@ final class BroadcastRecord extends Binder { final int callingPid; // the pid of who sent this final int callingUid; // the uid of who sent this final boolean callerInstantApp; // caller is an Instant App? + final boolean callerInstrumented; // caller is being instrumented final boolean ordered; // serialize the send to receivers? final boolean sticky; // originated from existing sticky data? final boolean alarm; // originated from an alarm triggering? final boolean pushMessage; // originated from a push message? final boolean pushMessageOverQuota; // originated from a push message which was over quota? + final boolean interactive; // originated from user interaction? final boolean initialSticky; // initial broadcast from register to sticky? final boolean prioritized; // contains more than one priority tranche final int userId; // user id this broadcast was for @@ -364,6 +366,8 @@ final class BroadcastRecord extends Binder { callingPid = _callingPid; callingUid = _callingUid; callerInstantApp = _callerInstantApp; + callerInstrumented = (_callerApp != null) + ? (_callerApp.getActiveInstrumentation() != null) : false; resolvedType = _resolvedType; requiredPermissions = _requiredPermissions; excludedPermissions = _excludedPermissions; @@ -392,6 +396,7 @@ final class BroadcastRecord extends Binder { alarm = options != null && options.isAlarmBroadcast(); pushMessage = options != null && options.isPushMessagingBroadcast(); pushMessageOverQuota = options != null && options.isPushMessagingOverQuotaBroadcast(); + interactive = options != null && options.isInteractiveBroadcast(); this.filterExtrasForReceiver = filterExtrasForReceiver; } @@ -409,6 +414,7 @@ final class BroadcastRecord extends Binder { callingPid = from.callingPid; callingUid = from.callingUid; callerInstantApp = from.callerInstantApp; + callerInstrumented = from.callerInstrumented; ordered = from.ordered; sticky = from.sticky; initialSticky = from.initialSticky; @@ -450,6 +456,7 @@ final class BroadcastRecord extends Binder { alarm = from.alarm; pushMessage = from.pushMessage; pushMessageOverQuota = from.pushMessageOverQuota; + interactive = from.interactive; filterExtrasForReceiver = from.filterExtrasForReceiver; } @@ -611,6 +618,18 @@ final class BroadcastRecord extends Binder { return (intent.getFlags() & Intent.FLAG_RECEIVER_NO_ABORT) != 0; } + /** + * Core policy determination about this broadcast's delivery prioritization + */ + boolean isUrgent() { + // TODO: flags for controlling policy + // TODO: migrate alarm-prioritization flag to BroadcastConstants + return (isForeground() + || interactive + || alarm) + && receivers.size() == 1; + } + @NonNull String getHostingRecordTriggerType() { if (alarm) { return HostingRecord.TRIGGER_TYPE_ALARM; @@ -796,6 +815,16 @@ final class BroadcastRecord extends Binder { } } + public boolean matchesDeliveryGroup(@NonNull BroadcastRecord other) { + final String key = (options != null) ? options.getDeliveryGroupKey() : null; + final String otherKey = (other.options != null) + ? other.options.getDeliveryGroupKey() : null; + if (key == null && otherKey == null) { + return intent.filterEquals(other.intent); + } + return Objects.equals(key, otherKey); + } + @Override public String toString() { if (mCachedToString == null) { diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java index 975619feaea0..740efbc658ba 100644 --- a/services/core/java/com/android/server/am/PendingIntentRecord.java +++ b/services/core/java/com/android/server/am/PendingIntentRecord.java @@ -443,13 +443,14 @@ public final class PendingIntentRecord extends IIntentSender.Stub { // invocation side effects such as allowlisting. if (options != null && callingUid != Process.SYSTEM_UID && key.type == ActivityManager.INTENT_SENDER_BROADCAST) { - if (options.containsKey(BroadcastOptions.KEY_ALARM_BROADCAST)) { + if (options.containsKey(BroadcastOptions.KEY_ALARM_BROADCAST) + || options.containsKey(BroadcastOptions.KEY_INTERACTIVE_BROADCAST)) { if (DEBUG_BROADCAST_LIGHT) { Slog.w(TAG, "Non-system caller " + callingUid - + " may not flag broadcast as alarm-related"); + + " may not flag broadcast as alarm or interactive"); } throw new SecurityException( - "Non-system callers may not flag broadcasts as alarm-related"); + "Non-system callers may not flag broadcasts as alarm or interactive"); } } diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index 216a48ec699c..3fa41c0f0420 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -16,7 +16,6 @@ package com.android.server.am; -import static android.Manifest.permission.CREATE_USERS; import static android.Manifest.permission.INTERACT_ACROSS_PROFILES; import static android.Manifest.permission.INTERACT_ACROSS_USERS; import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; @@ -1482,7 +1481,7 @@ class UserController implements Handler.Callback { // defined boolean startUserOnSecondaryDisplay(@UserIdInt int userId, int displayId) { checkCallingHasOneOfThosePermissions("startUserOnSecondaryDisplay", - MANAGE_USERS, CREATE_USERS); + MANAGE_USERS, INTERACT_ACROSS_USERS); // DEFAULT_DISPLAY is used for the current foreground user only Preconditions.checkArgument(displayId != Display.DEFAULT_DISPLAY, diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthSessionCoordinator.java b/services/core/java/com/android/server/biometrics/sensors/AuthSessionCoordinator.java index dec1b559556a..5bc9d2341535 100644 --- a/services/core/java/com/android/server/biometrics/sensors/AuthSessionCoordinator.java +++ b/services/core/java/com/android/server/biometrics/sensors/AuthSessionCoordinator.java @@ -54,7 +54,7 @@ public class AuthSessionCoordinator implements AuthSessionListener { private AuthResultCoordinator mAuthResultCoordinator; public AuthSessionCoordinator() { - this(SystemClock.currentNetworkTimeClock()); + this(SystemClock.elapsedRealtimeClock()); } @VisibleForTesting diff --git a/services/core/java/com/android/server/biometrics/sensors/MultiBiometricLockoutState.java b/services/core/java/com/android/server/biometrics/sensors/MultiBiometricLockoutState.java index d9bd04d3f1c8..6605d49ece9b 100644 --- a/services/core/java/com/android/server/biometrics/sensors/MultiBiometricLockoutState.java +++ b/services/core/java/com/android/server/biometrics/sensors/MultiBiometricLockoutState.java @@ -22,7 +22,6 @@ import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMET import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_WEAK; import android.hardware.biometrics.BiometricManager; -import android.os.SystemClock; import android.util.Slog; import java.time.Clock; @@ -43,10 +42,6 @@ class MultiBiometricLockoutState { private final Map<Integer, Map<Integer, AuthenticatorState>> mCanUserAuthenticate; private final Clock mClock; - MultiBiometricLockoutState() { - this(SystemClock.currentNetworkTimeClock()); - } - MultiBiometricLockoutState(Clock clock) { mCanUserAuthenticate = new HashMap<>(); mClock = clock; diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java index f599acac7ed4..2e5663db57b5 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java @@ -16,6 +16,7 @@ package com.android.server.biometrics.sensors.fingerprint.aidl; +import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_START; import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_VENDOR; import android.annotation.NonNull; @@ -59,6 +60,7 @@ import com.android.server.biometrics.sensors.SensorOverlays; import com.android.server.biometrics.sensors.fingerprint.PowerPressHandler; import com.android.server.biometrics.sensors.fingerprint.Udfps; +import java.time.Clock; import java.util.ArrayList; import java.util.function.Supplier; @@ -92,7 +94,9 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession> private long mWaitForAuthKeyguard; private long mWaitForAuthBp; private long mIgnoreAuthFor; + private long mSideFpsLastAcquireStartTime; private Runnable mAuthSuccessRunnable; + private final Clock mClock; FingerprintAuthenticationClient( @NonNull Context context, @@ -117,7 +121,8 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession> boolean allowBackgroundAuthentication, @NonNull FingerprintSensorPropertiesInternal sensorProps, @NonNull Handler handler, - @Authenticators.Types int biometricStrength) { + @Authenticators.Types int biometricStrength, + @NonNull Clock clock) { super( context, lazyDaemon, @@ -161,6 +166,8 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession> R.integer.config_sidefpsSkipWaitForPowerVendorAcquireMessage); mBiometricStrength = biometricStrength; mAuthSessionCoordinator = biometricContext.getAuthSessionCoordinator(); + mSideFpsLastAcquireStartTime = -1; + mClock = clock; if (mSensorProps.isAnySidefpsType()) { if (Build.isDebuggable()) { @@ -246,8 +253,14 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession> return; } delay = isKeyguard() ? mWaitForAuthKeyguard : mWaitForAuthBp; - Slog.i(TAG, "(sideFPS) Auth succeeded, sideFps waiting for power for: " - + delay + "ms"); + + if (mSideFpsLastAcquireStartTime != -1) { + delay = Math.max(0, + delay - (mClock.millis() - mSideFpsLastAcquireStartTime)); + } + + Slog.i(TAG, "(sideFPS) Auth succeeded, sideFps " + + "waiting for power until: " + delay + "ms"); } if (mHandler.hasMessages(MESSAGE_FINGER_UP)) { @@ -271,13 +284,15 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession> mSensorOverlays.ifUdfps(controller -> controller.onAcquired(getSensorId(), acquiredInfo)); super.onAcquired(acquiredInfo, vendorCode); if (mSensorProps.isAnySidefpsType()) { + if (acquiredInfo == FINGERPRINT_ACQUIRED_START) { + mSideFpsLastAcquireStartTime = mClock.millis(); + } final boolean shouldLookForVendor = mSkipWaitForPowerAcquireMessage == FINGERPRINT_ACQUIRED_VENDOR; final boolean acquireMessageMatch = acquiredInfo == mSkipWaitForPowerAcquireMessage; final boolean vendorMessageMatch = vendorCode == mSkipWaitForPowerVendorAcquireMessage; final boolean ignorePowerPress = - (acquireMessageMatch && !shouldLookForVendor) || (shouldLookForVendor - && acquireMessageMatch && vendorMessageMatch); + acquireMessageMatch && (!shouldLookForVendor || vendorMessageMatch); if (ignorePowerPress) { Slog.d(TAG, "(sideFPS) onFingerUp"); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java index 774aff1dd72c..650894db431a 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java @@ -47,6 +47,7 @@ import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.SystemClock; import android.os.UserManager; import android.util.Slog; import android.util.SparseArray; @@ -449,7 +450,8 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi mTaskStackListener, mSensors.get(sensorId).getLockoutCache(), mUdfpsOverlayController, mSidefpsController, allowBackgroundAuthentication, mSensors.get(sensorId).getSensorProperties(), mHandler, - Utils.getCurrentStrength(sensorId)); + Utils.getCurrentStrength(sensorId), + SystemClock.elapsedRealtimeClock()); scheduleForSensor(sensorId, client, mBiometricStateCallback); }); } diff --git a/services/core/java/com/android/server/broadcastradio/IRadioServiceAidlImpl.java b/services/core/java/com/android/server/broadcastradio/IRadioServiceAidlImpl.java index 0770062cd4d3..6a010424db13 100644 --- a/services/core/java/com/android/server/broadcastradio/IRadioServiceAidlImpl.java +++ b/services/core/java/com/android/server/broadcastradio/IRadioServiceAidlImpl.java @@ -29,6 +29,8 @@ import android.os.ServiceManager; import android.util.IndentingPrintWriter; import android.util.Log; +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.broadcastradio.aidl.BroadcastRadioServiceImpl; import com.android.server.utils.Slogf; import java.io.FileDescriptor; @@ -47,7 +49,7 @@ final class IRadioServiceAidlImpl extends IRadioService.Stub { private static final List<String> SERVICE_NAMES = Arrays.asList( IBroadcastRadio.DESCRIPTOR + "/amfm", IBroadcastRadio.DESCRIPTOR + "/dab"); - private final com.android.server.broadcastradio.aidl.BroadcastRadioServiceImpl mHalAidl; + private final BroadcastRadioServiceImpl mHalAidl; private final BroadcastRadioService mService; /** @@ -65,10 +67,15 @@ final class IRadioServiceAidlImpl extends IRadioService.Stub { } IRadioServiceAidlImpl(BroadcastRadioService service, ArrayList<String> serviceList) { + this(service, new BroadcastRadioServiceImpl(serviceList)); Slogf.i(TAG, "Initialize BroadcastRadioServiceAidl(%s)", service); - mService = Objects.requireNonNull(service); - mHalAidl = - new com.android.server.broadcastradio.aidl.BroadcastRadioServiceImpl(serviceList); + } + + @VisibleForTesting + IRadioServiceAidlImpl(BroadcastRadioService service, BroadcastRadioServiceImpl halAidl) { + mService = Objects.requireNonNull(service, "Broadcast radio service cannot be null"); + mHalAidl = Objects.requireNonNull(halAidl, + "Broadcast radio service implementation for AIDL HAL cannot be null"); } @Override @@ -96,8 +103,8 @@ final class IRadioServiceAidlImpl extends IRadioService.Stub { if (isDebugEnabled()) { Slogf.d(TAG, "Adding announcement listener for %s", Arrays.toString(enabledTypes)); } - Objects.requireNonNull(enabledTypes); - Objects.requireNonNull(listener); + Objects.requireNonNull(enabledTypes, "Enabled announcement types cannot be null"); + Objects.requireNonNull(listener, "Announcement listener cannot be null"); mService.enforcePolicyAccess(); return mHalAidl.addAnnouncementListener(enabledTypes, listener); diff --git a/services/core/java/com/android/server/broadcastradio/IRadioServiceHidlImpl.java b/services/core/java/com/android/server/broadcastradio/IRadioServiceHidlImpl.java index 28b6d02581be..a8e4034e3f86 100644 --- a/services/core/java/com/android/server/broadcastradio/IRadioServiceHidlImpl.java +++ b/services/core/java/com/android/server/broadcastradio/IRadioServiceHidlImpl.java @@ -27,6 +27,7 @@ import android.util.IndentingPrintWriter; import android.util.Log; import android.util.Slog; +import com.android.internal.annotations.VisibleForTesting; import com.android.server.broadcastradio.hal2.AnnouncementAggregator; import java.io.FileDescriptor; @@ -53,7 +54,7 @@ final class IRadioServiceHidlImpl extends IRadioService.Stub { private final List<RadioManager.ModuleProperties> mV1Modules; IRadioServiceHidlImpl(BroadcastRadioService service) { - mService = Objects.requireNonNull(service); + mService = Objects.requireNonNull(service, "broadcast radio service cannot be null"); mHal1 = new com.android.server.broadcastradio.hal1.BroadcastRadioService(mLock); mV1Modules = mHal1.loadModules(); OptionalInt max = mV1Modules.stream().mapToInt(RadioManager.ModuleProperties::getId).max(); @@ -61,6 +62,18 @@ final class IRadioServiceHidlImpl extends IRadioService.Stub { max.isPresent() ? max.getAsInt() + 1 : 0, mLock); } + @VisibleForTesting + IRadioServiceHidlImpl(BroadcastRadioService service, + com.android.server.broadcastradio.hal1.BroadcastRadioService hal1, + com.android.server.broadcastradio.hal2.BroadcastRadioService hal2) { + mService = Objects.requireNonNull(service, "Broadcast radio service cannot be null"); + mHal1 = Objects.requireNonNull(hal1, + "Broadcast radio service implementation for HIDL 1 HAL cannot be null"); + mV1Modules = mHal1.loadModules(); + mHal2 = Objects.requireNonNull(hal2, + "Broadcast radio service implementation for HIDL 2 HAL cannot be null"); + } + @Override public List<RadioManager.ModuleProperties> listModules() { mService.enforcePolicyAccess(); @@ -95,8 +108,8 @@ final class IRadioServiceHidlImpl extends IRadioService.Stub { if (isDebugEnabled()) { Slog.d(TAG, "Adding announcement listener for " + Arrays.toString(enabledTypes)); } - Objects.requireNonNull(enabledTypes); - Objects.requireNonNull(listener); + Objects.requireNonNull(enabledTypes, "Enabled announcement types cannot be null"); + Objects.requireNonNull(listener, "Announcement listener cannot be null"); mService.enforcePolicyAccess(); synchronized (mLock) { diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 587db41c0df8..19069ea891a4 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -40,6 +40,7 @@ import static android.os.Process.ROOT_UID; import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.annotation.UserIdInt; import android.app.AppOpsManager; import android.app.compat.CompatChanges; @@ -101,6 +102,7 @@ import android.os.SystemProperties; import android.os.Trace; import android.os.UserHandle; import android.os.UserManager; +import android.provider.DeviceConfig; import android.provider.Settings; import android.sysprop.DisplayProperties; import android.text.TextUtils; @@ -202,8 +204,6 @@ public final class DisplayManagerService extends SystemService { private static final String FORCE_WIFI_DISPLAY_ENABLE = "persist.debug.wfd.enable"; private static final String PROP_DEFAULT_DISPLAY_TOP_INSET = "persist.sys.displayinset.top"; - private static final String PROP_USE_NEW_DISPLAY_POWER_CONTROLLER = - "persist.sys.use_new_display_power_controller"; private static final long WAIT_FOR_DEFAULT_DISPLAY_TIMEOUT = 10000; // This value needs to be in sync with the threshold // in RefreshRateConfigs::getFrameRateDivisor. @@ -2575,6 +2575,7 @@ public final class DisplayManagerService extends SystemService { mLogicalDisplayMapper.forEachLocked(this::addDisplayPowerControllerLocked); } + @RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG) private void addDisplayPowerControllerLocked(LogicalDisplay display) { if (mPowerHandler == null) { // initPowerManagement has not yet been called. @@ -2588,7 +2589,8 @@ public final class DisplayManagerService extends SystemService { display, mSyncRoot); final DisplayPowerControllerInterface displayPowerController; - if (SystemProperties.getInt(PROP_USE_NEW_DISPLAY_POWER_CONTROLLER, 0) == 1) { + if (DeviceConfig.getBoolean("display_manager", + "use_newly_structured_display_power_controller", false)) { displayPowerController = new DisplayPowerController2( mContext, /* injector= */ null, mDisplayPowerCallbacks, mPowerHandler, mSensorManager, mDisplayBlanker, display, mBrightnessTracker, brightnessSetting, diff --git a/services/core/java/com/android/server/dreams/DreamController.java b/services/core/java/com/android/server/dreams/DreamController.java index b8af1bfcc254..819b719dd22e 100644 --- a/services/core/java/com/android/server/dreams/DreamController.java +++ b/services/core/java/com/android/server/dreams/DreamController.java @@ -16,6 +16,9 @@ package com.android.server.dreams; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM; + +import android.app.ActivityTaskManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -34,8 +37,6 @@ import android.os.UserHandle; import android.service.dreams.DreamService; import android.service.dreams.IDreamService; import android.util.Slog; -import android.view.IWindowManager; -import android.view.WindowManagerGlobal; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; @@ -60,7 +61,7 @@ final class DreamController { private final Context mContext; private final Handler mHandler; private final Listener mListener; - private final IWindowManager mIWindowManager; + private final ActivityTaskManager mActivityTaskManager; private long mDreamStartTime; private String mSavedStopReason; @@ -93,7 +94,7 @@ final class DreamController { mContext = context; mHandler = handler; mListener = listener; - mIWindowManager = WindowManagerGlobal.getWindowManagerService(); + mActivityTaskManager = mContext.getSystemService(ActivityTaskManager.class); mCloseNotificationShadeIntent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); mCloseNotificationShadeIntent.putExtra("reason", "dream"); } @@ -229,6 +230,8 @@ final class DreamController { } oldDream.releaseWakeLockIfNeeded(); + mActivityTaskManager.removeRootTasksWithActivityTypes(new int[] {ACTIVITY_TYPE_DREAM}); + mHandler.post(() -> mListener.onDreamStopped(oldDream.mToken)); } finally { Trace.traceEnd(Trace.TRACE_TAG_POWER); diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 76331fd6089c..76495b17c984 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -58,7 +58,6 @@ import android.Manifest; import android.accessibilityservice.AccessibilityService; import android.annotation.AnyThread; import android.annotation.BinderThread; -import android.annotation.ColorInt; import android.annotation.DrawableRes; import android.annotation.DurationMillisLong; import android.annotation.EnforcePermission; @@ -69,9 +68,6 @@ import android.annotation.UiThread; import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.ActivityManagerInternal; -import android.app.Notification; -import android.app.NotificationManager; -import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentProvider; @@ -94,7 +90,6 @@ import android.inputmethodservice.InputMethodService; import android.media.AudioManagerInternal; import android.net.Uri; import android.os.Binder; -import android.os.Bundle; import android.os.Debug; import android.os.Handler; import android.os.IBinder; @@ -170,8 +165,6 @@ import com.android.internal.inputmethod.SoftInputShowHideReason; import com.android.internal.inputmethod.StartInputFlags; import com.android.internal.inputmethod.StartInputReason; import com.android.internal.inputmethod.UnbindReason; -import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; -import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.os.TransferPipe; import com.android.internal.util.ArrayUtils; import com.android.internal.util.ConcurrentUtils; @@ -255,13 +248,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub private static final String HANDLER_THREAD_NAME = "android.imms"; /** - * A protected broadcast intent action for internal use for {@link PendingIntent} in - * the notification. - */ - private static final String ACTION_SHOW_INPUT_METHOD_PICKER = - "com.android.server.inputmethod.InputMethodManagerService.SHOW_INPUT_METHOD_PICKER"; - - /** * When set, {@link #startInputUncheckedLocked} will return * {@link InputBindResult#NO_EDITOR} instead of starting an IME connection * unless {@link StartInputFlags#IS_TEXT_EDITOR} is set. This behavior overrides @@ -334,13 +320,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub @GuardedBy("ImfLock.class") private int mDisplayIdToShowIme = INVALID_DISPLAY; - // Ongoing notification - private NotificationManager mNotificationManager; @Nullable private StatusBarManagerInternal mStatusBarManagerInternal; - private final Notification.Builder mImeSwitcherNotification; - private final PendingIntent mImeSwitchPendingIntent; private boolean mShowOngoingImeSwitcherForPhones; - private boolean mNotificationShown; @GuardedBy("ImfLock.class") private final HandwritingModeController mHwController; @GuardedBy("ImfLock.class") @@ -1253,17 +1234,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub return; } else if (Intent.ACTION_LOCALE_CHANGED.equals(action)) { onActionLocaleChanged(); - } else if (ACTION_SHOW_INPUT_METHOD_PICKER.equals(action)) { - // ACTION_SHOW_INPUT_METHOD_PICKER action is a protected-broadcast and it is - // guaranteed to be send only from the system, so that there is no need for extra - // security check such as - // {@link #canShowInputMethodPickerLocked(IInputMethodClient)}. - mHandler.obtainMessage( - MSG_SHOW_IM_SUBTYPE_PICKER, - // TODO(b/120076400): Design and implement IME switcher for heterogeneous - // navbar configuration. - InputMethodManager.SHOW_IM_PICKER_MODE_INCLUDE_AUXILIARY_SUBTYPES, - DEFAULT_DISPLAY).sendToTarget(); } else { Slog.w(TAG, "Unexpected intent " + intent); } @@ -1720,27 +1690,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub mSlotIme = mContext.getString(com.android.internal.R.string.status_bar_ime); - Bundle extras = new Bundle(); - extras.putBoolean(Notification.EXTRA_ALLOW_DURING_SETUP, true); - @ColorInt final int accentColor = mContext.getColor( - com.android.internal.R.color.system_notification_accent_color); - mImeSwitcherNotification = - new Notification.Builder(mContext, SystemNotificationChannels.VIRTUAL_KEYBOARD) - .setSmallIcon(com.android.internal.R.drawable.ic_notification_ime_default) - .setWhen(0) - .setOngoing(true) - .addExtras(extras) - .setCategory(Notification.CATEGORY_SYSTEM) - .setColor(accentColor); - - Intent intent = new Intent(ACTION_SHOW_INPUT_METHOD_PICKER) - .setPackage(mContext.getPackageName()); - mImeSwitchPendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, - PendingIntent.FLAG_IMMUTABLE); - mShowOngoingImeSwitcherForPhones = false; - mNotificationShown = false; final int userId = mActivityManagerInternal.getCurrentUserId(); mLastSwitchUserId = userId; @@ -1939,7 +1890,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub final int currentUserId = mSettings.getCurrentUserId(); mSettings.switchCurrentUser(currentUserId, !mUserManagerInternal.isUserUnlockingOrUnlocked(currentUserId)); - mNotificationManager = mContext.getSystemService(NotificationManager.class); mStatusBarManagerInternal = LocalServices.getService(StatusBarManagerInternal.class); hideStatusBarIconLocked(); @@ -1977,7 +1927,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub broadcastFilterForSystemUser.addAction(Intent.ACTION_USER_ADDED); broadcastFilterForSystemUser.addAction(Intent.ACTION_USER_REMOVED); broadcastFilterForSystemUser.addAction(Intent.ACTION_LOCALE_CHANGED); - broadcastFilterForSystemUser.addAction(ACTION_SHOW_INPUT_METHOD_PICKER); mContext.registerReceiver(new ImmsBroadcastReceiverForSystemUser(), broadcastFilterForSystemUser); @@ -3159,41 +3108,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub mStatusBarManagerInternal.setImeWindowStatus(mCurTokenDisplayId, getCurTokenLocked(), vis, backDisposition, needsToShowImeSwitcher); } - final InputMethodInfo imi = mMethodMap.get(getSelectedMethodIdLocked()); - if (imi != null && needsToShowImeSwitcher) { - // Used to load label - final CharSequence title = mRes.getText( - com.android.internal.R.string.select_input_method); - final int currentUserId = mSettings.getCurrentUserId(); - final Context userAwareContext = mContext.getUserId() == currentUserId - ? mContext - : mContext.createContextAsUser(UserHandle.of(currentUserId), 0 /* flags */); - final CharSequence summary = InputMethodUtils.getImeAndSubtypeDisplayName( - userAwareContext, imi, mCurrentSubtype); - mImeSwitcherNotification.setContentTitle(title) - .setContentText(summary) - .setContentIntent(mImeSwitchPendingIntent); - // TODO(b/120076400): Figure out what is the best behavior - if ((mNotificationManager != null) - && !mWindowManagerInternal.hasNavigationBar(DEFAULT_DISPLAY)) { - if (DEBUG) { - Slog.d(TAG, "--- show notification: label = " + summary); - } - mNotificationManager.notifyAsUser(null, - SystemMessage.NOTE_SELECT_INPUT_METHOD, - mImeSwitcherNotification.build(), UserHandle.ALL); - mNotificationShown = true; - } - } else { - if (mNotificationShown && mNotificationManager != null) { - if (DEBUG) { - Slog.d(TAG, "--- hide notification"); - } - mNotificationManager.cancelAsUser(null, - SystemMessage.NOTE_SELECT_INPUT_METHOD, UserHandle.ALL); - mNotificationShown = false; - } - } } finally { Binder.restoreCallingIdentity(ident); } diff --git a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java index c7ff8caf176b..ebf9237d61ea 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java @@ -179,16 +179,6 @@ final class InputMethodUtils { } } - static CharSequence getImeAndSubtypeDisplayName(Context context, InputMethodInfo imi, - InputMethodSubtype subtype) { - final CharSequence imiLabel = imi.loadLabel(context.getPackageManager()); - return subtype != null - ? TextUtils.concat(subtype.getDisplayName(context, - imi.getPackageName(), imi.getServiceInfo().applicationInfo), - (TextUtils.isEmpty(imiLabel) ? "" : " - " + imiLabel)) - : imiLabel; - } - /** * Returns true if a package name belongs to a UID. * diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java index e653f0466863..6f637b83a694 100644 --- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java +++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java @@ -1447,7 +1447,9 @@ public class GnssLocationProvider extends AbstractLocationProvider implements * @return the cell ID or -1 if invalid */ private static long getCidFromCellIdentity(CellIdentity id) { - if (id == null) return -1; + if (id == null) { + return -1; + } long cid = -1; switch(id.getType()) { case CellInfo.TYPE_GSM: cid = ((CellIdentityGsm) id).getCid(); break; @@ -1522,7 +1524,8 @@ public class GnssLocationProvider extends AbstractLocationProvider implements for (CellInfo ci : cil) { int status = ci.getCellConnectionStatus(); - if (status == CellInfo.CONNECTION_PRIMARY_SERVING + if (ci.isRegistered() + || status == CellInfo.CONNECTION_PRIMARY_SERVING || status == CellInfo.CONNECTION_SECONDARY_SERVING) { CellIdentity c = ci.getCellIdentity(); int t = getCellType(ci); diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java index 5f06ca945eae..77dbde13889b 100644 --- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java +++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java @@ -151,10 +151,7 @@ class MediaRouter2ServiceImpl { mContext.registerReceiver(mScreenOnOffReceiver, screenOnOffIntentFilter); } - //////////////////////////////////////////////////////////////// - //// Calls from MediaRouter2 - //// - Should not have @NonNull/@Nullable on any arguments - //////////////////////////////////////////////////////////////// + // Methods that implement MediaRouter2 operations. @NonNull public void enforceMediaContentControlPermission() { @@ -242,7 +239,7 @@ class MediaRouter2ServiceImpl { } } - public void registerRouter2(IMediaRouter2 router, String packageName) { + public void registerRouter2(@NonNull IMediaRouter2 router, @NonNull String packageName) { Objects.requireNonNull(router, "router must not be null"); if (TextUtils.isEmpty(packageName)) { throw new IllegalArgumentException("packageName must not be empty"); @@ -269,7 +266,7 @@ class MediaRouter2ServiceImpl { } } - public void unregisterRouter2(IMediaRouter2 router) { + public void unregisterRouter2(@NonNull IMediaRouter2 router) { Objects.requireNonNull(router, "router must not be null"); final long token = Binder.clearCallingIdentity(); @@ -282,8 +279,8 @@ class MediaRouter2ServiceImpl { } } - public void setDiscoveryRequestWithRouter2(IMediaRouter2 router, - RouteDiscoveryPreference preference) { + public void setDiscoveryRequestWithRouter2(@NonNull IMediaRouter2 router, + @NonNull RouteDiscoveryPreference preference) { Objects.requireNonNull(router, "router must not be null"); Objects.requireNonNull(preference, "preference must not be null"); @@ -302,8 +299,8 @@ class MediaRouter2ServiceImpl { } } - public void setRouteVolumeWithRouter2(IMediaRouter2 router, - MediaRoute2Info route, int volume) { + public void setRouteVolumeWithRouter2(@NonNull IMediaRouter2 router, + @NonNull MediaRoute2Info route, int volume) { Objects.requireNonNull(router, "router must not be null"); Objects.requireNonNull(route, "route must not be null"); @@ -317,9 +314,9 @@ class MediaRouter2ServiceImpl { } } - public void requestCreateSessionWithRouter2(IMediaRouter2 router, int requestId, - long managerRequestId, RoutingSessionInfo oldSession, - MediaRoute2Info route, Bundle sessionHints) { + public void requestCreateSessionWithRouter2(@NonNull IMediaRouter2 router, int requestId, + long managerRequestId, @NonNull RoutingSessionInfo oldSession, + @NonNull MediaRoute2Info route, Bundle sessionHints) { Objects.requireNonNull(router, "router must not be null"); Objects.requireNonNull(oldSession, "oldSession must not be null"); Objects.requireNonNull(route, "route must not be null"); @@ -335,8 +332,8 @@ class MediaRouter2ServiceImpl { } } - public void selectRouteWithRouter2(IMediaRouter2 router, String uniqueSessionId, - MediaRoute2Info route) { + public void selectRouteWithRouter2(@NonNull IMediaRouter2 router, + @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) { Objects.requireNonNull(router, "router must not be null"); Objects.requireNonNull(route, "route must not be null"); if (TextUtils.isEmpty(uniqueSessionId)) { @@ -353,8 +350,8 @@ class MediaRouter2ServiceImpl { } } - public void deselectRouteWithRouter2(IMediaRouter2 router, String uniqueSessionId, - MediaRoute2Info route) { + public void deselectRouteWithRouter2(@NonNull IMediaRouter2 router, + @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) { Objects.requireNonNull(router, "router must not be null"); Objects.requireNonNull(route, "route must not be null"); if (TextUtils.isEmpty(uniqueSessionId)) { @@ -371,8 +368,8 @@ class MediaRouter2ServiceImpl { } } - public void transferToRouteWithRouter2(IMediaRouter2 router, String uniqueSessionId, - MediaRoute2Info route) { + public void transferToRouteWithRouter2(@NonNull IMediaRouter2 router, + @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) { Objects.requireNonNull(router, "router must not be null"); Objects.requireNonNull(route, "route must not be null"); if (TextUtils.isEmpty(uniqueSessionId)) { @@ -389,8 +386,8 @@ class MediaRouter2ServiceImpl { } } - public void setSessionVolumeWithRouter2(IMediaRouter2 router, String uniqueSessionId, - int volume) { + public void setSessionVolumeWithRouter2(@NonNull IMediaRouter2 router, + @NonNull String uniqueSessionId, int volume) { Objects.requireNonNull(router, "router must not be null"); Objects.requireNonNull(uniqueSessionId, "uniqueSessionId must not be null"); @@ -404,7 +401,8 @@ class MediaRouter2ServiceImpl { } } - public void releaseSessionWithRouter2(IMediaRouter2 router, String uniqueSessionId) { + public void releaseSessionWithRouter2(@NonNull IMediaRouter2 router, + @NonNull String uniqueSessionId) { Objects.requireNonNull(router, "router must not be null"); if (TextUtils.isEmpty(uniqueSessionId)) { throw new IllegalArgumentException("uniqueSessionId must not be empty"); @@ -420,13 +418,10 @@ class MediaRouter2ServiceImpl { } } - //////////////////////////////////////////////////////////////// - //// Calls from MediaRouter2Manager - //// - Should not have @NonNull/@Nullable on any arguments - //////////////////////////////////////////////////////////////// + // Methods that implement MediaRouter2Manager operations. @NonNull - public List<RoutingSessionInfo> getRemoteSessions(IMediaRouter2Manager manager) { + public List<RoutingSessionInfo> getRemoteSessions(@NonNull IMediaRouter2Manager manager) { Objects.requireNonNull(manager, "manager must not be null"); final long token = Binder.clearCallingIdentity(); try { @@ -438,7 +433,8 @@ class MediaRouter2ServiceImpl { } } - public void registerManager(IMediaRouter2Manager manager, String packageName) { + public void registerManager(@NonNull IMediaRouter2Manager manager, + @NonNull String packageName) { Objects.requireNonNull(manager, "manager must not be null"); if (TextUtils.isEmpty(packageName)) { throw new IllegalArgumentException("packageName must not be empty"); @@ -458,7 +454,7 @@ class MediaRouter2ServiceImpl { } } - public void unregisterManager(IMediaRouter2Manager manager) { + public void unregisterManager(@NonNull IMediaRouter2Manager manager) { Objects.requireNonNull(manager, "manager must not be null"); final long token = Binder.clearCallingIdentity(); @@ -471,7 +467,7 @@ class MediaRouter2ServiceImpl { } } - public void startScan(IMediaRouter2Manager manager) { + public void startScan(@NonNull IMediaRouter2Manager manager) { Objects.requireNonNull(manager, "manager must not be null"); final long token = Binder.clearCallingIdentity(); try { @@ -483,7 +479,7 @@ class MediaRouter2ServiceImpl { } } - public void stopScan(IMediaRouter2Manager manager) { + public void stopScan(@NonNull IMediaRouter2Manager manager) { Objects.requireNonNull(manager, "manager must not be null"); final long token = Binder.clearCallingIdentity(); try { @@ -495,8 +491,8 @@ class MediaRouter2ServiceImpl { } } - public void setRouteVolumeWithManager(IMediaRouter2Manager manager, int requestId, - MediaRoute2Info route, int volume) { + public void setRouteVolumeWithManager(@NonNull IMediaRouter2Manager manager, int requestId, + @NonNull MediaRoute2Info route, int volume) { Objects.requireNonNull(manager, "manager must not be null"); Objects.requireNonNull(route, "route must not be null"); @@ -510,10 +506,11 @@ class MediaRouter2ServiceImpl { } } - public void requestCreateSessionWithManager(IMediaRouter2Manager manager, int requestId, - RoutingSessionInfo oldSession, MediaRoute2Info route) { + public void requestCreateSessionWithManager(@NonNull IMediaRouter2Manager manager, + int requestId, @NonNull RoutingSessionInfo oldSession, @NonNull MediaRoute2Info route) { Objects.requireNonNull(manager, "manager must not be null"); Objects.requireNonNull(oldSession, "oldSession must not be null"); + Objects.requireNonNull(route, "route must not be null"); final long token = Binder.clearCallingIdentity(); try { @@ -525,8 +522,8 @@ class MediaRouter2ServiceImpl { } } - public void selectRouteWithManager(IMediaRouter2Manager manager, int requestId, - String uniqueSessionId, MediaRoute2Info route) { + public void selectRouteWithManager(@NonNull IMediaRouter2Manager manager, int requestId, + @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) { Objects.requireNonNull(manager, "manager must not be null"); if (TextUtils.isEmpty(uniqueSessionId)) { throw new IllegalArgumentException("uniqueSessionId must not be empty"); @@ -543,8 +540,8 @@ class MediaRouter2ServiceImpl { } } - public void deselectRouteWithManager(IMediaRouter2Manager manager, int requestId, - String uniqueSessionId, MediaRoute2Info route) { + public void deselectRouteWithManager(@NonNull IMediaRouter2Manager manager, int requestId, + @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) { Objects.requireNonNull(manager, "manager must not be null"); if (TextUtils.isEmpty(uniqueSessionId)) { throw new IllegalArgumentException("uniqueSessionId must not be empty"); @@ -561,8 +558,8 @@ class MediaRouter2ServiceImpl { } } - public void transferToRouteWithManager(IMediaRouter2Manager manager, int requestId, - String uniqueSessionId, MediaRoute2Info route) { + public void transferToRouteWithManager(@NonNull IMediaRouter2Manager manager, int requestId, + @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) { Objects.requireNonNull(manager, "manager must not be null"); if (TextUtils.isEmpty(uniqueSessionId)) { throw new IllegalArgumentException("uniqueSessionId must not be empty"); @@ -579,8 +576,8 @@ class MediaRouter2ServiceImpl { } } - public void setSessionVolumeWithManager(IMediaRouter2Manager manager, int requestId, - String uniqueSessionId, int volume) { + public void setSessionVolumeWithManager(@NonNull IMediaRouter2Manager manager, int requestId, + @NonNull String uniqueSessionId, int volume) { Objects.requireNonNull(manager, "manager must not be null"); if (TextUtils.isEmpty(uniqueSessionId)) { throw new IllegalArgumentException("uniqueSessionId must not be empty"); @@ -596,8 +593,8 @@ class MediaRouter2ServiceImpl { } } - public void releaseSessionWithManager(IMediaRouter2Manager manager, int requestId, - String uniqueSessionId) { + public void releaseSessionWithManager(@NonNull IMediaRouter2Manager manager, int requestId, + @NonNull String uniqueSessionId) { Objects.requireNonNull(manager, "manager must not be null"); if (TextUtils.isEmpty(uniqueSessionId)) { throw new IllegalArgumentException("uniqueSessionId must not be empty"); @@ -681,11 +678,6 @@ class MediaRouter2ServiceImpl { return mUserManagerInternal.getProfileParentId(userId) == mCurrentActiveUserId; } - //////////////////////////////////////////////////////////////// - //// ***Locked methods related to MediaRouter2 - //// - Should have @NonNull/@Nullable on all arguments - //////////////////////////////////////////////////////////////// - @GuardedBy("mLock") private void registerRouter2Locked(@NonNull IMediaRouter2 router, int uid, int pid, @NonNull String packageName, int userId, boolean hasConfigureWifiDisplayPermission, @@ -960,11 +952,6 @@ class MediaRouter2ServiceImpl { DUMMY_REQUEST_ID, routerRecord, uniqueSessionId)); } - //////////////////////////////////////////////////////////// - //// ***Locked methods related to MediaRouter2Manager - //// - Should have @NonNull/@Nullable on all arguments - //////////////////////////////////////////////////////////// - private List<RoutingSessionInfo> getRemoteSessionsLocked( @NonNull IMediaRouter2Manager manager) { final IBinder binder = manager.asBinder(); @@ -1102,8 +1089,8 @@ class MediaRouter2ServiceImpl { } private void requestCreateSessionWithManagerLocked(int requestId, - @NonNull IMediaRouter2Manager manager, - @NonNull RoutingSessionInfo oldSession, @NonNull MediaRoute2Info route) { + @NonNull IMediaRouter2Manager manager, @NonNull RoutingSessionInfo oldSession, + @NonNull MediaRoute2Info route) { ManagerRecord managerRecord = mAllManagerRecords.get(manager.asBinder()); if (managerRecord == null) { return; @@ -1250,8 +1237,7 @@ class MediaRouter2ServiceImpl { } private void releaseSessionWithManagerLocked(int requestId, - @NonNull IMediaRouter2Manager manager, - @NonNull String uniqueSessionId) { + @NonNull IMediaRouter2Manager manager, @NonNull String uniqueSessionId) { final IBinder binder = manager.asBinder(); ManagerRecord managerRecord = mAllManagerRecords.get(binder); @@ -1274,11 +1260,6 @@ class MediaRouter2ServiceImpl { uniqueRequestId, routerRecord, uniqueSessionId)); } - //////////////////////////////////////////////////////////// - //// ***Locked methods used by both router2 and manager - //// - Should have @NonNull/@Nullable on all arguments - //////////////////////////////////////////////////////////// - @GuardedBy("mLock") private UserRecord getOrCreateUserRecordLocked(int userId) { UserRecord userRecord = mUserRecords.get(userId); diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 77fea09b5ecc..f459c0e5eeb4 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -61,6 +61,7 @@ import static android.content.pm.PackageManager.FEATURE_LEANBACK; import static android.content.pm.PackageManager.FEATURE_TELECOM; import static android.content.pm.PackageManager.FEATURE_TELEVISION; import static android.content.pm.PackageManager.MATCH_ALL; +import static android.content.pm.PackageManager.MATCH_ANY_USER; import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE; import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE; import static android.content.pm.PackageManager.PERMISSION_GRANTED; @@ -10700,10 +10701,18 @@ public class NotificationManagerService extends SystemService { private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>(); ArrayMap<Pair<ComponentName, Integer>, NotificationListenerFilter> mRequestedNotificationListeners = new ArrayMap<>(); + private final boolean mIsHeadlessSystemUserMode; public NotificationListeners(Context context, Object lock, UserProfiles userProfiles, IPackageManager pm) { + this(context, lock, userProfiles, pm, UserManager.isHeadlessSystemUserMode()); + } + + @VisibleForTesting + public NotificationListeners(Context context, Object lock, UserProfiles userProfiles, + IPackageManager pm, boolean isHeadlessSystemUserMode) { super(context, lock, userProfiles, pm); + this.mIsHeadlessSystemUserMode = isHeadlessSystemUserMode; } @Override @@ -10728,10 +10737,16 @@ public class NotificationManagerService extends SystemService { if (TextUtils.isEmpty(listeners[i])) { continue; } + int packageQueryFlags = MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE; + // In the headless system user mode, packages might not be installed for the + // system user. Match packages for any user since apps can be installed only for + // non-system users and would be considering uninstalled for the system user. + if (mIsHeadlessSystemUserMode) { + packageQueryFlags += MATCH_ANY_USER; + } ArraySet<ComponentName> approvedListeners = - this.queryPackageForServices(listeners[i], - MATCH_DIRECT_BOOT_AWARE - | MATCH_DIRECT_BOOT_UNAWARE, USER_SYSTEM); + this.queryPackageForServices(listeners[i], packageQueryFlags, + USER_SYSTEM); for (int k = 0; k < approvedListeners.size(); k++) { ComponentName cn = approvedListeners.valueAt(k); addDefaultComponentOrPackage(cn.flattenToString()); diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java index 8e672c3b32c5..17bb39c945bd 100644 --- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java +++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java @@ -166,6 +166,14 @@ final class OverlayManagerServiceImpl { CollectionUtils.addAll(updatedTargets, removeOverlaysForUser( (info) -> !userPackages.containsKey(info.packageName), newUserId)); + final ArraySet<String> overlaidByOthers = new ArraySet<>(); + for (AndroidPackage androidPackage : userPackages.values()) { + final String overlayTarget = androidPackage.getOverlayTarget(); + if (!TextUtils.isEmpty(overlayTarget)) { + overlaidByOthers.add(overlayTarget); + } + } + // Update the state of all installed packages containing overlays, and initialize new // overlays that are not currently in the settings. for (int i = 0, n = userPackages.size(); i < n; i++) { @@ -175,8 +183,10 @@ final class OverlayManagerServiceImpl { updatePackageOverlays(pkg, newUserId, 0 /* flags */)); // When a new user is switched to for the first time, package manager must be - // informed of the overlay paths for all packages installed in the user. - updatedTargets.add(new PackageAndUser(pkg.getPackageName(), newUserId)); + // informed of the overlay paths for all overlaid packages installed in the user. + if (overlaidByOthers.contains(pkg.getPackageName())) { + updatedTargets.add(new PackageAndUser(pkg.getPackageName(), newUserId)); + } } catch (OperationFailedException e) { Slog.e(TAG, "failed to initialize overlays of '" + pkg.getPackageName() + "' for user " + newUserId + "", e); diff --git a/services/core/java/com/android/server/pm/Computer.java b/services/core/java/com/android/server/pm/Computer.java index a4e295b4f7df..bf00a33d7d20 100644 --- a/services/core/java/com/android/server/pm/Computer.java +++ b/services/core/java/com/android/server/pm/Computer.java @@ -203,6 +203,12 @@ public interface Computer extends PackageDataSnapshot { boolean filterSharedLibPackage(@Nullable PackageStateInternal ps, int uid, int userId, long flags); boolean isCallerSameApp(String packageName, int uid); + /** + * Returns true if the package name and the uid represent the same app. + * + * @param resolveIsolatedUid if true, resolves an isolated uid into the real uid. + */ + boolean isCallerSameApp(String packageName, int uid, boolean resolveIsolatedUid); boolean isComponentVisibleToInstantApp(@Nullable ComponentName component); boolean isComponentVisibleToInstantApp(@Nullable ComponentName component, @PackageManager.ComponentType int type); diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java index 5d479d52d6cc..86b8272dbe00 100644 --- a/services/core/java/com/android/server/pm/ComputerEngine.java +++ b/services/core/java/com/android/server/pm/ComputerEngine.java @@ -2209,11 +2209,19 @@ public class ComputerEngine implements Computer { } public final boolean isCallerSameApp(String packageName, int uid) { + return isCallerSameApp(packageName, uid, false /* resolveIsolatedUid */); + } + + @Override + public final boolean isCallerSameApp(String packageName, int uid, boolean resolveIsolatedUid) { if (Process.isSdkSandboxUid(uid)) { return (packageName != null && packageName.equals(mService.getSdkSandboxPackageName())); } AndroidPackage pkg = mPackages.get(packageName); + if (resolveIsolatedUid && Process.isIsolated(uid)) { + uid = getIsolatedOwner(uid); + } return pkg != null && UserHandle.getAppId(uid) == pkg.getUid(); } diff --git a/services/core/java/com/android/server/pm/DexOptHelper.java b/services/core/java/com/android/server/pm/DexOptHelper.java index 3f04264714e4..c4f6836eba7b 100644 --- a/services/core/java/com/android/server/pm/DexOptHelper.java +++ b/services/core/java/com/android/server/pm/DexOptHelper.java @@ -18,6 +18,7 @@ package com.android.server.pm; import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; +import static com.android.server.LocalManagerRegistry.ManagerNotFoundException; import static com.android.server.pm.ApexManager.ActiveApexInfo; import static com.android.server.pm.InstructionSets.getAppDexInstructionSets; import static com.android.server.pm.PackageManagerService.DEBUG_DEXOPT; @@ -34,6 +35,7 @@ import static com.android.server.pm.PackageManagerServiceUtils.REMOVE_IF_NULL_PK import android.Manifest; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.app.ActivityManager; import android.app.AppGlobals; @@ -56,9 +58,16 @@ import android.util.Slog; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.logging.MetricsLogger; +import com.android.server.LocalManagerRegistry; +import com.android.server.art.ArtManagerLocal; +import com.android.server.art.model.ArtFlags; +import com.android.server.art.model.OptimizeParams; +import com.android.server.art.model.OptimizeResult; +import com.android.server.pm.PackageDexOptimizer.DexOptResult; import com.android.server.pm.dex.DexManager; import com.android.server.pm.dex.DexoptOptions; import com.android.server.pm.pkg.AndroidPackage; +import com.android.server.pm.pkg.PackageState; import com.android.server.pm.pkg.PackageStateInternal; import dalvik.system.DexFile; @@ -72,11 +81,15 @@ import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.List; +import java.util.Optional; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.function.Predicate; -final class DexOptHelper { +/** + * Helper class for dex optimization operations in PackageManagerService. + */ +public final class DexOptHelper { private static final long SEVEN_DAYS_IN_MILLISECONDS = 7 * 24 * 60 * 60 * 1000; private final PackageManagerService mPm; @@ -405,11 +418,12 @@ final class DexOptHelper { * {@link PackageDexOptimizer#DEX_OPT_CANCELLED} * {@link PackageDexOptimizer#DEX_OPT_FAILED} */ - @PackageDexOptimizer.DexOptResult + @DexOptResult /* package */ int performDexOptWithStatus(DexoptOptions options) { return performDexOptTraced(options); } + @DexOptResult private int performDexOptTraced(DexoptOptions options) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt"); try { @@ -421,7 +435,13 @@ final class DexOptHelper { // Run dexopt on a given package. Returns true if dexopt did not fail, i.e. // if the package can now be considered up to date for the given filter. + @DexOptResult private int performDexOptInternal(DexoptOptions options) { + Optional<Integer> artSrvRes = performDexOptWithArtService(options); + if (artSrvRes.isPresent()) { + return artSrvRes.get(); + } + AndroidPackage p; PackageSetting pkgSetting; synchronized (mPm.mLock) { @@ -446,8 +466,74 @@ final class DexOptHelper { } } - private int performDexOptInternalWithDependenciesLI(AndroidPackage p, - @NonNull PackageStateInternal pkgSetting, DexoptOptions options) { + /** + * Performs dexopt on the given package using ART Service. + * + * @return a {@link DexOptResult}, or empty if the request isn't supported so that it is + * necessary to fall back to the legacy code paths. + */ + private Optional<Integer> performDexOptWithArtService(DexoptOptions options) { + ArtManagerLocal artManager = getArtManagerLocal(); + if (artManager == null) { + return Optional.empty(); + } + + try (PackageManagerLocal.FilteredSnapshot snapshot = + getPackageManagerLocal().withFilteredSnapshot()) { + PackageState ops = snapshot.getPackageState(options.getPackageName()); + if (ops == null) { + return Optional.of(PackageDexOptimizer.DEX_OPT_FAILED); + } + AndroidPackage oap = ops.getAndroidPackage(); + if (oap == null) { + return Optional.of(PackageDexOptimizer.DEX_OPT_FAILED); + } + if (oap.isApex()) { + return Optional.of(PackageDexOptimizer.DEX_OPT_SKIPPED); + } + + // TODO(b/245301593): Delete the conditional when ART Service supports + // FLAG_SHOULD_INCLUDE_DEPENDENCIES and we can just set it unconditionally. + /*@OptimizeFlags*/ int extraFlags = ops.getUsesLibraries().isEmpty() + ? 0 + : ArtFlags.FLAG_SHOULD_INCLUDE_DEPENDENCIES; + + OptimizeParams params = options.convertToOptimizeParams(extraFlags); + if (params == null) { + return Optional.empty(); + } + + // TODO(b/251903639): Either remove controlDexOptBlocking, or don't ignore it here. + OptimizeResult result; + try { + result = artManager.optimizePackage(snapshot, options.getPackageName(), params); + } catch (UnsupportedOperationException e) { + reportArtManagerFallback(options.getPackageName(), e.toString()); + return Optional.empty(); + } + + // TODO(b/251903639): Move this to ArtManagerLocal.addOptimizePackageDoneCallback when + // it is implemented. + for (OptimizeResult.PackageOptimizeResult pkgRes : result.getPackageOptimizeResults()) { + PackageState ps = snapshot.getPackageState(pkgRes.getPackageName()); + AndroidPackage ap = ps != null ? ps.getAndroidPackage() : null; + if (ap != null) { + CompilerStats.PackageStats stats = mPm.getOrCreateCompilerPackageStats(ap); + for (OptimizeResult.DexContainerFileOptimizeResult dexRes : + pkgRes.getDexContainerFileOptimizeResults()) { + stats.setCompileTime( + dexRes.getDexContainerFile(), dexRes.getDex2oatWallTimeMillis()); + } + } + } + + return Optional.of(convertToDexOptResult(result)); + } + } + + @DexOptResult + private int performDexOptInternalWithDependenciesLI( + AndroidPackage p, @NonNull PackageStateInternal pkgSetting, DexoptOptions options) { // System server gets a special path. if (PLATFORM_PACKAGE_NAME.equals(p.getPackageName())) { return mPm.getDexManager().dexoptSystemServer(options); @@ -514,10 +600,20 @@ final class DexOptHelper { // Whoever is calling forceDexOpt wants a compiled package. // Don't use profiles since that may cause compilation to be skipped. - final int res = performDexOptInternalWithDependenciesLI(pkg, packageState, - new DexoptOptions(packageName, REASON_CMDLINE, - getDefaultCompilerFilter(), null /* splitName */, - DexoptOptions.DEXOPT_FORCE | DexoptOptions.DEXOPT_BOOT_COMPLETE)); + DexoptOptions options = new DexoptOptions(packageName, REASON_CMDLINE, + getDefaultCompilerFilter(), null /* splitName */, + DexoptOptions.DEXOPT_FORCE | DexoptOptions.DEXOPT_BOOT_COMPLETE); + + // performDexOptWithArtService ignores the snapshot and takes its own, so it can race with + // the package checks above, but at worst the effect is only a bit less friendly error + // below. + Optional<Integer> artSrvRes = performDexOptWithArtService(options); + int res; + if (artSrvRes.isPresent()) { + res = artSrvRes.get(); + } else { + res = performDexOptInternalWithDependenciesLI(pkg, packageState, options); + } Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); if (res != PackageDexOptimizer.DEX_OPT_PERFORMED) { @@ -800,4 +896,59 @@ final class DexOptHelper { } return false; } + + private @NonNull PackageManagerLocal getPackageManagerLocal() { + try { + return LocalManagerRegistry.getManagerOrThrow(PackageManagerLocal.class); + } catch (ManagerNotFoundException e) { + throw new RuntimeException(e); + } + } + + /** + * Called whenever we need to fall back from ART Service to the legacy dexopt code. + */ + public static void reportArtManagerFallback(String packageName, String reason) { + // STOPSHIP(b/251903639): Minimize these calls to avoid platform getting shipped with code + // paths that will always bypass ART Service. + Slog.i(TAG, "Falling back to old PackageManager dexopt for " + packageName + ": " + reason); + } + + /** + * Returns {@link ArtManagerLocal} if one is found and should be used for package optimization. + */ + private @Nullable ArtManagerLocal getArtManagerLocal() { + if (!"true".equals(SystemProperties.get("dalvik.vm.useartservice", ""))) { + return null; + } + try { + return LocalManagerRegistry.getManagerOrThrow(ArtManagerLocal.class); + } catch (ManagerNotFoundException e) { + throw new RuntimeException(e); + } + } + + /** + * Converts an ART Service {@link OptimizeResult} to {@link DexOptResult}. + * + * For interfacing {@link ArtManagerLocal} with legacy dex optimization code in PackageManager. + */ + @DexOptResult + private static int convertToDexOptResult(OptimizeResult result) { + /*@OptimizeStatus*/ int status = result.getFinalStatus(); + switch (status) { + case OptimizeResult.OPTIMIZE_SKIPPED: + return PackageDexOptimizer.DEX_OPT_SKIPPED; + case OptimizeResult.OPTIMIZE_FAILED: + return PackageDexOptimizer.DEX_OPT_FAILED; + case OptimizeResult.OPTIMIZE_PERFORMED: + return PackageDexOptimizer.DEX_OPT_PERFORMED; + case OptimizeResult.OPTIMIZE_CANCELLED: + return PackageDexOptimizer.DEX_OPT_CANCELLED; + default: + throw new IllegalArgumentException("OptimizeResult for " + + result.getPackageOptimizeResults().get(0).getPackageName() + + " has unsupported status " + status); + } + } } diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java index d25bca76245b..2a2410fd1767 100644 --- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java +++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java @@ -652,12 +652,6 @@ public class PackageDexOptimizer { @DexOptResult private int dexOptSecondaryDexPathLI(ApplicationInfo info, String path, PackageDexUsage.DexUseInfo dexUseInfo, DexoptOptions options) { - if (options.isDexoptOnlySharedDex() && !dexUseInfo.isUsedByOtherApps()) { - // We are asked to optimize only the dex files used by other apps and this is not - // on of them: skip it. - return DEX_OPT_SKIPPED; - } - String compilerFilter = getRealCompilerFilter(info, options.getCompilerFilter(), dexUseInfo.isUsedByOtherApps()); // Get the dexopt flags after getRealCompilerFilter to make sure we get the correct flags. diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 8fed153825db..6e54d0bbd656 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -5242,25 +5242,30 @@ public class PackageManagerService implements PackageSender, TestUtilityService Map<String, String> classLoaderContextMap, String loaderIsa) { int callingUid = Binder.getCallingUid(); - if (PackageManagerService.PLATFORM_PACKAGE_NAME.equals(loadingPackageName) - && callingUid != Process.SYSTEM_UID) { + + // TODO(b/254043366): System server should not report its own dex load because there's + // nothing ART can do with it. + + Computer snapshot = snapshot(); + + // System server should be able to report dex load on behalf of other apps. E.g., it + // could potentially resend the notifications in order to migrate the existing dex load + // info to ART Service. + if (!PackageManagerServiceUtils.isSystemOrRoot() + && !snapshot.isCallerSameApp( + loadingPackageName, callingUid, true /* resolveIsolatedUid */)) { Slog.w(PackageManagerService.TAG, - "Non System Server process reporting dex loads as system server. uid=" - + callingUid); - // Do not record dex loads from processes pretending to be system server. - // Only the system server should be assigned the package "android", so reject calls - // that don't satisfy the constraint. - // - // notifyDexLoad is a PM API callable from the app process. So in theory, apps could - // craft calls to this API and pretend to be system server. Doing so poses no - // particular danger for dex load reporting or later dexopt, however it is a - // sensible check to do in order to verify the expectations. + TextUtils.formatSimple( + "Invalid dex load report. loadingPackageName=%s, uid=%d", + loadingPackageName, callingUid)); return; } + // TODO(b/254043366): Call `ArtManagerLocal.notifyDexLoad`. + int userId = UserHandle.getCallingUserId(); - ApplicationInfo ai = snapshot().getApplicationInfo(loadingPackageName, /*flags*/ 0, - userId); + ApplicationInfo ai = + snapshot.getApplicationInfo(loadingPackageName, /*flags*/ 0, userId); if (ai == null) { Slog.w(PackageManagerService.TAG, "Loading a package that does not exist for the calling user. package=" + loadingPackageName + ", user=" + userId); diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 60f247843bb7..21191919c8e0 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -2633,6 +2633,9 @@ public class UserManagerService extends IUserManager.Stub { /** @return a specific user restriction that's in effect currently. */ @Override public boolean hasUserRestriction(String restrictionKey, @UserIdInt int userId) { + if (!userExists(userId)) { + return false; + } checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "hasUserRestriction"); return mLocalService.hasUserRestriction(restrictionKey, userId); } @@ -5516,6 +5519,13 @@ public class UserManagerService extends IUserManager.Stub { private void removeUserState(final @UserIdInt int userId) { Slog.i(LOG_TAG, "Removing user state of user " + userId); + + // Cleanup lock settings. This must happen before destroyUserKey(), since the user's DE + // storage must still be accessible for the lock settings state to be properly cleaned up. + mLockPatternUtils.removeUser(userId); + + // Evict and destroy the user's CE and DE encryption keys. At this point, the user's CE and + // DE storage is made inaccessible, except to delete its contents. try { mContext.getSystemService(StorageManager.class).destroyUserKey(userId); } catch (IllegalStateException e) { @@ -5523,9 +5533,6 @@ public class UserManagerService extends IUserManager.Stub { Slog.i(LOG_TAG, "Destroying key for user " + userId + " failed, continuing anyway", e); } - // Cleanup lock settings - mLockPatternUtils.removeUser(userId); - // Cleanup package manager settings mPm.cleanUpUser(this, userId); diff --git a/services/core/java/com/android/server/pm/dex/DexoptOptions.java b/services/core/java/com/android/server/pm/dex/DexoptOptions.java index ea233161b4af..f5557c417f1b 100644 --- a/services/core/java/com/android/server/pm/dex/DexoptOptions.java +++ b/services/core/java/com/android/server/pm/dex/DexoptOptions.java @@ -18,6 +18,16 @@ package com.android.server.pm.dex; import static com.android.server.pm.PackageManagerServiceCompilerMapping.getCompilerFilterForReason; +import android.annotation.Nullable; + +import com.android.server.art.ReasonMapping; +import com.android.server.art.model.ArtFlags; +import com.android.server.art.model.OptimizeParams; +import com.android.server.pm.DexOptHelper; +import com.android.server.pm.PackageManagerService; + +import dalvik.system.DexFile; + /** * Options used for dexopt invocations. */ @@ -40,10 +50,6 @@ public final class DexoptOptions { // will only consider the primary apk. public static final int DEXOPT_ONLY_SECONDARY_DEX = 1 << 3; - // When set, dexopt will optimize only dex files that are used by other apps. - // Currently, this flag is ignored for primary apks. - public static final int DEXOPT_ONLY_SHARED_DEX = 1 << 4; - // When set, dexopt will attempt to scale down the optimizations previously applied in order // save disk space. public static final int DEXOPT_DOWNGRADE = 1 << 5; @@ -105,7 +111,6 @@ public final class DexoptOptions { DEXOPT_FORCE | DEXOPT_BOOT_COMPLETE | DEXOPT_ONLY_SECONDARY_DEX | - DEXOPT_ONLY_SHARED_DEX | DEXOPT_DOWNGRADE | DEXOPT_AS_SHARED_LIBRARY | DEXOPT_IDLE_BACKGROUND_JOB | @@ -146,10 +151,6 @@ public final class DexoptOptions { return (mFlags & DEXOPT_ONLY_SECONDARY_DEX) != 0; } - public boolean isDexoptOnlySharedDex() { - return (mFlags & DEXOPT_ONLY_SHARED_DEX) != 0; - } - public boolean isDowngrade() { return (mFlags & DEXOPT_DOWNGRADE) != 0; } @@ -198,4 +199,133 @@ public final class DexoptOptions { mSplitName, mFlags); } + + /** + * Returns an {@link OptimizeParams} instance corresponding to this object, for use with + * {@link com.android.server.art.ArtManagerLocal}. + * + * @param extraFlags extra {@link ArtFlags#OptimizeFlags} to set in the returned + * {@code OptimizeParams} beyond those converted from this object + * @return null if the settings cannot be accurately represented, and hence the old + * PackageManager/installd code paths need to be used. + */ + public @Nullable OptimizeParams convertToOptimizeParams(/*@OptimizeFlags*/ int extraFlags) { + if (mSplitName != null) { + DexOptHelper.reportArtManagerFallback( + mPackageName, "Request to optimize only split " + mSplitName); + return null; + } + + /*@OptimizeFlags*/ int flags = extraFlags; + if ((mFlags & DEXOPT_CHECK_FOR_PROFILES_UPDATES) == 0 + && DexFile.isProfileGuidedCompilerFilter(mCompilerFilter)) { + // ART Service doesn't support bypassing this, so not setting this flag is not + // supported. + DexOptHelper.reportArtManagerFallback(mPackageName, + "DEXOPT_CHECK_FOR_PROFILES_UPDATES not set with profile compiler filter"); + return null; + } + if ((mFlags & DEXOPT_FORCE) != 0) { + flags |= ArtFlags.FLAG_FORCE; + } + if ((mFlags & DEXOPT_ONLY_SECONDARY_DEX) != 0) { + flags |= ArtFlags.FLAG_FOR_SECONDARY_DEX; + } else { + flags |= ArtFlags.FLAG_FOR_PRIMARY_DEX; + } + if ((mFlags & DEXOPT_DOWNGRADE) != 0) { + flags |= ArtFlags.FLAG_SHOULD_DOWNGRADE; + } + if ((mFlags & DEXOPT_INSTALL_WITH_DEX_METADATA_FILE) == 0) { + // ART Service cannot be instructed to ignore a DM file if present, so not setting this + // flag is not supported. + DexOptHelper.reportArtManagerFallback( + mPackageName, "DEXOPT_INSTALL_WITH_DEX_METADATA_FILE not set"); + return null; + } + + /*@PriorityClassApi*/ int priority; + // Replicates logic in RunDex2Oat::PrepareCompilerRuntimeAndPerfConfigFlags in installd. + if ((mFlags & DEXOPT_BOOT_COMPLETE) != 0) { + if ((mFlags & DEXOPT_FOR_RESTORE) != 0) { + priority = ArtFlags.PRIORITY_INTERACTIVE_FAST; + } else { + // TODO(b/251903639): Repurpose DEXOPT_IDLE_BACKGROUND_JOB to choose new + // dalvik.vm.background-dex2oat-* properties. + priority = ArtFlags.PRIORITY_INTERACTIVE; + } + } else { + priority = ArtFlags.PRIORITY_BOOT; + } + + // The following flags in mFlags are ignored: + // + // - DEXOPT_AS_SHARED_LIBRARY: It's implicit with ART Service since it always looks at + // <uses-library> rather than actual dependencies. + // + // We don't require it to be set either. It's safe when switching between old and new + // code paths since the only effect is that some packages may be unnecessarily compiled + // without user profiles. + // + // - DEXOPT_IDLE_BACKGROUND_JOB: Its only effect is to allow the debug variant dex2oatd to + // be used, but ART Service never uses that (cf. Artd::GetDex2Oat in artd.cc). + + String reason; + switch (mCompilationReason) { + case PackageManagerService.REASON_FIRST_BOOT: + reason = ReasonMapping.REASON_FIRST_BOOT; + break; + case PackageManagerService.REASON_BOOT_AFTER_OTA: + reason = ReasonMapping.REASON_BOOT_AFTER_OTA; + break; + case PackageManagerService.REASON_POST_BOOT: + // This reason will go away with the legacy dexopt code. + DexOptHelper.reportArtManagerFallback( + mPackageName, "Unsupported compilation reason REASON_POST_BOOT"); + return null; + case PackageManagerService.REASON_INSTALL: + reason = ReasonMapping.REASON_INSTALL; + break; + case PackageManagerService.REASON_INSTALL_FAST: + reason = ReasonMapping.REASON_INSTALL_FAST; + break; + case PackageManagerService.REASON_INSTALL_BULK: + reason = ReasonMapping.REASON_INSTALL_BULK; + break; + case PackageManagerService.REASON_INSTALL_BULK_SECONDARY: + reason = ReasonMapping.REASON_INSTALL_BULK_SECONDARY; + break; + case PackageManagerService.REASON_INSTALL_BULK_DOWNGRADED: + reason = ReasonMapping.REASON_INSTALL_BULK_DOWNGRADED; + break; + case PackageManagerService.REASON_INSTALL_BULK_SECONDARY_DOWNGRADED: + reason = ReasonMapping.REASON_INSTALL_BULK_SECONDARY_DOWNGRADED; + break; + case PackageManagerService.REASON_BACKGROUND_DEXOPT: + reason = ReasonMapping.REASON_BG_DEXOPT; + break; + case PackageManagerService.REASON_INACTIVE_PACKAGE_DOWNGRADE: + reason = ReasonMapping.REASON_INACTIVE; + break; + case PackageManagerService.REASON_CMDLINE: + reason = ReasonMapping.REASON_CMDLINE; + break; + case PackageManagerService.REASON_SHARED: + case PackageManagerService.REASON_AB_OTA: + // REASON_SHARED shouldn't go into this code path - it's only used at lower levels + // in PackageDexOptimizer. + // TODO(b/251921228): OTA isn't supported, so REASON_AB_OTA shouldn't come this way + // either. + throw new UnsupportedOperationException( + "ART Service unsupported compilation reason " + mCompilationReason); + default: + throw new IllegalArgumentException( + "Invalid compilation reason " + mCompilationReason); + } + + return new OptimizeParams.Builder(reason, flags) + .setCompilerFilter(mCompilerFilter) + .setPriorityClass(priority) + .build(); + } } diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index 5abc875a697f..4784723b7735 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -6768,6 +6768,11 @@ public final class PowerManagerService extends SystemService public void nap(long eventTime, boolean allowWake) { napInternal(eventTime, Process.SYSTEM_UID, allowWake); } + + @Override + public boolean isAmbientDisplaySuppressed() { + return mAmbientDisplaySuppressionController.isSuppressed(); + } } /** diff --git a/services/core/java/com/android/server/vibrator/VibrationStepConductor.java b/services/core/java/com/android/server/vibrator/VibrationStepConductor.java index 8ac4fd4860bb..141be702ee13 100644 --- a/services/core/java/com/android/server/vibrator/VibrationStepConductor.java +++ b/services/core/java/com/android/server/vibrator/VibrationStepConductor.java @@ -65,6 +65,9 @@ final class VibrationStepConductor implements IBinder.DeathRecipient { public final DeviceVibrationEffectAdapter deviceEffectAdapter; public final VibrationThread.VibratorManagerHooks vibratorManagerHooks; + // Not guarded by lock because they're not modified by this conductor, it's used here only to + // check immutable attributes. The status and other mutable states are changed by the service or + // by the vibrator steps. private final Vibration mVibration; private final SparseArray<VibratorController> mVibrators = new SparseArray<>(); @@ -412,6 +415,16 @@ final class VibrationStepConductor implements IBinder.DeathRecipient { } } + /** Returns true if a cancellation signal was sent via {@link #notifyCancelled}. */ + public boolean wasNotifiedToCancel() { + if (Build.IS_DEBUGGABLE) { + expectIsVibrationThread(false); + } + synchronized (mLock) { + return mSignalCancel != null; + } + } + @GuardedBy("mLock") private boolean hasPendingNotifySignalLocked() { if (Build.IS_DEBUGGABLE) { diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java index 8514e272e250..8613b5027d57 100644 --- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java +++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java @@ -864,8 +864,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { } Vibration currentVibration = mCurrentVibration.getVibration(); - if (currentVibration.hasEnded()) { - // Current vibration is finishing up, it should not block incoming vibrations. + if (currentVibration.hasEnded() || mCurrentVibration.wasNotifiedToCancel()) { + // Current vibration has ended or is cancelling, should not block incoming vibrations. return null; } diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java index 214a2c197a5c..3c457e1cc277 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java @@ -2578,6 +2578,9 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { // activity lifecycle transaction to make sure the override pending app // transition will be applied immediately. targetActivity.applyOptionsAnimation(); + if (activityOptions != null && activityOptions.getLaunchCookie() != null) { + targetActivity.mLaunchCookie = activityOptions.getLaunchCookie(); + } } finally { mActivityMetricsLogger.notifyActivityLaunched(launchingState, START_TASK_TO_FRONT, false /* newActivityCreated */, diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java index e9774479233d..30399ed15f7e 100644 --- a/services/core/java/com/android/server/wm/BackNavigationController.java +++ b/services/core/java/com/android/server/wm/BackNavigationController.java @@ -588,6 +588,7 @@ class BackNavigationController { ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "Setting Activity.mLauncherTaskBehind to true. Activity=%s", activity); + activity.mTaskSupervisor.mStoppingActivities.remove(activity); activity.getDisplayContent().ensureActivitiesVisible(null /* starting */, 0 /* configChanges */, false /* preserveWindows */, true); } diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java index 32feb6c98b24..c206a15503de 100644 --- a/services/core/java/com/android/server/wm/WindowManagerInternal.java +++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java @@ -613,15 +613,6 @@ public abstract class WindowManagerInternal { @NonNull IBinder imeTargetWindowToken); /** - * Returns the presence of a software navigation bar on the specified display. - * - * @param displayId the id of display to check if there is a software navigation bar. - * @return {@code true} if there is a software navigation. {@code false} otherwise, including - * the case when the specified display does not exist. - */ - public abstract boolean hasNavigationBar(int displayId); - - /** * Returns true when the hardware keyboard is available. */ public abstract boolean isHardKeyboardAvailable(); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index c17af3093e51..c9d3dac104de 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -7917,11 +7917,6 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public boolean hasNavigationBar(int displayId) { - return WindowManagerService.this.hasNavigationBar(displayId); - } - - @Override public boolean isHardKeyboardAvailable() { synchronized (mGlobalLock) { return mHardKeyboardAvailable; @@ -8703,11 +8698,12 @@ public class WindowManagerService extends IWindowManager.Stub h.ownerPid = callingPid; if (region == null) { - h.replaceTouchableRegionWithCrop = true; + h.replaceTouchableRegionWithCrop(null); } else { h.touchableRegion.set(region); + h.replaceTouchableRegionWithCrop = false; + h.setTouchableRegionCrop(surface); } - h.setTouchableRegionCrop(null /* use the input surface's bounds */); final SurfaceControl.Transaction t = mTransactionFactory.get(); t.setInputWindowInfo(surface, h); diff --git a/services/core/jni/gnss/AGnssRil.cpp b/services/core/jni/gnss/AGnssRil.cpp index 424ffd463713..34e4976dcca0 100644 --- a/services/core/jni/gnss/AGnssRil.cpp +++ b/services/core/jni/gnss/AGnssRil.cpp @@ -55,13 +55,13 @@ jboolean AGnssRil::setRefLocation(jint type, jint mcc, jint mnc, jint lac, jlong case IAGnssRil::AGnssRefLocationType::UMTS_CELLID: case IAGnssRil::AGnssRefLocationType::LTE_CELLID: case IAGnssRil::AGnssRefLocationType::NR_CELLID: - location.cellID.mcc = mcc; - location.cellID.mnc = mnc; - location.cellID.lac = lac; - location.cellID.cid = cid; - location.cellID.tac = tac; - location.cellID.pcid = pcid; - location.cellID.arfcn = arfcn; + location.cellID.mcc = static_cast<int>(mcc); + location.cellID.mnc = static_cast<int>(mnc); + location.cellID.lac = static_cast<int>(lac); + location.cellID.cid = static_cast<long>(cid); + location.cellID.tac = static_cast<int>(tac); + location.cellID.pcid = static_cast<int>(pcid); + location.cellID.arfcn = static_cast<int>(arfcn); break; default: ALOGE("Unknown cellid (%s:%d).", __FUNCTION__, __LINE__); @@ -106,20 +106,24 @@ jboolean AGnssRil_V1_0::setSetId(jint type, const jstring& setid_string) { return checkHidlReturn(result, "IAGnssRil_V1_0 setSetId() failed."); } -jboolean AGnssRil_V1_0::setRefLocation(jint type, jint mcc, jint mnc, jint lac, jlong cid, jint, - jint, jint) { +jboolean AGnssRil_V1_0::setRefLocation(jint type, jint mcc, jint mnc, jint lac, jlong cid, jint tac, + jint pcid, jint) { IAGnssRil_V1_0::AGnssRefLocation location; - switch (static_cast<IAGnssRil_V1_0::AGnssRefLocationType>(type)) { + location.type = static_cast<IAGnssRil_V1_0::AGnssRefLocationType>(type); + + switch (location.type) { case IAGnssRil_V1_0::AGnssRefLocationType::GSM_CELLID: case IAGnssRil_V1_0::AGnssRefLocationType::UMTS_CELLID: - location.type = static_cast<IAGnssRil_V1_0::AGnssRefLocationType>(type); - location.cellID.mcc = mcc; - location.cellID.mnc = mnc; - location.cellID.lac = lac; - location.cellID.cid = cid; + case IAGnssRil_V1_0::AGnssRefLocationType::LTE_CELLID: + location.cellID.mcc = static_cast<uint16_t>(mcc); + location.cellID.mnc = static_cast<uint16_t>(mnc); + location.cellID.lac = static_cast<uint16_t>(lac); + location.cellID.cid = static_cast<uint32_t>(cid); + location.cellID.tac = static_cast<uint16_t>(tac); + location.cellID.pcid = static_cast<uint16_t>(pcid); break; default: - ALOGE("Neither a GSM nor a UMTS cellid (%s:%d).", __FUNCTION__, __LINE__); + ALOGE("Unknown cellid (%s:%d).", __FUNCTION__, __LINE__); return JNI_FALSE; break; } diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerServiceImpl.java b/services/credentials/java/com/android/server/credentials/CredentialManagerServiceImpl.java index f45f62682f1c..aa19241e77dd 100644 --- a/services/credentials/java/com/android/server/credentials/CredentialManagerServiceImpl.java +++ b/services/credentials/java/com/android/server/credentials/CredentialManagerServiceImpl.java @@ -17,6 +17,11 @@ package com.android.server.credentials; import android.annotation.NonNull; +import android.app.AppGlobals; +import android.content.ComponentName; +import android.content.pm.PackageManager; +import android.content.pm.ServiceInfo; +import android.os.RemoteException; import android.util.Log; import com.android.server.infra.AbstractPerUserSystemService; @@ -24,7 +29,7 @@ import com.android.server.infra.AbstractPerUserSystemService; /** * Per-user implementation of {@link CredentialManagerService} */ -public class CredentialManagerServiceImpl extends +public final class CredentialManagerServiceImpl extends AbstractPerUserSystemService<CredentialManagerServiceImpl, CredentialManagerService> { private static final String TAG = "CredManSysServiceImpl"; @@ -34,6 +39,20 @@ public class CredentialManagerServiceImpl extends super(master, lock, userId); } + @Override // from PerUserSystemService + protected ServiceInfo newServiceInfoLocked(@NonNull ComponentName serviceComponent) + throws PackageManager.NameNotFoundException { + ServiceInfo si; + try { + si = AppGlobals.getPackageManager().getServiceInfo(serviceComponent, + PackageManager.GET_META_DATA, mUserId); + } catch (RemoteException e) { + throw new PackageManager.NameNotFoundException( + "Could not get service for " + serviceComponent); + } + return si; + } + /** * Unimplemented getCredentials */ diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java index 90b1f4ecdcb3..b7e66f23a706 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java @@ -39,6 +39,8 @@ import android.app.AppOpsManager; import android.app.BroadcastOptions; import android.content.Intent; import android.content.IntentFilter; +import android.media.AudioManager; +import android.os.Bundle; import android.os.HandlerThread; import android.os.UserHandle; import android.provider.Settings; @@ -289,20 +291,30 @@ public class BroadcastQueueModernImplTest { final BroadcastProcessQueue queue = new BroadcastProcessQueue(mConstants, PACKAGE_GREEN, getUidForPackage(PACKAGE_GREEN)); + // enqueue a bg-priority broadcast then a fg-priority one + final Intent timezone = new Intent(Intent.ACTION_TIMEZONE_CHANGED); + final BroadcastRecord timezoneRecord = makeBroadcastRecord(timezone); + queue.enqueueOrReplaceBroadcast(timezoneRecord, 0, 0); + final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED); airplane.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); final BroadcastRecord airplaneRecord = makeBroadcastRecord(airplane); queue.enqueueOrReplaceBroadcast(airplaneRecord, 0, 0); + // verify that: + // (a) the queue is immediately runnable by existence of a fg-priority broadcast + // (b) the next one up is the fg-priority broadcast despite its later enqueue time queue.setProcessCached(false); assertTrue(queue.isRunnable()); assertEquals(airplaneRecord.enqueueTime, queue.getRunnableAt()); assertEquals(ProcessList.SCHED_GROUP_DEFAULT, queue.getPreferredSchedulingGroupLocked()); + assertEquals(queue.peekNextBroadcastRecord(), airplaneRecord); queue.setProcessCached(true); assertTrue(queue.isRunnable()); assertEquals(airplaneRecord.enqueueTime, queue.getRunnableAt()); assertEquals(ProcessList.SCHED_GROUP_DEFAULT, queue.getPreferredSchedulingGroupLocked()); + assertEquals(queue.peekNextBroadcastRecord(), airplaneRecord); } /** @@ -386,4 +398,86 @@ public class BroadcastQueueModernImplTest { assertEquals(Intent.ACTION_SCREEN_OFF, queue.getActive().intent.getAction()); assertTrue(queue.isEmpty()); } + + /** + * Verify that sending a broadcast with DELIVERY_GROUP_POLICY_MOST_RECENT works as expected. + */ + @Test + public void testDeliveryGroupPolicy_mostRecent() { + final Intent timeTick = new Intent(Intent.ACTION_TIME_TICK); + final BroadcastOptions optionsTimeTick = BroadcastOptions.makeBasic(); + optionsTimeTick.setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT); + + final Intent musicVolumeChanged = new Intent(AudioManager.VOLUME_CHANGED_ACTION); + musicVolumeChanged.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, + AudioManager.STREAM_MUSIC); + final BroadcastOptions optionsMusicVolumeChanged = BroadcastOptions.makeBasic(); + optionsMusicVolumeChanged.setDeliveryGroupPolicy( + BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT); + optionsMusicVolumeChanged.setDeliveryGroupKey("audio", + String.valueOf(AudioManager.STREAM_MUSIC)); + + final Intent alarmVolumeChanged = new Intent(AudioManager.VOLUME_CHANGED_ACTION); + alarmVolumeChanged.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, + AudioManager.STREAM_ALARM); + final BroadcastOptions optionsAlarmVolumeChanged = BroadcastOptions.makeBasic(); + optionsAlarmVolumeChanged.setDeliveryGroupPolicy( + BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT); + optionsAlarmVolumeChanged.setDeliveryGroupKey("audio", + String.valueOf(AudioManager.STREAM_ALARM)); + + // Halt all processing so that we get a consistent view + mHandlerThread.getLooper().getQueue().postSyncBarrier(); + + mImpl.enqueueBroadcastLocked(makeBroadcastRecord(timeTick, optionsTimeTick)); + mImpl.enqueueBroadcastLocked(makeBroadcastRecord(musicVolumeChanged, + optionsMusicVolumeChanged)); + mImpl.enqueueBroadcastLocked(makeBroadcastRecord(alarmVolumeChanged, + optionsAlarmVolumeChanged)); + mImpl.enqueueBroadcastLocked(makeBroadcastRecord(musicVolumeChanged, + optionsMusicVolumeChanged)); + + final BroadcastProcessQueue queue = mImpl.getProcessQueue(PACKAGE_GREEN, + getUidForPackage(PACKAGE_GREEN)); + // Verify that the older musicVolumeChanged has been removed. + verifyPendingRecords(queue, + List.of(timeTick, alarmVolumeChanged, musicVolumeChanged)); + + mImpl.enqueueBroadcastLocked(makeBroadcastRecord(timeTick, optionsTimeTick)); + mImpl.enqueueBroadcastLocked(makeBroadcastRecord(alarmVolumeChanged, + optionsAlarmVolumeChanged)); + mImpl.enqueueBroadcastLocked(makeBroadcastRecord(musicVolumeChanged, + optionsMusicVolumeChanged)); + mImpl.enqueueBroadcastLocked(makeBroadcastRecord(alarmVolumeChanged, + optionsAlarmVolumeChanged)); + // Verify that the older alarmVolumeChanged has been removed. + verifyPendingRecords(queue, + List.of(timeTick, musicVolumeChanged, alarmVolumeChanged)); + + mImpl.enqueueBroadcastLocked(makeBroadcastRecord(timeTick, optionsTimeTick)); + mImpl.enqueueBroadcastLocked(makeBroadcastRecord(musicVolumeChanged, + optionsMusicVolumeChanged)); + mImpl.enqueueBroadcastLocked(makeBroadcastRecord(alarmVolumeChanged, + optionsAlarmVolumeChanged)); + mImpl.enqueueBroadcastLocked(makeBroadcastRecord(timeTick, optionsTimeTick)); + // Verify that the older timeTick has been removed. + verifyPendingRecords(queue, + List.of(musicVolumeChanged, alarmVolumeChanged, timeTick)); + } + + private void verifyPendingRecords(BroadcastProcessQueue queue, + List<Intent> intents) { + for (int i = 0; i < intents.size(); i++) { + queue.makeActiveNextPending(); + final Intent actualIntent = queue.getActive().intent; + final Intent expectedIntent = intents.get(i); + final String errMsg = "actual=" + actualIntent + ", expected=" + expectedIntent + + ", actual_extras=" + actualIntent.getExtras() + + ", expected_extras=" + expectedIntent.getExtras(); + assertTrue(errMsg, actualIntent.filterEquals(expectedIntent)); + assertTrue(errMsg, Bundle.kindofEquals( + actualIntent.getExtras(), expectedIntent.getExtras())); + } + assertTrue(queue.isEmpty()); + } } diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java index c12544897941..d9a26c68f3ed 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java @@ -549,12 +549,6 @@ public class BroadcastQueueTest { receivers, false, null, null, userId); } - private BroadcastRecord makeOrderedBroadcastRecord(Intent intent, ProcessRecord callerApp, - List<Object> receivers, IIntentReceiver orderedResultTo, Bundle orderedExtras) { - return makeBroadcastRecord(intent, callerApp, BroadcastOptions.makeBasic(), - receivers, true, orderedResultTo, orderedExtras, UserHandle.USER_SYSTEM); - } - private BroadcastRecord makeBroadcastRecord(Intent intent, ProcessRecord callerApp, BroadcastOptions options, List<Object> receivers) { return makeBroadcastRecord(intent, callerApp, options, @@ -562,12 +556,24 @@ public class BroadcastQueueTest { } private BroadcastRecord makeBroadcastRecord(Intent intent, ProcessRecord callerApp, + List<Object> receivers, IIntentReceiver resultTo) { + return makeBroadcastRecord(intent, callerApp, BroadcastOptions.makeBasic(), + receivers, false, resultTo, null, UserHandle.USER_SYSTEM); + } + + private BroadcastRecord makeOrderedBroadcastRecord(Intent intent, ProcessRecord callerApp, + List<Object> receivers, IIntentReceiver resultTo, Bundle resultExtras) { + return makeBroadcastRecord(intent, callerApp, BroadcastOptions.makeBasic(), + receivers, true, resultTo, resultExtras, UserHandle.USER_SYSTEM); + } + + private BroadcastRecord makeBroadcastRecord(Intent intent, ProcessRecord callerApp, BroadcastOptions options, List<Object> receivers, boolean ordered, - IIntentReceiver orderedResultTo, Bundle orderedExtras, int userId) { + IIntentReceiver resultTo, Bundle resultExtras, int userId) { return new BroadcastRecord(mQueue, intent, callerApp, callerApp.info.packageName, null, callerApp.getPid(), callerApp.info.uid, false, null, null, null, null, - AppOpsManager.OP_NONE, options, receivers, callerApp, orderedResultTo, - Activity.RESULT_OK, null, orderedExtras, ordered, false, false, userId, false, null, + AppOpsManager.OP_NONE, options, receivers, callerApp, resultTo, + Activity.RESULT_OK, null, resultExtras, ordered, false, false, userId, false, null, false, null); } @@ -1347,6 +1353,26 @@ public class BroadcastQueueTest { } /** + * Verify that we deliver results for unordered broadcasts. + */ + @Test + public void testUnordered_ResultTo() throws Exception { + final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED); + final IApplicationThread callerThread = callerApp.getThread(); + + final IIntentReceiver resultTo = mock(IIntentReceiver.class); + final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED); + enqueueBroadcast(makeBroadcastRecord(airplane, callerApp, + List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN), + makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE)), resultTo)); + + waitForIdle(); + verify(callerThread).scheduleRegisteredReceiver(any(), argThat(filterEquals(airplane)), + eq(Activity.RESULT_OK), any(), any(), eq(false), + anyBoolean(), eq(UserHandle.USER_SYSTEM), anyInt()); + } + + /** * Verify that we're not surprised by a process attempting to finishing a * broadcast when none is in progress. */ diff --git a/services/tests/servicestests/src/com/android/server/backup/transport/BackupTransportClientTest.java b/services/tests/servicestests/src/com/android/server/backup/transport/BackupTransportClientTest.java index 581a2a71a9b6..2d7d46f83c47 100644 --- a/services/tests/servicestests/src/com/android/server/backup/transport/BackupTransportClientTest.java +++ b/services/tests/servicestests/src/com/android/server/backup/transport/BackupTransportClientTest.java @@ -21,6 +21,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.fail; import android.app.backup.BackupTransport; +import android.app.backup.IBackupManagerMonitor; import android.app.backup.RestoreDescription; import android.app.backup.RestoreSet; import android.content.Intent; @@ -254,6 +255,9 @@ public class BackupTransportClientTest { ITransportStatusCallback c) throws RemoteException {} @Override public void abortFullRestore(ITransportStatusCallback c) throws RemoteException {} @Override public void getTransportFlags(AndroidFuture<Integer> f) throws RemoteException {} + @Override + public void getBackupManagerMonitor(AndroidFuture<IBackupManagerMonitor> resultFuture) + throws RemoteException {} @Override public IBinder asBinder() { return null; } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java index 73548a3a1132..1b5db0a35449 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java @@ -41,6 +41,7 @@ import android.hardware.biometrics.common.OperationContext; import android.hardware.biometrics.fingerprint.ISession; import android.hardware.biometrics.fingerprint.PointerContext; import android.hardware.fingerprint.Fingerprint; +import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.hardware.fingerprint.ISidefpsController; import android.hardware.fingerprint.IUdfpsOverlayController; @@ -74,6 +75,7 @@ import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; +import java.time.Clock; import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; @@ -131,6 +133,8 @@ public class FingerprintAuthenticationClientTest { private Probe mLuxProbe; @Mock private AuthSessionCoordinator mAuthSessionCoordinator; + @Mock + private Clock mClock; @Captor private ArgumentCaptor<OperationContext> mOperationContextCaptor; @Captor @@ -451,6 +455,52 @@ public class FingerprintAuthenticationClientTest { } @Test + public void sideFingerprintSkipsWindowIfVendorMessageMatch() throws Exception { + when(mSensorProps.isAnySidefpsType()).thenReturn(true); + final int vendorAcquireMessage = 1234; + + mContext.getOrCreateTestableResources().addOverride( + R.integer.config_sidefpsSkipWaitForPowerAcquireMessage, + FingerprintManager.FINGERPRINT_ACQUIRED_VENDOR); + mContext.getOrCreateTestableResources().addOverride( + R.integer.config_sidefpsSkipWaitForPowerVendorAcquireMessage, + vendorAcquireMessage); + + final FingerprintAuthenticationClient client = createClient(1); + client.start(mCallback); + mLooper.dispatchAll(); + client.onAuthenticated(new Fingerprint("friendly", 4 /* fingerId */, 5 /* deviceId */), + true /* authenticated */, new ArrayList<>()); + client.onAcquired(FingerprintManager.FINGERPRINT_ACQUIRED_VENDOR, vendorAcquireMessage); + mLooper.dispatchAll(); + + verify(mCallback).onClientFinished(any(), eq(true)); + } + + @Test + public void sideFingerprintDoesNotSkipWindowOnVendorErrorMismatch() throws Exception { + when(mSensorProps.isAnySidefpsType()).thenReturn(true); + final int vendorAcquireMessage = 1234; + + mContext.getOrCreateTestableResources().addOverride( + R.integer.config_sidefpsSkipWaitForPowerAcquireMessage, + FingerprintManager.FINGERPRINT_ACQUIRED_VENDOR); + mContext.getOrCreateTestableResources().addOverride( + R.integer.config_sidefpsSkipWaitForPowerVendorAcquireMessage, + vendorAcquireMessage); + + final FingerprintAuthenticationClient client = createClient(1); + client.start(mCallback); + mLooper.dispatchAll(); + client.onAuthenticated(new Fingerprint("friendly", 4 /* fingerId */, 5 /* deviceId */), + true /* authenticated */, new ArrayList<>()); + client.onAcquired(FingerprintManager.FINGERPRINT_ACQUIRED_VENDOR, 1); + mLooper.dispatchAll(); + + verify(mCallback, never()).onClientFinished(any(), anyBoolean()); + } + + @Test public void sideFingerprintSendsAuthIfFingerUp() throws Exception { when(mSensorProps.isAnySidefpsType()).thenReturn(true); @@ -497,6 +547,79 @@ public class FingerprintAuthenticationClientTest { verify(mCallback).onClientFinished(any(), eq(true)); } + @Test + public void sideFingerprintPowerWindowStartsOnAcquireStart() throws Exception { + final int powerWindow = 500; + final long authStart = 300; + + when(mSensorProps.isAnySidefpsType()).thenReturn(true); + mContext.getOrCreateTestableResources().addOverride( + R.integer.config_sidefpsBpPowerPressWindow, powerWindow); + + final FingerprintAuthenticationClient client = createClient(1); + client.start(mCallback); + + // Acquire start occurs at time = 0ms + when(mClock.millis()).thenReturn(0L); + client.onAcquired(FingerprintManager.FINGERPRINT_ACQUIRED_START, 0 /* vendorCode */); + + // Auth occurs at time = 300 + when(mClock.millis()).thenReturn(authStart); + // At this point the delay should be 500 - (300 - 0) == 200 milliseconds. + client.onAuthenticated(new Fingerprint("friendly", 4 /* fingerId */, 5 /* deviceId */), + true /* authenticated */, new ArrayList<>()); + mLooper.dispatchAll(); + verify(mCallback, never()).onClientFinished(any(), anyBoolean()); + + // After waiting 200 milliseconds, auth should succeed. + mLooper.moveTimeForward(powerWindow - authStart); + mLooper.dispatchAll(); + verify(mCallback).onClientFinished(any(), eq(true)); + } + + @Test + public void sideFingerprintPowerWindowStartsOnLastAcquireStart() throws Exception { + final int powerWindow = 500; + + when(mSensorProps.isAnySidefpsType()).thenReturn(true); + mContext.getOrCreateTestableResources().addOverride( + R.integer.config_sidefpsBpPowerPressWindow, powerWindow); + + final FingerprintAuthenticationClient client = createClient(1); + client.start(mCallback); + // Acquire start occurs at time = 0ms + when(mClock.millis()).thenReturn(0L); + client.onAcquired(FingerprintManager.FINGERPRINT_ACQUIRED_START, 0 /* vendorCode */); + + // Auth reject occurs at time = 300ms + when(mClock.millis()).thenReturn(300L); + client.onAuthenticated(new Fingerprint("friendly", 4 /* fingerId */, 5 /* deviceId */), + false /* authenticated */, new ArrayList<>()); + mLooper.dispatchAll(); + + mLooper.moveTimeForward(300); + mLooper.dispatchAll(); + verify(mCallback, never()).onClientFinished(any(), anyBoolean()); + + when(mClock.millis()).thenReturn(1300L); + client.onAcquired(FingerprintManager.FINGERPRINT_ACQUIRED_START, 0 /* vendorCode */); + + // If code is correct, the new acquired start timestamp should be used + // and the code should only have to wait 500 - (1500-1300)ms. + when(mClock.millis()).thenReturn(1500L); + client.onAuthenticated(new Fingerprint("friendly", 4 /* fingerId */, 5 /* deviceId */), + true /* authenticated */, new ArrayList<>()); + mLooper.dispatchAll(); + + mLooper.moveTimeForward(299); + mLooper.dispatchAll(); + verify(mCallback, never()).onClientFinished(any(), anyBoolean()); + + mLooper.moveTimeForward(1); + mLooper.dispatchAll(); + verify(mCallback).onClientFinished(any(), eq(true)); + } + private FingerprintAuthenticationClient createClient() throws RemoteException { return createClient(100 /* version */, true /* allowBackgroundAuthentication */); } @@ -524,7 +647,7 @@ public class FingerprintAuthenticationClientTest { null /* taskStackListener */, mLockoutCache, mUdfpsOverlayController, mSideFpsController, allowBackgroundAuthentication, mSensorProps, - new Handler(mLooper.getLooper()), 0 /* biometricStrength */) { + new Handler(mLooper.getLooper()), 0 /* biometricStrength */, mClock) { @Override protected ActivityTaskManager getActivityTaskManager() { return mActivityTaskManager; diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java index 6b8c26d7b1d4..d2f2af1b91b6 100644 --- a/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java @@ -16,6 +16,8 @@ package com.android.server.companion.virtual; +import static com.google.common.truth.Truth.assertWithMessage; + import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; @@ -25,6 +27,7 @@ import static org.mockito.Mockito.verify; import android.hardware.display.DisplayManagerInternal; import android.hardware.input.IInputManager; +import android.hardware.input.InputManager; import android.os.Binder; import android.os.Handler; import android.os.IBinder; @@ -88,6 +91,30 @@ public class InputControllerTest { } @Test + public void registerInputDevice_deviceCreation_hasDeviceId() { + final IBinder device1Token = new Binder("device1"); + mInputController.createMouse("mouse", /*vendorId= */ 1, /*productId= */ 1, device1Token, + /* displayId= */ 1); + int device1Id = mInputController.getInputDeviceId(device1Token); + + final IBinder device2Token = new Binder("device2"); + mInputController.createKeyboard("keyboard", /*vendorId= */2, /*productId= */ 2, + device2Token, 2); + int device2Id = mInputController.getInputDeviceId(device2Token); + + assertWithMessage("Different devices should have different id").that( + device1Id).isNotEqualTo(device2Id); + + + int[] deviceIds = InputManager.getInstance().getInputDeviceIds(); + assertWithMessage("InputManager's deviceIds list should contain id of device 1").that( + deviceIds).asList().contains(device1Id); + assertWithMessage("InputManager's deviceIds list should contain id of device 2").that( + deviceIds).asList().contains(device2Id); + + } + + @Test public void unregisterInputDevice_allMiceUnregistered_clearPointerDisplayId() { final IBinder deviceToken = new Binder(); mInputController.createMouse("name", /*vendorId= */ 1, /*productId= */ 1, deviceToken, @@ -115,4 +142,5 @@ public class InputControllerTest { mInputController.unregisterInputDevice(deviceToken); verify(mInputManagerInternalMock).setVirtualMousePointerDisplayId(eq(1)); } + } diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java index 9c5d1a5b0610..c5ba360b6170 100644 --- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java @@ -121,6 +121,7 @@ public class VirtualDeviceManagerServiceTest { private static final int VENDOR_ID = 5; private static final String UNIQUE_ID = "uniqueid"; private static final String PHYS = "phys"; + private static final int DEVICE_ID = 42; private static final int HEIGHT = 1800; private static final int WIDTH = 900; private static final Binder BINDER = new Binder("binder"); @@ -530,6 +531,16 @@ public class VirtualDeviceManagerServiceTest { } @Test + public void createVirtualKeyboard_inputDeviceId_obtainFromInputController() { + final int fd = 1; + mInputController.addDeviceForTesting(BINDER, fd, /* type= */ 1, /* displayId= */ 1, PHYS, + DEVICE_ID); + assertWithMessage( + "InputController should return device id from InputDeviceDescriptor").that( + mInputController.getInputDeviceId(BINDER)).isEqualTo(DEVICE_ID); + } + + @Test public void onAudioSessionStarting_hasVirtualAudioController() { mDeviceImpl.onVirtualDisplayCreatedLocked( mDeviceImpl.createWindowPolicyController(), DISPLAY_ID); @@ -578,7 +589,7 @@ public class VirtualDeviceManagerServiceTest { final int action = VirtualKeyEvent.ACTION_UP; mInputController.mInputDeviceDescriptors.put(BINDER, new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 1, - /* displayId= */ 1, PHYS)); + /* displayId= */ 1, PHYS, DEVICE_ID)); mDeviceImpl.sendKeyEvent(BINDER, new VirtualKeyEvent.Builder().setKeyCode(keyCode) .setAction(action).build()); verify(mNativeWrapperMock).writeKeyEvent(fd, keyCode, action); @@ -603,7 +614,7 @@ public class VirtualDeviceManagerServiceTest { final int action = VirtualMouseButtonEvent.ACTION_BUTTON_PRESS; mInputController.mInputDeviceDescriptors.put(BINDER, new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 2, - /* displayId= */ 1, PHYS)); + /* displayId= */ 1, PHYS, DEVICE_ID)); doReturn(1).when(mInputManagerInternalMock).getVirtualMousePointerDisplayId(); mDeviceImpl.sendButtonEvent(BINDER, new VirtualMouseButtonEvent.Builder() .setButtonCode(buttonCode) @@ -618,7 +629,7 @@ public class VirtualDeviceManagerServiceTest { final int action = VirtualMouseButtonEvent.ACTION_BUTTON_PRESS; mInputController.mInputDeviceDescriptors.put(BINDER, new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 2, - /* displayId= */ 1, PHYS)); + /* displayId= */ 1, PHYS, DEVICE_ID)); assertThrows( IllegalStateException.class, () -> @@ -644,7 +655,7 @@ public class VirtualDeviceManagerServiceTest { final float y = 0.7f; mInputController.mInputDeviceDescriptors.put(BINDER, new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 2, - /* displayId= */ 1, PHYS)); + /* displayId= */ 1, PHYS, DEVICE_ID)); doReturn(1).when(mInputManagerInternalMock).getVirtualMousePointerDisplayId(); mDeviceImpl.sendRelativeEvent(BINDER, new VirtualMouseRelativeEvent.Builder() .setRelativeX(x).setRelativeY(y).build()); @@ -658,7 +669,7 @@ public class VirtualDeviceManagerServiceTest { final float y = 0.7f; mInputController.mInputDeviceDescriptors.put(BINDER, new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 2, - /* displayId= */ 1, PHYS)); + /* displayId= */ 1, PHYS, DEVICE_ID)); assertThrows( IllegalStateException.class, () -> @@ -685,7 +696,7 @@ public class VirtualDeviceManagerServiceTest { final float y = 1f; mInputController.mInputDeviceDescriptors.put(BINDER, new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 2, - /* displayId= */ 1, PHYS)); + /* displayId= */ 1, PHYS, DEVICE_ID)); doReturn(1).when(mInputManagerInternalMock).getVirtualMousePointerDisplayId(); mDeviceImpl.sendScrollEvent(BINDER, new VirtualMouseScrollEvent.Builder() .setXAxisMovement(x) @@ -700,7 +711,7 @@ public class VirtualDeviceManagerServiceTest { final float y = 1f; mInputController.mInputDeviceDescriptors.put(BINDER, new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 2, - /* displayId= */ 1, PHYS)); + /* displayId= */ 1, PHYS, DEVICE_ID)); assertThrows( IllegalStateException.class, () -> @@ -733,7 +744,7 @@ public class VirtualDeviceManagerServiceTest { final int action = VirtualTouchEvent.ACTION_UP; mInputController.mInputDeviceDescriptors.put(BINDER, new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 3, - /* displayId= */ 1, PHYS)); + /* displayId= */ 1, PHYS, DEVICE_ID)); mDeviceImpl.sendTouchEvent(BINDER, new VirtualTouchEvent.Builder().setX(x) .setY(y).setAction(action).setPointerId(pointerId).setToolType(toolType).build()); verify(mNativeWrapperMock).writeTouchEvent(fd, pointerId, toolType, action, x, y, Float.NaN, @@ -752,7 +763,7 @@ public class VirtualDeviceManagerServiceTest { final float majorAxisSize = 10.0f; mInputController.mInputDeviceDescriptors.put(BINDER, new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 3, - /* displayId= */ 1, PHYS)); + /* displayId= */ 1, PHYS, DEVICE_ID)); mDeviceImpl.sendTouchEvent(BINDER, new VirtualTouchEvent.Builder().setX(x) .setY(y).setAction(action).setPointerId(pointerId).setToolType(toolType) .setPressure(pressure).setMajorAxisSize(majorAxisSize).build()); diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java index 94e67d16acab..3f55f1bb76a4 100644 --- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java +++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java @@ -16,16 +16,16 @@ package com.android.server.om; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +import static com.google.common.truth.Truth.assertThat; import android.content.om.OverlayIdentifier; import android.content.om.OverlayInfo; import androidx.test.runner.AndroidJUnit4; +import com.google.common.truth.Expect; + +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -43,6 +43,9 @@ public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceI private static final String OVERLAY2 = OVERLAY + "2"; private static final OverlayIdentifier IDENTIFIER2 = new OverlayIdentifier(OVERLAY2); + @Rule + public final Expect expect = Expect.create(); + @Test public void alwaysInitializeAllPackages() { final OverlayManagerServiceImpl impl = getImpl(); @@ -51,13 +54,11 @@ public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceI addPackage(target(otherTarget), USER); addPackage(overlay(OVERLAY, TARGET), USER); - final Set<PackageAndUser> allPackages = - Set.of(new PackageAndUser(TARGET, USER), - new PackageAndUser(otherTarget, USER), - new PackageAndUser(OVERLAY, USER)); + final Set<PackageAndUser> allPackages = Set.of(new PackageAndUser(TARGET, USER)); - assertEquals(allPackages, impl.updateOverlaysForUser(USER)); - assertEquals(allPackages, impl.updateOverlaysForUser(USER)); + // The result should be the same for every time + assertThat(impl.updateOverlaysForUser(USER)).isEqualTo(allPackages); + assertThat(impl.updateOverlaysForUser(USER)).isEqualTo(allPackages); } @Test @@ -66,29 +67,31 @@ public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceI addPackage(target(TARGET), USER); addPackage(overlay(OVERLAY, TARGET), USER); - final Set<PackageAndUser> allPackages = - Set.of(new PackageAndUser(TARGET, USER), new PackageAndUser(OVERLAY, USER)); + final Set<PackageAndUser> allPackages = Set.of(new PackageAndUser(TARGET, USER)); configureSystemOverlay(OVERLAY, ConfigState.IMMUTABLE_DISABLED, 0 /* priority */); - assertEquals(allPackages, impl.updateOverlaysForUser(USER)); + expect.that(impl.updateOverlaysForUser(USER)).isEqualTo(allPackages); final OverlayInfo o1 = impl.getOverlayInfo(IDENTIFIER, USER); - assertNotNull(o1); - assertFalse(o1.isEnabled()); - assertFalse(o1.isMutable); + expect.that(o1).isNotNull(); + assertThat(expect.hasFailures()).isFalse(); + expect.that(o1.isEnabled()).isFalse(); + expect.that(o1.isMutable).isFalse(); configureSystemOverlay(OVERLAY, ConfigState.IMMUTABLE_ENABLED, 0 /* priority */); - assertEquals(allPackages, impl.updateOverlaysForUser(USER)); + expect.that(impl.updateOverlaysForUser(USER)).isEqualTo(allPackages); final OverlayInfo o2 = impl.getOverlayInfo(IDENTIFIER, USER); - assertNotNull(o2); - assertTrue(o2.isEnabled()); - assertFalse(o2.isMutable); + expect.that(o2).isNotNull(); + assertThat(expect.hasFailures()).isFalse(); + expect.that(o2.isEnabled()).isTrue(); + expect.that(o2.isMutable).isFalse(); configureSystemOverlay(OVERLAY, ConfigState.IMMUTABLE_DISABLED, 0 /* priority */); - assertEquals(allPackages, impl.updateOverlaysForUser(USER)); + expect.that(impl.updateOverlaysForUser(USER)).isEqualTo(allPackages); final OverlayInfo o3 = impl.getOverlayInfo(IDENTIFIER, USER); - assertNotNull(o3); - assertFalse(o3.isEnabled()); - assertFalse(o3.isMutable); + expect.that(o3).isNotNull(); + assertThat(expect.hasFailures()).isFalse(); + expect.that(o3.isEnabled()).isFalse(); + expect.that(o3.isMutable).isFalse(); } @Test @@ -98,28 +101,30 @@ public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceI addPackage(overlay(OVERLAY, TARGET), USER); configureSystemOverlay(OVERLAY, ConfigState.MUTABLE_DISABLED, 0 /* priority */); - final Set<PackageAndUser> allPackages = - Set.of(new PackageAndUser(TARGET, USER), new PackageAndUser(OVERLAY, USER)); + final Set<PackageAndUser> allPackages = Set.of(new PackageAndUser(TARGET, USER)); - assertEquals(allPackages, impl.updateOverlaysForUser(USER)); + expect.that(impl.updateOverlaysForUser(USER)).isEqualTo(allPackages); final OverlayInfo o1 = impl.getOverlayInfo(IDENTIFIER, USER); - assertNotNull(o1); - assertFalse(o1.isEnabled()); - assertTrue(o1.isMutable); + expect.that(o1).isNotNull(); + assertThat(expect.hasFailures()).isFalse(); + expect.that(o1.isEnabled()).isFalse(); + expect.that(o1.isMutable).isTrue(); configureSystemOverlay(OVERLAY, ConfigState.MUTABLE_ENABLED, 0 /* priority */); - assertEquals(allPackages, impl.updateOverlaysForUser(USER)); + expect.that(impl.updateOverlaysForUser(USER)).isEqualTo(allPackages); final OverlayInfo o2 = impl.getOverlayInfo(IDENTIFIER, USER); - assertNotNull(o2); - assertFalse(o2.isEnabled()); - assertTrue(o2.isMutable); + expect.that(o2).isNotNull(); + assertThat(expect.hasFailures()).isFalse(); + expect.that(o2.isEnabled()).isFalse(); + expect.that(o2.isMutable).isTrue(); configureSystemOverlay(OVERLAY, ConfigState.MUTABLE_DISABLED, 0 /* priority */); - assertEquals(allPackages, impl.updateOverlaysForUser(USER)); + expect.that(impl.updateOverlaysForUser(USER)).isEqualTo(allPackages); final OverlayInfo o3 = impl.getOverlayInfo(IDENTIFIER, USER); - assertNotNull(o3); - assertFalse(o3.isEnabled()); - assertTrue(o3.isMutable); + expect.that(o3).isNotNull(); + assertThat(expect.hasFailures()).isFalse(); + expect.that(o3.isEnabled()).isFalse(); + expect.that(o3.isMutable).isTrue(); } @Test @@ -128,17 +133,17 @@ public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceI addPackage(target(TARGET), USER); addPackage(overlay(OVERLAY, TARGET), USER); - final Set<PackageAndUser> allPackages = - Set.of(new PackageAndUser(TARGET, USER), new PackageAndUser(OVERLAY, USER)); + final Set<PackageAndUser> allPackages = Set.of(new PackageAndUser(TARGET, USER)); final Consumer<ConfigState> setOverlay = (state -> { configureSystemOverlay(OVERLAY, state, 0 /* priority */); - assertEquals(allPackages, impl.updateOverlaysForUser(USER)); + expect.that(impl.updateOverlaysForUser(USER)).isEqualTo(allPackages); final OverlayInfo o = impl.getOverlayInfo(IDENTIFIER, USER); - assertNotNull(o); - assertEquals(o.isEnabled(), state == ConfigState.IMMUTABLE_ENABLED + expect.that(o).isNotNull(); + assertThat(expect.hasFailures()).isFalse(); + expect.that(o.isEnabled()).isEqualTo(state == ConfigState.IMMUTABLE_ENABLED || state == ConfigState.MUTABLE_ENABLED); - assertEquals(o.isMutable, state == ConfigState.MUTABLE_DISABLED + expect.that(o.isMutable).isEqualTo(state == ConfigState.MUTABLE_DISABLED || state == ConfigState.MUTABLE_ENABLED); }); @@ -180,20 +185,20 @@ public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceI configureSystemOverlay(OVERLAY, ConfigState.MUTABLE_DISABLED, 0 /* priority */); configureSystemOverlay(OVERLAY2, ConfigState.MUTABLE_DISABLED, 1 /* priority */); - final Set<PackageAndUser> allPackages = - Set.of(new PackageAndUser(TARGET, USER), new PackageAndUser(OVERLAY, USER), - new PackageAndUser(OVERLAY2, USER)); + final Set<PackageAndUser> allPackages = Set.of(new PackageAndUser(TARGET, USER)); - assertEquals(allPackages, impl.updateOverlaysForUser(USER)); + expect.that(impl.updateOverlaysForUser(USER)).isEqualTo(allPackages); final OverlayInfo o1 = impl.getOverlayInfo(IDENTIFIER, USER); - assertNotNull(o1); - assertEquals(0, o1.priority); - assertFalse(o1.isEnabled()); + expect.that(o1).isNotNull(); + assertThat(expect.hasFailures()).isFalse(); + expect.that(o1.priority).isEqualTo(0); + expect.that(o1.isEnabled()).isFalse(); final OverlayInfo o2 = impl.getOverlayInfo(IDENTIFIER2, USER); - assertNotNull(o2); - assertEquals(1, o2.priority); - assertFalse(o2.isEnabled()); + expect.that(o2).isNotNull(); + assertThat(expect.hasFailures()).isFalse(); + expect.that(o2.priority).isEqualTo(1); + expect.that(o2.isEnabled()).isFalse(); // Overlay priority changing between reboots should not affect enable state of mutable // overlays. @@ -202,16 +207,18 @@ public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceI // Reorder the overlays configureSystemOverlay(OVERLAY, ConfigState.MUTABLE_DISABLED, 1 /* priority */); configureSystemOverlay(OVERLAY2, ConfigState.MUTABLE_DISABLED, 0 /* priority */); - assertEquals(allPackages, impl.updateOverlaysForUser(USER)); + expect.that(impl.updateOverlaysForUser(USER)).isEqualTo(allPackages); final OverlayInfo o3 = impl.getOverlayInfo(IDENTIFIER, USER); - assertNotNull(o3); - assertEquals(1, o3.priority); - assertTrue(o3.isEnabled()); + expect.that(o3).isNotNull(); + assertThat(expect.hasFailures()).isFalse(); + expect.that(o3.priority).isEqualTo(1); + expect.that(o3.isEnabled()).isTrue(); final OverlayInfo o4 = impl.getOverlayInfo(IDENTIFIER2, USER); - assertNotNull(o4); - assertEquals(0, o4.priority); - assertFalse(o4.isEnabled()); + expect.that(o4).isNotNull(); + assertThat(expect.hasFailures()).isFalse(); + expect.that(o4.priority).isEqualTo(0); + expect.that(o4.isEnabled()).isFalse(); } @Test @@ -223,33 +230,35 @@ public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceI configureSystemOverlay(OVERLAY, ConfigState.IMMUTABLE_ENABLED, 0 /* priority */); configureSystemOverlay(OVERLAY2, ConfigState.IMMUTABLE_ENABLED, 1 /* priority */); - final Set<PackageAndUser> allPackages = - Set.of(new PackageAndUser(TARGET, USER), new PackageAndUser(OVERLAY, USER), - new PackageAndUser(OVERLAY2, USER)); + final Set<PackageAndUser> allPackages = Set.of(new PackageAndUser(TARGET, USER)); - assertEquals(allPackages, impl.updateOverlaysForUser(USER)); + expect.that(impl.updateOverlaysForUser(USER)).isEqualTo(allPackages); final OverlayInfo o1 = impl.getOverlayInfo(IDENTIFIER, USER); - assertNotNull(o1); - assertEquals(0, o1.priority); - assertTrue(o1.isEnabled()); + expect.that(o1).isNotNull(); + assertThat(expect.hasFailures()).isFalse(); + expect.that(o1.priority).isEqualTo(0); + expect.that(o1.isEnabled()).isTrue(); final OverlayInfo o2 = impl.getOverlayInfo(IDENTIFIER2, USER); - assertNotNull(o2); - assertEquals(1, o2.priority); - assertTrue(o2.isEnabled()); + expect.that(o2).isNotNull(); + assertThat(expect.hasFailures()).isFalse(); + expect.that(o2.priority).isEqualTo(1); + expect.that(o2.isEnabled()).isTrue(); // Reorder the overlays configureSystemOverlay(OVERLAY, ConfigState.IMMUTABLE_ENABLED, 1 /* priority */); configureSystemOverlay(OVERLAY2, ConfigState.IMMUTABLE_ENABLED, 0 /* priority */); - assertEquals(allPackages, impl.updateOverlaysForUser(USER)); + expect.that(impl.updateOverlaysForUser(USER)).isEqualTo(allPackages); final OverlayInfo o3 = impl.getOverlayInfo(IDENTIFIER, USER); - assertNotNull(o3); - assertEquals(1, o3.priority); - assertTrue(o3.isEnabled()); + expect.that(o3).isNotNull(); + assertThat(expect.hasFailures()).isFalse(); + expect.that(o3.priority).isEqualTo(1); + expect.that(o3.isEnabled()).isTrue(); final OverlayInfo o4 = impl.getOverlayInfo(IDENTIFIER2, USER); - assertNotNull(o4); - assertEquals(0, o4.priority); - assertTrue(o4.isEnabled()); + expect.that(o4).isNotNull(); + assertThat(expect.hasFailures()).isFalse(); + expect.that(o4.priority).isEqualTo(0); + expect.that(o4.isEnabled()).isTrue(); } } diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java index 96707fde8edb..00aa52012e59 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java @@ -176,6 +176,13 @@ public class UserManagerServiceTest { } @Test + public void testHasUserRestriction_NonExistentUserReturnsFalse() { + int nonExistentUserId = UserHandle.USER_NULL; + assertThat(mUserManagerService.hasUserRestriction(DISALLOW_USER_SWITCH, nonExistentUserId)) + .isFalse(); + } + + @Test public void testSetUserRestrictionWithIncorrectID() throws Exception { int incorrectId = 1; while (mUserManagerService.userExists(incorrectId)) { diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java index d5893c8d0b9f..77d542a2e43d 100644 --- a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java @@ -52,7 +52,6 @@ public class DexoptOptionsTests { assertFalse(opt.isBootComplete()); assertFalse(opt.isCheckForProfileUpdates()); assertFalse(opt.isDexoptOnlySecondaryDex()); - assertFalse(opt.isDexoptOnlySharedDex()); assertFalse(opt.isDowngrade()); assertFalse(opt.isForce()); assertFalse(opt.isDexoptIdleBackgroundJob()); @@ -67,7 +66,6 @@ public class DexoptOptionsTests { DexoptOptions.DEXOPT_BOOT_COMPLETE | DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES | DexoptOptions.DEXOPT_ONLY_SECONDARY_DEX | - DexoptOptions.DEXOPT_ONLY_SHARED_DEX | DexoptOptions.DEXOPT_DOWNGRADE | DexoptOptions.DEXOPT_AS_SHARED_LIBRARY | DexoptOptions.DEXOPT_IDLE_BACKGROUND_JOB | @@ -81,7 +79,6 @@ public class DexoptOptionsTests { assertTrue(opt.isBootComplete()); assertTrue(opt.isCheckForProfileUpdates()); assertTrue(opt.isDexoptOnlySecondaryDex()); - assertTrue(opt.isDexoptOnlySharedDex()); assertTrue(opt.isDowngrade()); assertTrue(opt.isForce()); assertTrue(opt.isDexoptAsSharedLibrary()); @@ -113,7 +110,6 @@ public class DexoptOptionsTests { assertTrue(opt.isBootComplete()); assertTrue(opt.isCheckForProfileUpdates()); assertFalse(opt.isDexoptOnlySecondaryDex()); - assertFalse(opt.isDexoptOnlySharedDex()); assertFalse(opt.isDowngrade()); assertTrue(opt.isForce()); assertFalse(opt.isDexoptAsSharedLibrary()); @@ -131,7 +127,6 @@ public class DexoptOptionsTests { assertTrue(opt.isBootComplete()); assertFalse(opt.isCheckForProfileUpdates()); assertFalse(opt.isDexoptOnlySecondaryDex()); - assertFalse(opt.isDexoptOnlySharedDex()); assertFalse(opt.isDowngrade()); assertTrue(opt.isForce()); assertFalse(opt.isDexoptAsSharedLibrary()); diff --git a/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java index 235849c1cd8b..c484f457faea 100644 --- a/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java +++ b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java @@ -53,7 +53,8 @@ final class FakeVibratorControllerProvider { private boolean mIsAvailable = true; private boolean mIsInfoLoadSuccessful = true; - private long mLatency; + private long mOnLatency; + private long mOffLatency; private int mOffCount; private int mCapabilities; @@ -97,7 +98,7 @@ final class FakeVibratorControllerProvider { public long on(long milliseconds, long vibrationId) { recordEffectSegment(vibrationId, new StepSegment(VibrationEffect.DEFAULT_AMPLITUDE, /* frequencyHz= */ 0, (int) milliseconds)); - applyLatency(); + applyLatency(mOnLatency); scheduleListener(milliseconds, vibrationId); return milliseconds; } @@ -105,12 +106,13 @@ final class FakeVibratorControllerProvider { @Override public void off() { mOffCount++; + applyLatency(mOffLatency); } @Override public void setAmplitude(float amplitude) { mAmplitudes.add(amplitude); - applyLatency(); + applyLatency(mOnLatency); } @Override @@ -121,7 +123,7 @@ final class FakeVibratorControllerProvider { } recordEffectSegment(vibrationId, new PrebakedSegment((int) effect, false, (int) strength)); - applyLatency(); + applyLatency(mOnLatency); scheduleListener(EFFECT_DURATION, vibrationId); return EFFECT_DURATION; } @@ -141,7 +143,7 @@ final class FakeVibratorControllerProvider { duration += EFFECT_DURATION + primitive.getDelay(); recordEffectSegment(vibrationId, primitive); } - applyLatency(); + applyLatency(mOnLatency); scheduleListener(duration, vibrationId); return duration; } @@ -154,7 +156,7 @@ final class FakeVibratorControllerProvider { recordEffectSegment(vibrationId, primitive); } recordBraking(vibrationId, braking); - applyLatency(); + applyLatency(mOnLatency); scheduleListener(duration, vibrationId); return duration; } @@ -193,10 +195,10 @@ final class FakeVibratorControllerProvider { return mIsInfoLoadSuccessful; } - private void applyLatency() { + private void applyLatency(long latencyMillis) { try { - if (mLatency > 0) { - Thread.sleep(mLatency); + if (latencyMillis > 0) { + Thread.sleep(latencyMillis); } } catch (InterruptedException e) { } @@ -240,10 +242,15 @@ final class FakeVibratorControllerProvider { /** * Sets the latency this controller should fake for turning the vibrator hardware on or setting - * it's vibration amplitude. + * the vibration amplitude. */ - public void setLatency(long millis) { - mLatency = millis; + public void setOnLatency(long millis) { + mOnLatency = millis; + } + + /** Sets the latency this controller should fake for turning the vibrator off. */ + public void setOffLatency(long millis) { + mOffLatency = millis; } /** Set the capabilities of the fake vibrator hardware. */ diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java index a15e4b0c74a0..fc830a9f61ad 100644 --- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java +++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java @@ -1159,7 +1159,7 @@ public class VibrationThreadTest { // 25% of the first waveform step will be spent on the native on() call. // 25% of each waveform step will be spent on the native setAmplitude() call.. - mVibratorProviders.get(VIBRATOR_ID).setLatency(stepDuration / 4); + mVibratorProviders.get(VIBRATOR_ID).setOnLatency(stepDuration / 4); mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); int stepCount = totalDuration / stepDuration; @@ -1190,7 +1190,7 @@ public class VibrationThreadTest { fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK); long latency = 5_000; // 5s - fakeVibrator.setLatency(latency); + fakeVibrator.setOnLatency(latency); long vibrationId = 1; VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK); @@ -1204,8 +1204,7 @@ public class VibrationThreadTest { // fail at waitForCompletion(cancellingThread). Thread cancellingThread = new Thread( () -> conductor.notifyCancelled( - new Vibration.EndInfo( - Vibration.Status.CANCELLED_BY_USER), + new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_USER), /* immediate= */ false)); cancellingThread.start(); diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java index c46fecd1a55e..c83afb74ccba 100644 --- a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java @@ -826,13 +826,40 @@ public class VibratorManagerServiceTest { // The second vibration shouldn't have recorded that the vibrators were turned on. verify(mBatteryStatsMock, times(1)).noteVibratorOn(anyInt(), anyLong()); // No segment played is the prebaked CLICK from the second vibration. - assertFalse( - mVibratorProviders.get(1).getAllEffectSegments().stream() - .anyMatch(segment -> segment instanceof PrebakedSegment)); + assertFalse(mVibratorProviders.get(1).getAllEffectSegments().stream() + .anyMatch(PrebakedSegment.class::isInstance)); cancelVibrate(service); // Clean up repeating effect. } @Test + public void vibrate_withOngoingRepeatingVibrationBeingCancelled_playsAfterPreviousIsCancelled() + throws Exception { + mockVibrators(1); + FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1); + fakeVibrator.setOffLatency(50); // Add latency so cancellation is slow. + fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); + fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK); + VibratorManagerService service = createSystemReadyService(); + + VibrationEffect repeatingEffect = VibrationEffect.createWaveform( + new long[]{10, 10_000}, new int[]{255, 0}, 1); + vibrate(service, repeatingEffect, ALARM_ATTRS); + + // VibrationThread will start this vibration async, wait until the off waveform step. + assertTrue(waitUntil(s -> fakeVibrator.getOffCount() > 0, service, TEST_TIMEOUT_MILLIS)); + + // Cancel vibration right before requesting a new one. + // This should trigger slow IVibrator.off before setting the vibration status to cancelled. + cancelVibrate(service); + vibrateAndWaitUntilFinished(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), + ALARM_ATTRS); + + // Check that second vibration was played. + assertTrue(fakeVibrator.getAllEffectSegments().stream() + .anyMatch(PrebakedSegment.class::isInstance)); + } + + @Test public void vibrate_withNewRepeatingVibration_cancelsOngoingEffect() throws Exception { mockVibrators(1); mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); @@ -880,10 +907,8 @@ public class VibratorManagerServiceTest { // The second vibration shouldn't have recorded that the vibrators were turned on. verify(mBatteryStatsMock, times(1)).noteVibratorOn(anyInt(), anyLong()); // The second vibration shouldn't have played any prebaked segment. - assertFalse( - mVibratorProviders.get(1).getAllEffectSegments().stream() - .anyMatch(segment -> segment instanceof PrebakedSegment)); - + assertFalse(mVibratorProviders.get(1).getAllEffectSegments().stream() + .anyMatch(PrebakedSegment.class::isInstance)); cancelVibrate(service); // Clean up long effect. } diff --git a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java index 91c2fe0eb262..8e81e2d8997c 100644 --- a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java @@ -1371,6 +1371,39 @@ public class UiModeManagerServiceTest extends UiServiceTestCase { verify(mInjector).startDreamWhenDockedIfAppropriate(mContext); } + @Test + public void dreamWhenDocked_ambientModeSuppressed_suppressionEnabled() { + mUiManagerService.setStartDreamImmediatelyOnDock(true); + mUiManagerService.setDreamsDisabledByAmbientModeSuppression(true); + + when(mLocalPowerManager.isAmbientDisplaySuppressed()).thenReturn(true); + triggerDockIntent(); + verifyAndSendResultBroadcast(); + verify(mInjector, never()).startDreamWhenDockedIfAppropriate(mContext); + } + + @Test + public void dreamWhenDocked_ambientModeSuppressed_suppressionDisabled() { + mUiManagerService.setStartDreamImmediatelyOnDock(true); + mUiManagerService.setDreamsDisabledByAmbientModeSuppression(false); + + when(mLocalPowerManager.isAmbientDisplaySuppressed()).thenReturn(true); + triggerDockIntent(); + verifyAndSendResultBroadcast(); + verify(mInjector).startDreamWhenDockedIfAppropriate(mContext); + } + + @Test + public void dreamWhenDocked_ambientModeNotSuppressed_suppressionEnabled() { + mUiManagerService.setStartDreamImmediatelyOnDock(true); + mUiManagerService.setDreamsDisabledByAmbientModeSuppression(true); + + when(mLocalPowerManager.isAmbientDisplaySuppressed()).thenReturn(false); + triggerDockIntent(); + verifyAndSendResultBroadcast(); + verify(mInjector).startDreamWhenDockedIfAppropriate(mContext); + } + private void triggerDockIntent() { final Intent dockedIntent = new Intent(Intent.ACTION_DOCK_EVENT) diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java index 1e945776cf40..248a3fc8b3b1 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java @@ -15,6 +15,7 @@ */ package com.android.server.notification; +import static android.content.pm.PackageManager.MATCH_ANY_USER; import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ALERTING; import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_CONVERSATIONS; import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ONGOING; @@ -30,9 +31,11 @@ import static junit.framework.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.intThat; import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; @@ -49,6 +52,7 @@ import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.ServiceInfo; import android.content.pm.VersionedPackage; +import android.content.res.Resources; import android.os.Bundle; import android.os.UserHandle; import android.service.notification.NotificationListenerFilter; @@ -69,6 +73,7 @@ import com.google.common.collect.ImmutableList; import org.junit.Before; import org.junit.Test; +import org.mockito.ArgumentMatcher; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.internal.util.reflection.FieldSetter; @@ -77,6 +82,7 @@ import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.util.Arrays; import java.util.List; public class NotificationListenersTest extends UiServiceTestCase { @@ -85,6 +91,8 @@ public class NotificationListenersTest extends UiServiceTestCase { private PackageManager mPm; @Mock private IPackageManager miPm; + @Mock + private Resources mResources; @Mock NotificationManagerService mNm; @@ -96,7 +104,8 @@ public class NotificationListenersTest extends UiServiceTestCase { private ComponentName mCn1 = new ComponentName("pkg", "pkg.cmp"); private ComponentName mCn2 = new ComponentName("pkg2", "pkg2.cmp2"); - + private ComponentName mUninstalledComponent = new ComponentName("pkg3", + "pkg3.NotificationListenerService"); @Before public void setUp() throws Exception { @@ -111,7 +120,7 @@ public class NotificationListenersTest extends UiServiceTestCase { @Test public void testReadExtraTag() throws Exception { - String xml = "<" + TAG_REQUESTED_LISTENERS+ ">" + String xml = "<" + TAG_REQUESTED_LISTENERS + ">" + "<listener component=\"" + mCn1.flattenToString() + "\" user=\"0\">" + "<allowed types=\"7\" />" + "</listener>" @@ -131,11 +140,55 @@ public class NotificationListenersTest extends UiServiceTestCase { } @Test + public void loadDefaultsFromConfig_forHeadlessSystemUser_loadUninstalled() throws Exception { + // setup with headless system user mode + mListeners = spy(mNm.new NotificationListeners( + mContext, new Object(), mock(ManagedServices.UserProfiles.class), miPm, + /* isHeadlessSystemUserMode= */ true)); + mockDefaultListenerConfigForUninstalledComponent(mUninstalledComponent); + + mListeners.loadDefaultsFromConfig(); + + assertThat(mListeners.getDefaultComponents()).contains(mUninstalledComponent); + } + + @Test + public void loadDefaultsFromConfig_forNonHeadlessSystemUser_ignoreUninstalled() + throws Exception { + // setup without headless system user mode + mListeners = spy(mNm.new NotificationListeners( + mContext, new Object(), mock(ManagedServices.UserProfiles.class), miPm, + /* isHeadlessSystemUserMode= */ false)); + mockDefaultListenerConfigForUninstalledComponent(mUninstalledComponent); + + mListeners.loadDefaultsFromConfig(); + + assertThat(mListeners.getDefaultComponents()).doesNotContain(mUninstalledComponent); + } + + private void mockDefaultListenerConfigForUninstalledComponent(ComponentName componentName) { + ArraySet<ComponentName> components = new ArraySet<>(Arrays.asList(componentName)); + when(mResources + .getString( + com.android.internal.R.string.config_defaultListenerAccessPackages)) + .thenReturn(componentName.getPackageName()); + when(mContext.getResources()).thenReturn(mResources); + doReturn(components).when(mListeners).queryPackageForServices( + eq(componentName.getPackageName()), + intThat(hasIntBitFlag(MATCH_ANY_USER)), + anyInt()); + } + + public static ArgumentMatcher<Integer> hasIntBitFlag(int flag) { + return arg -> arg != null && ((arg & flag) == flag); + } + + @Test public void testWriteExtraTag() throws Exception { NotificationListenerFilter nlf = new NotificationListenerFilter(7, new ArraySet<>()); VersionedPackage a1 = new VersionedPackage("pkg1", 243); NotificationListenerFilter nlf2 = - new NotificationListenerFilter(4, new ArraySet<>(new VersionedPackage[] {a1})); + new NotificationListenerFilter(4, new ArraySet<>(new VersionedPackage[]{a1})); mListeners.setNotificationListenerFilter(Pair.create(mCn1, 0), nlf); mListeners.setNotificationListenerFilter(Pair.create(mCn2, 10), nlf2); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationTest.java deleted file mode 100644 index d7650420788c..000000000000 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationTest.java +++ /dev/null @@ -1,551 +0,0 @@ -/* - * Copyright (C) 2017 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.notification; - -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertNotNull; -import static junit.framework.Assert.assertNull; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import android.app.ActivityManager; -import android.app.Notification; -import android.app.PendingIntent; -import android.app.Person; -import android.app.RemoteInput; -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.Color; -import android.graphics.Typeface; -import android.graphics.drawable.Icon; -import android.net.Uri; -import android.text.SpannableStringBuilder; -import android.text.Spanned; -import android.text.style.StyleSpan; -import android.util.Pair; -import android.widget.RemoteViews; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.server.UiServiceTestCase; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class NotificationTest extends UiServiceTestCase { - - @Mock - ActivityManager mAm; - - @Mock - Resources mResources; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - } - - @Test - public void testDoesNotStripsExtenders() { - Notification.Builder nb = new Notification.Builder(mContext, "channel"); - nb.extend(new Notification.CarExtender().setColor(Color.RED)); - nb.extend(new Notification.TvExtender().setChannelId("different channel")); - nb.extend(new Notification.WearableExtender().setDismissalId("dismiss")); - Notification before = nb.build(); - Notification after = Notification.Builder.maybeCloneStrippedForDelivery(before); - - assertTrue(before == after); - - assertEquals("different channel", new Notification.TvExtender(before).getChannelId()); - assertEquals(Color.RED, new Notification.CarExtender(before).getColor()); - assertEquals("dismiss", new Notification.WearableExtender(before).getDismissalId()); - } - - @Test - public void testStyleChangeVisiblyDifferent_noStyles() { - Notification.Builder n1 = new Notification.Builder(mContext, "test"); - Notification.Builder n2 = new Notification.Builder(mContext, "test"); - - assertFalse(Notification.areStyledNotificationsVisiblyDifferent(n1, n2)); - } - - @Test - public void testStyleChangeVisiblyDifferent_noStyleToStyle() { - Notification.Builder n1 = new Notification.Builder(mContext, "test"); - Notification.Builder n2 = new Notification.Builder(mContext, "test") - .setStyle(new Notification.BigTextStyle()); - - assertTrue(Notification.areStyledNotificationsVisiblyDifferent(n1, n2)); - } - - @Test - public void testStyleChangeVisiblyDifferent_styleToNoStyle() { - Notification.Builder n2 = new Notification.Builder(mContext, "test"); - Notification.Builder n1 = new Notification.Builder(mContext, "test") - .setStyle(new Notification.BigTextStyle()); - - assertTrue(Notification.areStyledNotificationsVisiblyDifferent(n1, n2)); - } - - @Test - public void testStyleChangeVisiblyDifferent_changeStyle() { - Notification.Builder n1 = new Notification.Builder(mContext, "test") - .setStyle(new Notification.InboxStyle()); - Notification.Builder n2 = new Notification.Builder(mContext, "test") - .setStyle(new Notification.BigTextStyle()); - - assertTrue(Notification.areStyledNotificationsVisiblyDifferent(n1, n2)); - } - - @Test - public void testInboxTextChange() { - Notification.Builder nInbox1 = new Notification.Builder(mContext, "test") - .setStyle(new Notification.InboxStyle().addLine("a").addLine("b")); - Notification.Builder nInbox2 = new Notification.Builder(mContext, "test") - .setStyle(new Notification.InboxStyle().addLine("b").addLine("c")); - - assertTrue(Notification.areStyledNotificationsVisiblyDifferent(nInbox1, nInbox2)); - } - - @Test - public void testBigTextTextChange() { - Notification.Builder nBigText1 = new Notification.Builder(mContext, "test") - .setStyle(new Notification.BigTextStyle().bigText("something")); - Notification.Builder nBigText2 = new Notification.Builder(mContext, "test") - .setStyle(new Notification.BigTextStyle().bigText("else")); - - assertTrue(Notification.areStyledNotificationsVisiblyDifferent(nBigText1, nBigText2)); - } - - @Test - public void testBigPictureChange() { - Bitmap bitA = mock(Bitmap.class); - when(bitA.getGenerationId()).thenReturn(100); - Bitmap bitB = mock(Bitmap.class); - when(bitB.getGenerationId()).thenReturn(200); - - Notification.Builder nBigPic1 = new Notification.Builder(mContext, "test") - .setStyle(new Notification.BigPictureStyle().bigPicture(bitA)); - Notification.Builder nBigPic2 = new Notification.Builder(mContext, "test") - .setStyle(new Notification.BigPictureStyle().bigPicture(bitB)); - - assertTrue(Notification.areStyledNotificationsVisiblyDifferent(nBigPic1, nBigPic2)); - } - - @Test - public void testMessagingChange_text() { - Notification.Builder nM1 = new Notification.Builder(mContext, "test") - .setStyle(new Notification.MessagingStyle("") - .addMessage(new Notification.MessagingStyle.Message( - "a", 100, mock(Person.class)))); - Notification.Builder nM2 = new Notification.Builder(mContext, "test") - .setStyle(new Notification.MessagingStyle("") - .addMessage(new Notification.MessagingStyle.Message( - "a", 100, mock(Person.class))) - .addMessage(new Notification.MessagingStyle.Message( - "b", 100, mock(Person.class))) - ); - - assertTrue(Notification.areStyledNotificationsVisiblyDifferent(nM1, nM2)); - } - - @Test - public void testMessagingChange_data() { - Notification.Builder nM1 = new Notification.Builder(mContext, "test") - .setStyle(new Notification.MessagingStyle("") - .addMessage(new Notification.MessagingStyle.Message( - "a", 100, mock(Person.class)) - .setData("text", mock(Uri.class)))); - Notification.Builder nM2 = new Notification.Builder(mContext, "test") - .setStyle(new Notification.MessagingStyle("") - .addMessage(new Notification.MessagingStyle.Message( - "a", 100, mock(Person.class)))); - - assertTrue(Notification.areStyledNotificationsVisiblyDifferent(nM1, nM2)); - } - - @Test - public void testMessagingChange_sender() { - Person a = mock(Person.class); - when(a.getName()).thenReturn("A"); - Person b = mock(Person.class); - when(b.getName()).thenReturn("b"); - Notification.Builder nM1 = new Notification.Builder(mContext, "test") - .setStyle(new Notification.MessagingStyle("") - .addMessage(new Notification.MessagingStyle.Message("a", 100, b))); - Notification.Builder nM2 = new Notification.Builder(mContext, "test") - .setStyle(new Notification.MessagingStyle("") - .addMessage(new Notification.MessagingStyle.Message("a", 100, a))); - - assertTrue(Notification.areStyledNotificationsVisiblyDifferent(nM1, nM2)); - } - - @Test - public void testMessagingChange_key() { - Person a = mock(Person.class); - when(a.getKey()).thenReturn("A"); - Person b = mock(Person.class); - when(b.getKey()).thenReturn("b"); - Notification.Builder nM1 = new Notification.Builder(mContext, "test") - .setStyle(new Notification.MessagingStyle("") - .addMessage(new Notification.MessagingStyle.Message("a", 100, a))); - Notification.Builder nM2 = new Notification.Builder(mContext, "test") - .setStyle(new Notification.MessagingStyle("") - .addMessage(new Notification.MessagingStyle.Message("a", 100, b))); - - assertTrue(Notification.areStyledNotificationsVisiblyDifferent(nM1, nM2)); - } - - @Test - public void testMessagingChange_ignoreTimeChange() { - Notification.Builder nM1 = new Notification.Builder(mContext, "test") - .setStyle(new Notification.MessagingStyle("") - .addMessage(new Notification.MessagingStyle.Message( - "a", 100, mock(Person.class)))); - Notification.Builder nM2 = new Notification.Builder(mContext, "test") - .setStyle(new Notification.MessagingStyle("") - .addMessage(new Notification.MessagingStyle.Message( - "a", 1000, mock(Person.class))) - ); - - assertFalse(Notification.areStyledNotificationsVisiblyDifferent(nM1, nM2)); - } - - @Test - public void testRemoteViews_nullChange() { - Notification.Builder n1 = new Notification.Builder(mContext, "test") - .setContent(mock(RemoteViews.class)); - Notification.Builder n2 = new Notification.Builder(mContext, "test"); - assertTrue(Notification.areRemoteViewsChanged(n1, n2)); - - n1 = new Notification.Builder(mContext, "test"); - n2 = new Notification.Builder(mContext, "test") - .setContent(mock(RemoteViews.class)); - assertTrue(Notification.areRemoteViewsChanged(n1, n2)); - - n1 = new Notification.Builder(mContext, "test") - .setCustomBigContentView(mock(RemoteViews.class)); - n2 = new Notification.Builder(mContext, "test"); - assertTrue(Notification.areRemoteViewsChanged(n1, n2)); - - n1 = new Notification.Builder(mContext, "test"); - n2 = new Notification.Builder(mContext, "test") - .setCustomBigContentView(mock(RemoteViews.class)); - assertTrue(Notification.areRemoteViewsChanged(n1, n2)); - - n1 = new Notification.Builder(mContext, "test"); - n2 = new Notification.Builder(mContext, "test"); - assertFalse(Notification.areRemoteViewsChanged(n1, n2)); - } - - @Test - public void testRemoteViews_layoutChange() { - RemoteViews a = mock(RemoteViews.class); - when(a.getLayoutId()).thenReturn(234); - RemoteViews b = mock(RemoteViews.class); - when(b.getLayoutId()).thenReturn(189); - - Notification.Builder n1 = new Notification.Builder(mContext, "test").setContent(a); - Notification.Builder n2 = new Notification.Builder(mContext, "test").setContent(b); - assertTrue(Notification.areRemoteViewsChanged(n1, n2)); - - n1 = new Notification.Builder(mContext, "test").setCustomBigContentView(a); - n2 = new Notification.Builder(mContext, "test").setCustomBigContentView(b); - assertTrue(Notification.areRemoteViewsChanged(n1, n2)); - - n1 = new Notification.Builder(mContext, "test").setCustomHeadsUpContentView(a); - n2 = new Notification.Builder(mContext, "test").setCustomHeadsUpContentView(b); - assertTrue(Notification.areRemoteViewsChanged(n1, n2)); - } - - @Test - public void testRemoteViews_layoutSame() { - RemoteViews a = mock(RemoteViews.class); - when(a.getLayoutId()).thenReturn(234); - RemoteViews b = mock(RemoteViews.class); - when(b.getLayoutId()).thenReturn(234); - - Notification.Builder n1 = new Notification.Builder(mContext, "test").setContent(a); - Notification.Builder n2 = new Notification.Builder(mContext, "test").setContent(b); - assertFalse(Notification.areRemoteViewsChanged(n1, n2)); - - n1 = new Notification.Builder(mContext, "test").setCustomBigContentView(a); - n2 = new Notification.Builder(mContext, "test").setCustomBigContentView(b); - assertFalse(Notification.areRemoteViewsChanged(n1, n2)); - - n1 = new Notification.Builder(mContext, "test").setCustomHeadsUpContentView(a); - n2 = new Notification.Builder(mContext, "test").setCustomHeadsUpContentView(b); - assertFalse(Notification.areRemoteViewsChanged(n1, n2)); - } - - @Test - public void testRemoteViews_sequenceChange() { - RemoteViews a = mock(RemoteViews.class); - when(a.getLayoutId()).thenReturn(234); - when(a.getSequenceNumber()).thenReturn(1); - RemoteViews b = mock(RemoteViews.class); - when(b.getLayoutId()).thenReturn(234); - when(b.getSequenceNumber()).thenReturn(2); - - Notification.Builder n1 = new Notification.Builder(mContext, "test").setContent(a); - Notification.Builder n2 = new Notification.Builder(mContext, "test").setContent(b); - assertTrue(Notification.areRemoteViewsChanged(n1, n2)); - - n1 = new Notification.Builder(mContext, "test").setCustomBigContentView(a); - n2 = new Notification.Builder(mContext, "test").setCustomBigContentView(b); - assertTrue(Notification.areRemoteViewsChanged(n1, n2)); - - n1 = new Notification.Builder(mContext, "test").setCustomHeadsUpContentView(a); - n2 = new Notification.Builder(mContext, "test").setCustomHeadsUpContentView(b); - assertTrue(Notification.areRemoteViewsChanged(n1, n2)); - } - - @Test - public void testRemoteViews_sequenceSame() { - RemoteViews a = mock(RemoteViews.class); - when(a.getLayoutId()).thenReturn(234); - when(a.getSequenceNumber()).thenReturn(1); - RemoteViews b = mock(RemoteViews.class); - when(b.getLayoutId()).thenReturn(234); - when(b.getSequenceNumber()).thenReturn(1); - - Notification.Builder n1 = new Notification.Builder(mContext, "test").setContent(a); - Notification.Builder n2 = new Notification.Builder(mContext, "test").setContent(b); - assertFalse(Notification.areRemoteViewsChanged(n1, n2)); - - n1 = new Notification.Builder(mContext, "test").setCustomBigContentView(a); - n2 = new Notification.Builder(mContext, "test").setCustomBigContentView(b); - assertFalse(Notification.areRemoteViewsChanged(n1, n2)); - - n1 = new Notification.Builder(mContext, "test").setCustomHeadsUpContentView(a); - n2 = new Notification.Builder(mContext, "test").setCustomHeadsUpContentView(b); - assertFalse(Notification.areRemoteViewsChanged(n1, n2)); - } - - @Test - public void testActionsDifferent_null() { - Notification n1 = new Notification.Builder(mContext, "test") - .build(); - Notification n2 = new Notification.Builder(mContext, "test") - .build(); - - assertFalse(Notification.areActionsVisiblyDifferent(n1, n2)); - } - - @Test - public void testActionsDifferentSame() { - PendingIntent intent = mock(PendingIntent.class); - Icon icon = mock(Icon.class); - - Notification n1 = new Notification.Builder(mContext, "test") - .addAction(new Notification.Action.Builder(icon, "TEXT 1", intent).build()) - .build(); - Notification n2 = new Notification.Builder(mContext, "test") - .addAction(new Notification.Action.Builder(icon, "TEXT 1", intent).build()) - .build(); - - assertFalse(Notification.areActionsVisiblyDifferent(n1, n2)); - } - - @Test - public void testActionsDifferentText() { - PendingIntent intent = mock(PendingIntent.class); - Icon icon = mock(Icon.class); - - Notification n1 = new Notification.Builder(mContext, "test") - .addAction(new Notification.Action.Builder(icon, "TEXT 1", intent).build()) - .build(); - Notification n2 = new Notification.Builder(mContext, "test") - .addAction(new Notification.Action.Builder(icon, "TEXT 2", intent).build()) - .build(); - - assertTrue(Notification.areActionsVisiblyDifferent(n1, n2)); - } - - @Test - public void testActionsDifferentSpannables() { - PendingIntent intent = mock(PendingIntent.class); - Icon icon = mock(Icon.class); - - Notification n1 = new Notification.Builder(mContext, "test") - .addAction(new Notification.Action.Builder(icon, - new SpannableStringBuilder().append("test1", - new StyleSpan(Typeface.BOLD), - Spanned.SPAN_EXCLUSIVE_EXCLUSIVE), - intent).build()) - .build(); - Notification n2 = new Notification.Builder(mContext, "test") - .addAction(new Notification.Action.Builder(icon, "test1", intent).build()) - .build(); - - assertFalse(Notification.areActionsVisiblyDifferent(n1, n2)); - } - - @Test - public void testActionsDifferentNumber() { - PendingIntent intent = mock(PendingIntent.class); - Icon icon = mock(Icon.class); - - Notification n1 = new Notification.Builder(mContext, "test") - .addAction(new Notification.Action.Builder(icon, "TEXT 1", intent).build()) - .build(); - Notification n2 = new Notification.Builder(mContext, "test") - .addAction(new Notification.Action.Builder(icon, "TEXT 1", intent).build()) - .addAction(new Notification.Action.Builder(icon, "TEXT 2", intent).build()) - .build(); - - assertTrue(Notification.areActionsVisiblyDifferent(n1, n2)); - } - - @Test - public void testActionsDifferentIntent() { - PendingIntent intent1 = mock(PendingIntent.class); - PendingIntent intent2 = mock(PendingIntent.class); - Icon icon = mock(Icon.class); - - Notification n1 = new Notification.Builder(mContext, "test") - .addAction(new Notification.Action.Builder(icon, "TEXT 1", intent1).build()) - .build(); - Notification n2 = new Notification.Builder(mContext, "test") - .addAction(new Notification.Action.Builder(icon, "TEXT 1", intent2).build()) - .build(); - - assertFalse(Notification.areActionsVisiblyDifferent(n1, n2)); - } - - @Test - public void testActionsIgnoresRemoteInputs() { - PendingIntent intent = mock(PendingIntent.class); - Icon icon = mock(Icon.class); - - Notification n1 = new Notification.Builder(mContext, "test") - .addAction(new Notification.Action.Builder(icon, "TEXT 1", intent) - .addRemoteInput(new RemoteInput.Builder("a") - .setChoices(new CharSequence[] {"i", "m"}) - .build()) - .build()) - .build(); - Notification n2 = new Notification.Builder(mContext, "test") - .addAction(new Notification.Action.Builder(icon, "TEXT 1", intent) - .addRemoteInput(new RemoteInput.Builder("a") - .setChoices(new CharSequence[] {"t", "m"}) - .build()) - .build()) - .build(); - - assertFalse(Notification.areActionsVisiblyDifferent(n1, n2)); - } - - @Test - public void testFreeformRemoteInputActionPair_noRemoteInput() { - PendingIntent intent = mock(PendingIntent.class); - Icon icon = mock(Icon.class); - Notification notification = new Notification.Builder(mContext, "test") - .addAction(new Notification.Action.Builder(icon, "TEXT 1", intent) - .build()) - .build(); - assertNull(notification.findRemoteInputActionPair(false)); - } - - @Test - public void testFreeformRemoteInputActionPair_hasRemoteInput() { - PendingIntent intent = mock(PendingIntent.class); - Icon icon = mock(Icon.class); - - RemoteInput remoteInput = new RemoteInput.Builder("a").build(); - - Notification.Action actionWithRemoteInput = - new Notification.Action.Builder(icon, "TEXT 1", intent) - .addRemoteInput(remoteInput) - .addRemoteInput(remoteInput) - .build(); - - Notification.Action actionWithoutRemoteInput = - new Notification.Action.Builder(icon, "TEXT 2", intent) - .build(); - - Notification notification = new Notification.Builder(mContext, "test") - .addAction(actionWithoutRemoteInput) - .addAction(actionWithRemoteInput) - .build(); - - Pair<RemoteInput, Notification.Action> remoteInputActionPair = - notification.findRemoteInputActionPair(false); - - assertNotNull(remoteInputActionPair); - assertEquals(remoteInput, remoteInputActionPair.first); - assertEquals(actionWithRemoteInput, remoteInputActionPair.second); - } - - @Test - public void testFreeformRemoteInputActionPair_requestFreeform_noFreeformRemoteInput() { - PendingIntent intent = mock(PendingIntent.class); - Icon icon = mock(Icon.class); - Notification notification = new Notification.Builder(mContext, "test") - .addAction(new Notification.Action.Builder(icon, "TEXT 1", intent) - .addRemoteInput( - new RemoteInput.Builder("a") - .setAllowFreeFormInput(false).build()) - .build()) - .build(); - assertNull(notification.findRemoteInputActionPair(true)); - } - - @Test - public void testFreeformRemoteInputActionPair_requestFreeform_hasFreeformRemoteInput() { - PendingIntent intent = mock(PendingIntent.class); - Icon icon = mock(Icon.class); - - RemoteInput remoteInput = - new RemoteInput.Builder("a").setAllowFreeFormInput(false).build(); - RemoteInput freeformRemoteInput = - new RemoteInput.Builder("b").setAllowFreeFormInput(true).build(); - - Notification.Action actionWithFreeformRemoteInput = - new Notification.Action.Builder(icon, "TEXT 1", intent) - .addRemoteInput(remoteInput) - .addRemoteInput(freeformRemoteInput) - .build(); - - Notification.Action actionWithoutFreeformRemoteInput = - new Notification.Action.Builder(icon, "TEXT 2", intent) - .addRemoteInput(remoteInput) - .build(); - - Notification notification = new Notification.Builder(mContext, "test") - .addAction(actionWithoutFreeformRemoteInput) - .addAction(actionWithFreeformRemoteInput) - .build(); - - Pair<RemoteInput, Notification.Action> remoteInputActionPair = - notification.findRemoteInputActionPair(true); - - assertNotNull(remoteInputActionPair); - assertEquals(freeformRemoteInput, remoteInputActionPair.first); - assertEquals(actionWithFreeformRemoteInput, remoteInputActionPair.second); - } -} - diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java index d5e336b1cf2f..eed32d7d815c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java @@ -40,14 +40,18 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.timeout; +import android.app.ActivityOptions; import android.app.WaitResult; import android.content.ComponentName; import android.content.Intent; import android.content.pm.ActivityInfo; +import android.os.Binder; import android.os.ConditionVariable; +import android.os.IBinder; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; import android.view.Display; @@ -308,4 +312,40 @@ public class ActivityTaskSupervisorTests extends WindowTestsBase { waitHandlerIdle(mAtm.mH); verify(mRootWindowContainer, timeout(TIMEOUT_MS)).startHomeOnEmptyDisplays("userUnlocked"); } + + /** Verifies that launch from recents sets the launch cookie on the activity. */ + @Test + public void testStartActivityFromRecents_withLaunchCookie() { + final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build(); + + IBinder launchCookie = new Binder("test_launch_cookie"); + ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchCookie(launchCookie); + SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(options.toBundle()); + + doNothing().when(mSupervisor.mService).moveTaskToFrontLocked(eq(null), eq(null), anyInt(), + anyInt(), any()); + + mSupervisor.startActivityFromRecents(-1, -1, activity.getRootTaskId(), safeOptions); + + assertThat(activity.mLaunchCookie).isEqualTo(launchCookie); + verify(mAtm).moveTaskToFrontLocked(any(), eq(null), anyInt(), anyInt(), eq(safeOptions)); + } + + /** Verifies that launch from recents doesn't set the launch cookie on the activity. */ + @Test + public void testStartActivityFromRecents_withoutLaunchCookie() { + final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build(); + + SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle( + ActivityOptions.makeBasic().toBundle()); + + doNothing().when(mSupervisor.mService).moveTaskToFrontLocked(eq(null), eq(null), anyInt(), + anyInt(), any()); + + mSupervisor.startActivityFromRecents(-1, -1, activity.getRootTaskId(), safeOptions); + + assertThat(activity.mLaunchCookie).isNull(); + verify(mAtm).moveTaskToFrontLocked(any(), eq(null), anyInt(), anyInt(), eq(safeOptions)); + } } diff --git a/services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java b/services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java index 2ae328b0d8e9..394d6e774aa1 100644 --- a/services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java +++ b/services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java @@ -19,6 +19,7 @@ package com.android.server.usb; import android.annotation.NonNull; import android.content.Context; import android.hardware.usb.UsbConfiguration; +import android.hardware.usb.UsbConstants; import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbDeviceConnection; import android.hardware.usb.UsbEndpoint; @@ -76,10 +77,10 @@ public final class UsbDirectMidiDevice implements Closeable { // event schedulers for each input port of the physical device private MidiEventScheduler[] mEventSchedulers; - // Arbitrary number for timeout to not continue sending to - // an inactive device. This number tries to balances the number - // of cycles and not being permanently stuck. - private static final int BULK_TRANSFER_TIMEOUT_MILLISECONDS = 10; + // Timeout for sending a packet to a device. + // If bulkTransfer times out, retry sending the packet up to 20 times. + private static final int BULK_TRANSFER_TIMEOUT_MILLISECONDS = 50; + private static final int BULK_TRANSFER_NUMBER_OF_RETRIES = 20; // Arbitrary number for timeout when closing a thread private static final int THREAD_JOIN_TIMEOUT_MILLISECONDS = 200; @@ -386,10 +387,15 @@ public final class UsbDirectMidiDevice implements Closeable { break; } final UsbRequest response = connectionFinal.requestWait(); - if (response != request) { - Log.w(TAG, "Unexpected response"); + if (response == null) { + Log.w(TAG, "Response is null"); break; } + if (request != response) { + Log.w(TAG, "Skipping response"); + continue; + } + int bytesRead = byteBuffer.position(); if (bytesRead > 0) { @@ -513,9 +519,47 @@ public final class UsbDirectMidiDevice implements Closeable { convertedArray.length); } - connectionFinal.bulkTransfer(endpointFinal, convertedArray, - convertedArray.length, - BULK_TRANSFER_TIMEOUT_MILLISECONDS); + boolean isInterrupted = false; + // Split the packet into multiple if they are greater than the + // endpoint's max packet size. + for (int curPacketStart = 0; + curPacketStart < convertedArray.length && + isInterrupted == false; + curPacketStart += endpointFinal.getMaxPacketSize()) { + int transferResult = -1; + int retryCount = 0; + int curPacketSize = Math.min(endpointFinal.getMaxPacketSize(), + convertedArray.length - curPacketStart); + + // Keep trying to send the packet until the result is + // successful or until the retry limit is reached. + while (transferResult < 0 && retryCount <= + BULK_TRANSFER_NUMBER_OF_RETRIES) { + transferResult = connectionFinal.bulkTransfer( + endpointFinal, + convertedArray, + curPacketStart, + curPacketSize, + BULK_TRANSFER_TIMEOUT_MILLISECONDS); + retryCount++; + + if (Thread.currentThread().interrupted()) { + Log.w(TAG, "output thread interrupted after send"); + isInterrupted = true; + break; + } + if (transferResult < 0) { + Log.d(TAG, "retrying packet. retryCount = " + + retryCount + " result = " + transferResult); + if (retryCount > BULK_TRANSFER_NUMBER_OF_RETRIES) { + Log.w(TAG, "Skipping packet because timeout"); + } + } + } + } + if (isInterrupted == true) { + break; + } eventSchedulerFinal.addEventToPool(event); } } catch (NullPointerException e) { |