Merge "Clarify two-phase constructions on two IME-related controllers" into main
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index 3213b40..abb562d 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -523,6 +523,8 @@
     private final IAppWidgetService mService;
     private final DisplayMetrics mDisplayMetrics;
 
+    private int mMaxBitmapMemory = 0;
+
     private boolean mHasPostedLegacyLists = false;
 
     /**
@@ -548,6 +550,12 @@
         if (mService == null) {
             return;
         }
+        // Allowing some buffer when estimating the maximum bitmap cache size
+        try {
+            mMaxBitmapMemory = (int) (mService.getMaxBitmapMemory() * 0.9);
+        } catch (Exception e) {
+            Log.e(TAG, "Error setting the maximum bitmap memory", e);
+        }
         BackgroundThread.getExecutor().execute(() -> {
             try {
                 mService.notifyProviderInheritance(getInstalledProvidersForPackage(mPackageName,
@@ -576,7 +584,7 @@
             final RemoteViews viewsCopy = new RemoteViews(original);
             Runnable updateWidgetWithTask = () -> {
                 try {
-                    viewsCopy.collectAllIntents().get();
+                    viewsCopy.collectAllIntents(mMaxBitmapMemory).get();
                     action.acceptOrThrow(viewsCopy);
                 } catch (Exception e) {
                     Log.e(TAG, failureMsg, e);
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 53771e3..21c9002 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -1025,8 +1025,18 @@
     /**
      * Monitor input on the specified display for gestures.
      *
+     * NOTE: New usages of Gesture Monitors are strongly discouraged. Gesture Monitors are
+     * deprecated, in favor of spy windows (see {@link LayoutParams#INPUT_FEATURE_SPY}).
+     * The spy window should be configured specifically to receive the desired events,
+     * unlike the gesture monitor which receives all events on the display.
+     *
      * @hide
+     * @deprecated
+     * @see LayoutParams#INPUT_FEATURE_SPY
+     * @see android.os.InputConfig#SPY
+     * @see #pilferPointers(IBinder)
      */
+    @Deprecated
     public InputMonitor monitorGestureInput(String name, int displayId) {
         return mGlobal.monitorGestureInput(name, displayId);
     }
diff --git a/core/java/android/view/InputMonitor.java b/core/java/android/view/InputMonitor.java
index 4996f5a..2302dc7 100644
--- a/core/java/android/view/InputMonitor.java
+++ b/core/java/android/view/InputMonitor.java
@@ -32,8 +32,11 @@
  * registered to monitor that type of event on the targeted display.
  *
  * @hide
+ * @deprecated See {@link android.hardware.input.InputManager#monitorGestureInput(String, int)}
+ *             for more details.
  */
 @DataClass(genToString = true)
+@Deprecated
 public final class InputMonitor implements Parcelable {
     private static final String TAG = "InputMonitor";
 
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 9896d98..21d6184 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -140,10 +140,12 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Set;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.Executor;
 import java.util.concurrent.TimeUnit;
@@ -1236,8 +1238,8 @@
     /**
      * @hide
      */
-    public CompletableFuture<Void> collectAllIntents() {
-        return mCollectionCache.collectAllIntentsNoComplete(this);
+    public CompletableFuture<Void> collectAllIntents(int bitmapSizeLimit) {
+        return mCollectionCache.collectAllIntentsNoComplete(this, bitmapSizeLimit);
     }
 
     private class RemoteCollectionCache {
@@ -1286,7 +1288,7 @@
         }
 
         public @NonNull CompletableFuture<Void> collectAllIntentsNoComplete(
-                @NonNull RemoteViews inViews) {
+                @NonNull RemoteViews inViews, int bitmapSizeLimit) {
             SparseArray<Intent> idToIntentMapping = new SparseArray<>();
             // Collect the number of uinque Intent (which is equal to the number of new connections
             // to make) for size allocation and exclude certain collections from being written to
@@ -1314,7 +1316,11 @@
                     ? 0
                     : remainingSize / numOfIntents;
 
-            return connectAllUniqueIntents(individualSize, idToIntentMapping);
+            int individualBitmapSizeLimit = (bitmapSizeLimit - getBitmapMemoryUsedByActions())
+                    / numOfIntents;
+
+            return connectAllUniqueIntents(individualSize, individualBitmapSizeLimit,
+                    idToIntentMapping);
         }
 
         private void collectAllIntentsInternal(@NonNull RemoteViews inViews,
@@ -1380,13 +1386,13 @@
         }
 
         private @NonNull CompletableFuture<Void> connectAllUniqueIntents(int individualSize,
-                @NonNull SparseArray<Intent> idToIntentMapping) {
+                int individualBitmapSize, @NonNull SparseArray<Intent> idToIntentMapping) {
             List<CompletableFuture<Void>> intentFutureList = new ArrayList<>();
             for (int i = 0; i < idToIntentMapping.size(); i++) {
                 String currentIntentUri = mIdToUriMapping.get(idToIntentMapping.keyAt(i));
                 Intent currentIntent = idToIntentMapping.valueAt(i);
                 intentFutureList.add(getItemsFutureFromIntentWithTimeout(currentIntent,
-                        individualSize)
+                        individualSize, individualBitmapSize)
                         .thenAccept(items -> {
                             items.setHierarchyRootData(getHierarchyRootData());
                             mUriToCollectionMapping.put(currentIntentUri, items);
@@ -1397,7 +1403,7 @@
         }
 
         private static CompletableFuture<RemoteCollectionItems> getItemsFutureFromIntentWithTimeout(
-                Intent intent, int individualSize) {
+                Intent intent, int individualSize, int individualBitmapSize) {
             if (intent == null) {
                 Log.e(LOG_TAG, "Null intent received when generating adapter future");
                 return CompletableFuture.completedFuture(new RemoteCollectionItems
@@ -1415,7 +1421,8 @@
                             RemoteCollectionItems items;
                             try {
                                 items = IRemoteViewsFactory.Stub.asInterface(iBinder)
-                                        .getRemoteCollectionItems(individualSize);
+                                        .getRemoteCollectionItems(individualSize,
+                                                individualBitmapSize);
                             } catch (RemoteException re) {
                                 items = new RemoteCollectionItems.Builder().build();
                                 Log.e(LOG_TAG, "Error getting collection items from the"
@@ -2007,7 +2014,14 @@
         }
     }
 
-    private static class BitmapCache {
+    /**
+     * @hide
+     */
+    @NonNull BitmapCache getBitmapCache() {
+        return mBitmapCache;
+    }
+
+    static class BitmapCache {
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
         ArrayList<Bitmap> mBitmaps;
         SparseIntArray mBitmapHashes;
@@ -2029,6 +2043,11 @@
             }
         }
 
+        BitmapCache(BitmapCache other) {
+            mBitmaps = new ArrayList<>(other.mBitmaps);
+            mBitmapHashes = other.mBitmapHashes.clone();
+        }
+
         public int getBitmapId(Bitmap b) {
             if (b == null) {
                 return -1;
@@ -2071,6 +2090,12 @@
             }
             return mBitmapMemory;
         }
+
+        public void mergeWithCache(BitmapCache other) {
+            for (int i = 0; i < other.mBitmaps.size(); i++) {
+                getBitmapId(other.mBitmaps.get(i));
+            }
+        }
     }
 
     private class BitmapReflectionAction extends Action {
@@ -7343,6 +7368,38 @@
         return true;
     }
 
+    private int getBitmapMemoryUsedByActions() {
+        Set<Integer> bitmapIdSet = getBitmapIdsUsedByActions(new HashSet<>());
+        int result = 0;
+        for (int bitmapId: bitmapIdSet) {
+            result += mBitmapCache.getBitmapForId(bitmapId).getAllocationByteCount();
+        }
+
+        return result;
+    }
+
+    private Set<Integer> getBitmapIdsUsedByActions(@NonNull Set<Integer> intSet) {
+        if (hasSizedRemoteViews()) {
+            for (RemoteViews views: mSizedRemoteViews) {
+                views.getBitmapIdsUsedByActions(intSet);
+            }
+        } else if (hasLandscapeAndPortraitLayouts()) {
+            mLandscape.getBitmapIdsUsedByActions(intSet);
+            mPortrait.getBitmapIdsUsedByActions(intSet);
+        } else if (mActions != null) {
+            for (Action action: mActions) {
+                if (action instanceof ViewGroupActionAdd vgaa
+                        && vgaa.mNestedViews != null) {
+                    vgaa.mNestedViews.getBitmapIdsUsedByActions(intSet);
+                } else if (action instanceof BitmapReflectionAction bitmapAction) {
+                    intSet.add(bitmapAction.mBitmapId);
+                }
+            }
+        }
+
+        return intSet;
+    }
+
     /** Representation of a fixed list of items to be displayed in a RemoteViews collection. */
     public static final class RemoteCollectionItems implements Parcelable {
         private final long[] mIds;
diff --git a/core/java/android/widget/RemoteViewsService.java b/core/java/android/widget/RemoteViewsService.java
index c79eac6..4045d42 100644
--- a/core/java/android/widget/RemoteViewsService.java
+++ b/core/java/android/widget/RemoteViewsService.java
@@ -128,7 +128,8 @@
         /**
          * @hide
          */
-        default RemoteViews.RemoteCollectionItems getRemoteCollectionItems(int capSize) {
+        default RemoteViews.RemoteCollectionItems getRemoteCollectionItems(int capSize,
+                int capBitmapSize) {
             RemoteViews.RemoteCollectionItems items = new RemoteViews.RemoteCollectionItems
                     .Builder().build();
             Parcel capSizeTestParcel = Parcel.obtain();
@@ -138,6 +139,7 @@
             try {
                 RemoteViews.RemoteCollectionItems.Builder itemsBuilder =
                         new RemoteViews.RemoteCollectionItems.Builder();
+                RemoteViews.BitmapCache testBitmapCache = null;
                 onDataSetChanged();
 
                 itemsBuilder.setHasStableIds(hasStableIds());
@@ -150,6 +152,15 @@
                     if (capSizeTestParcel.dataSize() > capSize) {
                         break;
                     }
+                    if (testBitmapCache == null) {
+                        testBitmapCache = new RemoteViews.BitmapCache(currentView.getBitmapCache());
+                    } else {
+                        testBitmapCache.mergeWithCache(currentView.getBitmapCache());
+                    }
+                    if (testBitmapCache.getBitmapMemory() >= capBitmapSize) {
+                        break;
+                    }
+
                     itemsBuilder.addItem(currentItemId, currentView);
                 }
 
@@ -266,11 +277,12 @@
         }
 
         @Override
-        public RemoteViews.RemoteCollectionItems getRemoteCollectionItems(int capSize) {
+        public RemoteViews.RemoteCollectionItems getRemoteCollectionItems(int capSize,
+                int capBitmapSize) {
             RemoteViews.RemoteCollectionItems items = new RemoteViews.RemoteCollectionItems
                     .Builder().build();
             try {
-                items = mFactory.getRemoteCollectionItems(capSize);
+                items = mFactory.getRemoteCollectionItems(capSize, capBitmapSize);
             } catch (Exception ex) {
                 Thread t = Thread.currentThread();
                 Thread.getDefaultUncaughtExceptionHandler().uncaughtException(t, ex);
diff --git a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
index e55cdef..b5930e1 100644
--- a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
+++ b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
@@ -72,6 +72,7 @@
     boolean bindRemoteViewsService(String callingPackage, int appWidgetId, in Intent intent,
             IApplicationThread caller, IBinder token, IServiceConnection connection, long flags);
     void notifyProviderInheritance(in ComponentName[] componentNames);
+    int getMaxBitmapMemory();
 
     @UnsupportedAppUsage
     int[] getAppWidgetIds(in ComponentName providerComponent);
diff --git a/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl b/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl
index 1d0e972..7e8724d 100644
--- a/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl
+++ b/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl
@@ -39,6 +39,6 @@
     boolean hasStableIds();
     @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     boolean isCreated();
-    RemoteViews.RemoteCollectionItems getRemoteCollectionItems(int capSize);
+    RemoteViews.RemoteCollectionItems getRemoteCollectionItems(int capSize, int capBitmapSize);
 }
 
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AidlTestUtils.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AidlTestUtils.java
index f3d4ccf..3fb1554 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AidlTestUtils.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AidlTestUtils.java
@@ -15,6 +15,7 @@
  */
 package com.android.server.broadcastradio.aidl;
 
+import android.annotation.Nullable;
 import android.hardware.broadcastradio.IdentifierType;
 import android.hardware.broadcastradio.Metadata;
 import android.hardware.broadcastradio.ProgramIdentifier;
@@ -110,20 +111,25 @@
 
     static ProgramInfo makeHalProgramInfo(
             android.hardware.broadcastradio.ProgramSelector hwSel,
-            ProgramIdentifier logicallyTunedTo, ProgramIdentifier physicallyTunedTo,
-            int hwSignalQuality) {
+            @Nullable ProgramIdentifier logicallyTunedTo,
+            @Nullable ProgramIdentifier physicallyTunedTo, int hwSignalQuality) {
         return makeHalProgramInfo(hwSel, logicallyTunedTo, physicallyTunedTo, hwSignalQuality,
                 new ProgramIdentifier[]{}, new Metadata[]{});
     }
 
     static ProgramInfo makeHalProgramInfo(
             android.hardware.broadcastradio.ProgramSelector hwSel,
-            ProgramIdentifier logicallyTunedTo, ProgramIdentifier physicallyTunedTo,
-            int hwSignalQuality, ProgramIdentifier[] relatedContent, Metadata[] metadata) {
+            @Nullable ProgramIdentifier logicallyTunedTo,
+            @Nullable ProgramIdentifier physicallyTunedTo, int hwSignalQuality,
+            ProgramIdentifier[] relatedContent, Metadata[] metadata) {
         ProgramInfo hwInfo = new ProgramInfo();
         hwInfo.selector = hwSel;
-        hwInfo.logicallyTunedTo = logicallyTunedTo;
-        hwInfo.physicallyTunedTo = physicallyTunedTo;
+        if (logicallyTunedTo != null) {
+            hwInfo.logicallyTunedTo = logicallyTunedTo;
+        }
+        if (physicallyTunedTo != null) {
+            hwInfo.physicallyTunedTo = physicallyTunedTo;
+        }
         hwInfo.signalQuality = hwSignalQuality;
         hwInfo.relatedContent = relatedContent;
         hwInfo.metadata = metadata;
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java
index 5a08acd..bdc4d25 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java
@@ -562,13 +562,29 @@
     @Test
     public void programInfoFromHalProgramInfo_withInvalidDabProgramInfo() {
         android.hardware.broadcastradio.ProgramSelector invalidHalDabSelector =
-                AidlTestUtils.makeHalSelector(TEST_HAL_DAB_SID_EXT_ID,
-                new ProgramIdentifier[]{TEST_HAL_DAB_ENSEMBLE_ID, TEST_HAL_DAB_FREQUENCY_ID});
+                AidlTestUtils.makeHalSelector(TEST_HAL_DAB_ENSEMBLE_ID,
+                        new ProgramIdentifier[]{TEST_HAL_DAB_FREQUENCY_ID});
+        ProgramInfo halProgramInfo = AidlTestUtils.makeHalProgramInfo(invalidHalDabSelector,
+                /* logicallyTunedTo= */ null, /* physicallyTunedTo= */ null,
+                TEST_SIGNAL_QUALITY);
+
+        RadioManager.ProgramInfo programInfo =
+                ConversionUtils.programInfoFromHalProgramInfo(halProgramInfo);
+
+        expect.withMessage("Invalid DAB program info with incorrect type of physically tuned to id")
+                .that(programInfo).isNull();
+    }
+
+    @Test
+    public void tunedProgramInfoFromHalProgramInfo_withInvalidDabProgramInfo() {
+        android.hardware.broadcastradio.ProgramSelector invalidHalDabSelector =
+                AidlTestUtils.makeHalSelector(TEST_HAL_DAB_SID_EXT_ID, new ProgramIdentifier[]{
+                        TEST_HAL_DAB_ENSEMBLE_ID, TEST_HAL_DAB_FREQUENCY_ID});
         ProgramInfo halProgramInfo = AidlTestUtils.makeHalProgramInfo(invalidHalDabSelector,
                 TEST_HAL_DAB_SID_EXT_ID, TEST_HAL_DAB_ENSEMBLE_ID, TEST_SIGNAL_QUALITY);
 
         RadioManager.ProgramInfo programInfo =
-                ConversionUtils.programInfoFromHalProgramInfo(halProgramInfo);
+                ConversionUtils.tunedProgramInfoFromHalProgramInfo(halProgramInfo);
 
         expect.withMessage("Invalid DAB program info with incorrect type of physically tuned to id")
                 .that(programInfo).isNull();
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ProgramInfoCacheTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ProgramInfoCacheTest.java
index d64fcaf..b1856719 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ProgramInfoCacheTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ProgramInfoCacheTest.java
@@ -189,13 +189,16 @@
 
     @Test
     public void updateFromHalProgramListChunk_withInvalidChunk() {
-        RadioManager.ProgramInfo invalidDabInfo = AidlTestUtils.makeProgramInfo(TEST_DAB_SELECTOR,
-                TEST_DAB_DMB_SID_EXT_ID, TEST_DAB_ENSEMBLE_ID, TEST_SIGNAL_QUALITY);
+        ProgramInfo invalidHalDabInfo = AidlTestUtils.makeHalProgramInfo(
+                AidlTestUtils.makeHalSelector(
+                        ConversionUtils.identifierToHalProgramIdentifier(TEST_DAB_ENSEMBLE_ID),
+                        new ProgramIdentifier[]{}), /* logicallyTunedTo= */ null,
+                /* physicallyTunedTo= */ null, TEST_SIGNAL_QUALITY);
         ProgramInfoCache cache = new ProgramInfoCache(/* filter= */ null,
                 /* complete= */ false);
         ProgramListChunk chunk = AidlTestUtils.makeHalChunk(/* purge= */ false,
-                /* complete= */ true, new ProgramInfo[]{AidlTestUtils.programInfoToHalProgramInfo(
-                        invalidDabInfo)}, new ProgramIdentifier[]{});
+                /* complete= */ true, new ProgramInfo[]{invalidHalDabInfo},
+                new ProgramIdentifier[]{});
 
         cache.updateFromHalProgramListChunk(chunk);
 
@@ -447,10 +450,10 @@
                 /* complete= */ false, TEST_FM_INFO, TEST_RDS_INFO, TEST_DAB_INFO);
         ProgramInfo[] halModified = new android.hardware.broadcastradio.ProgramInfo[1];
         halModified[0] = AidlTestUtils.makeHalProgramInfo(
-                ConversionUtils.programSelectorToHalProgramSelector(TEST_DAB_SELECTOR_ALTERNATIVE),
-                ConversionUtils.identifierToHalProgramIdentifier(TEST_DAB_FREQUENCY_ID_ALTERNATIVE),
-                ConversionUtils.identifierToHalProgramIdentifier(TEST_DAB_FREQUENCY_ID_ALTERNATIVE),
-                TEST_SIGNAL_QUALITY);
+                AidlTestUtils.makeHalSelector(
+                        ConversionUtils.identifierToHalProgramIdentifier(TEST_DAB_ENSEMBLE_ID),
+                        new ProgramIdentifier[]{}), /* logicallyTunedTo= */ null,
+                /* physicallyTunedTo= */ null, TEST_SIGNAL_QUALITY);
         ProgramIdentifier[] halRemoved = new android.hardware.broadcastradio.ProgramIdentifier[1];
         halRemoved[0] = new android.hardware.broadcastradio.ProgramIdentifier();
         ProgramListChunk halChunk = AidlTestUtils.makeHalChunk(/* purge= */ false,
diff --git a/core/tests/coretests/src/android/widget/RemoteViewsAdapterTest.java b/core/tests/coretests/src/android/widget/RemoteViewsAdapterTest.java
index 6ab77dc..534420e 100644
--- a/core/tests/coretests/src/android/widget/RemoteViewsAdapterTest.java
+++ b/core/tests/coretests/src/android/widget/RemoteViewsAdapterTest.java
@@ -355,7 +355,8 @@
         }
 
         @Override
-        public RemoteViews.RemoteCollectionItems getRemoteCollectionItems(int capSize) {
+        public RemoteViews.RemoteCollectionItems getRemoteCollectionItems(int capSize,
+                int capBitmapSize) {
             RemoteViews.RemoteCollectionItems.Builder itemsBuilder =
                     new RemoteViews.RemoteCollectionItems.Builder();
             itemsBuilder.setHasStableIds(hasStableIds())
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesPresetsController.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesPresetsController.java
index 02fa003..f81124e 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesPresetsController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesPresetsController.java
@@ -220,7 +220,8 @@
         if (mActiveHearingDevice == null) {
             return emptyList();
         }
-        return mHapClientProfile.getAllPresetInfo(mActiveHearingDevice.getDevice());
+        return mHapClientProfile.getAllPresetInfo(mActiveHearingDevice.getDevice()).stream().filter(
+                BluetoothHapPresetInfo::isAvailable).toList();
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/ambient/touch/TouchMonitor.java b/packages/SystemUI/src/com/android/systemui/ambient/touch/TouchMonitor.java
index 61b4401..4035e95 100644
--- a/packages/SystemUI/src/com/android/systemui/ambient/touch/TouchMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/TouchMonitor.java
@@ -323,12 +323,15 @@
 
         // When we stop monitoring touches, we must ensure that all active touch sessions and
         // descendants informed of the removal so any cleanup for active tracking can proceed.
-        mMainExecutor.execute(() -> mActiveTouchSessions.forEach(touchSession -> {
-            while (touchSession != null) {
-                touchSession.onRemoved();
-                touchSession = touchSession.getPredecessor();
-            }
-        }));
+        mMainExecutor.execute(() -> {
+            mActiveTouchSessions.forEach(touchSession -> {
+                while (touchSession != null) {
+                    touchSession.onRemoved();
+                    touchSession = touchSession.getPredecessor();
+                }
+            });
+            mActiveTouchSessions.clear();
+        });
 
         mCurrentInputSession.dispose();
         mCurrentInputSession = null;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java
index 4118c90..64c162f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java
@@ -212,6 +212,40 @@
     }
 
     @Test
+    public void testSessionResetOnLifecycle() {
+        final TouchHandler touchHandler = createTouchHandler();
+        final Rect touchArea = new Rect(4, 4, 8 , 8);
+
+        doAnswer(invocation -> {
+            final Region region = (Region) invocation.getArguments()[1];
+            region.set(touchArea);
+            return null;
+        }).when(touchHandler).getTouchInitiationRegion(any(), any(), any());
+
+        final Environment environment = new Environment(Stream.of(touchHandler)
+                .collect(Collectors.toCollection(HashSet::new)), mKosmos);
+
+        // Ensure touch outside specified region is not delivered.
+        final MotionEvent initialEvent = Mockito.mock(MotionEvent.class);
+
+        // Make sure touch inside region causes session start.
+        when(initialEvent.getX()).thenReturn(5.0f);
+        when(initialEvent.getY()).thenReturn(5.0f);
+        environment.publishInputEvent(initialEvent);
+        verify(touchHandler).onSessionStart(any());
+
+        Mockito.clearInvocations(touchHandler);
+
+        // Reset lifecycle, forcing monitoring to be reset
+        environment.updateLifecycle(Lifecycle.State.STARTED);
+        environment.updateLifecycle(Lifecycle.State.RESUMED);
+        environment.executeAll();
+
+        environment.publishInputEvent(initialEvent);
+        verify(touchHandler).onSessionStart(any());
+    }
+
+    @Test
     @EnableFlags(Flags.FLAG_AMBIENT_TOUCH_MONITOR_LISTEN_TO_DISPLAY_CHANGES)
     public void testConfigurationListenerUpdatesBounds() {
         final TouchHandler touchHandler = createTouchHandler();
@@ -332,7 +366,6 @@
         }
     }
 
-
     @Test
     public void testNoActiveSessionWhenHandlerDisabled() {
         final TouchHandler touchHandler = Mockito.mock(TouchHandler.class);
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index d7649dc..76f6d17 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -345,6 +345,11 @@
         LocalServices.addService(AppWidgetManagerInternal.class, new AppWidgetManagerLocal());
     }
 
+    @Override
+    public int getMaxBitmapMemory() {
+        return mMaxWidgetBitmapMemory;
+    }
+
     void systemServicesReady() {
         mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
         mAppOpsManagerInternal = LocalServices.getService(AppOpsManagerInternal.class);
diff --git a/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java b/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java
index 9467d6f..a3c68f9 100644
--- a/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java
+++ b/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java
@@ -590,15 +590,9 @@
                 || isVendorIdentifierType(id.type);
     }
 
-    private static boolean isValidHalProgramInfo(ProgramInfo info) {
-        return isValidHalProgramSelector(info.selector)
-                && isValidLogicallyTunedTo(info.logicallyTunedTo)
-                && isValidPhysicallyTunedTo(info.physicallyTunedTo);
-    }
-
     @Nullable
     static RadioManager.ProgramInfo programInfoFromHalProgramInfo(ProgramInfo info) {
-        if (!isValidHalProgramInfo(info)) {
+        if (!isValidHalProgramSelector(info.selector)) {
             return null;
         }
         Collection<ProgramSelector.Identifier> relatedContent = new ArrayList<>();
@@ -624,6 +618,15 @@
         );
     }
 
+    @Nullable
+    static RadioManager.ProgramInfo tunedProgramInfoFromHalProgramInfo(ProgramInfo info) {
+        if (!isValidLogicallyTunedTo(info.logicallyTunedTo)
+                || !isValidPhysicallyTunedTo(info.physicallyTunedTo)) {
+            return null;
+        }
+        return programInfoFromHalProgramInfo(info);
+    }
+
     static ProgramFilter filterToHalProgramFilter(@Nullable ProgramList.Filter filter) {
         if (filter == null) {
             filter = new ProgramList.Filter();
@@ -686,8 +689,10 @@
         if (!programSelectorMeetsSdkVersionRequirement(info.getSelector(), uid)) {
             return false;
         }
-        if (!identifierMeetsSdkVersionRequirement(info.getLogicallyTunedTo(), uid)
-                || !identifierMeetsSdkVersionRequirement(info.getPhysicallyTunedTo(), uid)) {
+        if ((info.getLogicallyTunedTo() != null
+                && !identifierMeetsSdkVersionRequirement(info.getLogicallyTunedTo(), uid))
+                || (info.getPhysicallyTunedTo() != null
+                && !identifierMeetsSdkVersionRequirement(info.getPhysicallyTunedTo(), uid))) {
             return false;
         }
         Iterator<ProgramSelector.Identifier> relatedContentIt = info.getRelatedContent().iterator();
diff --git a/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java b/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java
index 03e347a..4edd441 100644
--- a/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java
+++ b/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java
@@ -121,7 +121,7 @@
         public void onCurrentProgramInfoChanged(ProgramInfo halProgramInfo) {
             fireLater(() -> {
                 RadioManager.ProgramInfo currentProgramInfo =
-                        ConversionUtils.programInfoFromHalProgramInfo(halProgramInfo);
+                        ConversionUtils.tunedProgramInfoFromHalProgramInfo(halProgramInfo);
                 Objects.requireNonNull(currentProgramInfo,
                         "Program info from AIDL HAL is invalid");
                 synchronized (mLock) {
diff --git a/services/core/java/com/android/server/wm/RemoteDisplayChangeController.java b/services/core/java/com/android/server/wm/RemoteDisplayChangeController.java
index 4d3b95b..c22b07a 100644
--- a/services/core/java/com/android/server/wm/RemoteDisplayChangeController.java
+++ b/services/core/java/com/android/server/wm/RemoteDisplayChangeController.java
@@ -104,10 +104,9 @@
 
         final IDisplayChangeWindowCallback remoteCallback = createCallback(callback);
         try {
-            mService.mH.removeCallbacks(mTimeoutRunnable);
-            mService.mH.postDelayed(mTimeoutRunnable, REMOTE_DISPLAY_CHANGE_TIMEOUT_MS);
             mService.mDisplayChangeController.onDisplayChange(mDisplayContent.mDisplayId,
                     fromRotation, toRotation, newDisplayAreaInfo, remoteCallback);
+            mService.mH.postDelayed(mTimeoutRunnable, callback, REMOTE_DISPLAY_CHANGE_TIMEOUT_MS);
             return true;
         } catch (RemoteException e) {
             Slog.e(TAG, "Exception while dispatching remote display-change", e);
@@ -118,6 +117,7 @@
 
     private void onContinueTimedOut() {
         Slog.e(TAG, "RemoteDisplayChange timed-out, UI might get messed-up after this.");
+        mService.mH.removeCallbacks(mTimeoutRunnable);
         // timed-out, so run all continue callbacks and clear the list
         synchronized (mService.mGlobalLock) {
             for (int i = 0; i < mCallbacks.size(); ++i) {
@@ -172,9 +172,6 @@
             // The "toIndex" is exclusive, so it needs +1 to clear the current calling callback.
             mCallbacks.subList(0, idx + 1).clear();
             final boolean completed = mCallbacks.isEmpty();
-            if (completed) {
-                mService.mH.removeCallbacks(mTimeoutRunnable);
-            }
             callback.onContinueRemoteDisplayChange(transaction);
             if (completed) {
                 onCompleted();
@@ -198,6 +195,7 @@
                             }
                             mService.mH.post(() -> RemoteDisplayChangeController.this
                                     .continueDisplayChange(callback, t));
+                            mService.mH.removeCallbacks(mTimeoutRunnable, callback);
                         }
                     }
                 };
diff --git a/tests/Input/src/com/android/test/input/UnresponsiveGestureMonitorActivity.kt b/tests/Input/src/com/android/test/input/UnresponsiveGestureMonitorActivity.kt
index 63782f1..1842f0a 100644
--- a/tests/Input/src/com/android/test/input/UnresponsiveGestureMonitorActivity.kt
+++ b/tests/Input/src/com/android/test/input/UnresponsiveGestureMonitorActivity.kt
@@ -14,6 +14,9 @@
  * limitations under the License.
  */
 
+// InputMonitor is deprecated, but we still need to test it.
+@file:Suppress("DEPRECATION")
+
 package com.android.test.input
 
 import android.app.Activity
@@ -43,6 +46,7 @@
     }
     private lateinit var mInputEventReceiver: InputEventReceiver
     private lateinit var mInputMonitor: InputMonitor
+
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         val inputManager = checkNotNull(getSystemService(InputManager::class.java))