Merge "Enforce persisted snoozed notifications limits" into sc-dev
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
index d262412..6e64b1a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
@@ -233,6 +233,11 @@
     }
 
     @Override
+    public void onNullBinding(ComponentName name) {
+        setBindService(false);
+    }
+
+    @Override
     public void onServiceDisconnected(ComponentName name) {
         if (DEBUG) Log.d(TAG, "onServiceDisconnected " + name);
         handleDeath();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java
index 0a42865..7323a4a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java
@@ -22,13 +22,16 @@
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.ComponentName;
+import android.content.Context;
 import android.content.Intent;
+import android.content.ServiceConnection;
 import android.content.pm.PackageInfo;
 import android.content.pm.ServiceInfo;
 import android.net.Uri;
@@ -51,7 +54,7 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.Mockito;
+import org.mockito.ArgumentCaptor;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -59,10 +62,10 @@
     private static final int TEST_FAIL_TIMEOUT = 5000;
 
     private final PackageManagerAdapter mMockPackageManagerAdapter =
-            Mockito.mock(PackageManagerAdapter.class);
+            mock(PackageManagerAdapter.class);
     private final BroadcastDispatcher mMockBroadcastDispatcher =
-            Mockito.mock(BroadcastDispatcher.class);
-    private final IQSTileService.Stub mMockTileService = Mockito.mock(IQSTileService.Stub.class);
+            mock(BroadcastDispatcher.class);
+    private final IQSTileService.Stub mMockTileService = mock(IQSTileService.Stub.class);
     private ComponentName mTileServiceComponentName;
     private Intent mTileServiceIntent;
     private UserHandle mUser;
@@ -87,7 +90,7 @@
         mThread.start();
         mHandler = Handler.createAsync(mThread.getLooper());
         mStateManager = new TileLifecycleManager(mHandler, mContext,
-                Mockito.mock(IQSService.class), new Tile(),
+                mock(IQSService.class), new Tile(),
                 mTileServiceIntent,
                 mUser,
                 mMockPackageManagerAdapter,
@@ -247,4 +250,26 @@
     public void testToggleableTile() throws Exception {
         assertTrue(mStateManager.isToggleableTile());
     }
+
+    @Test
+    public void testNullBindingCallsUnbind() {
+        Context mockContext = mock(Context.class);
+        // Binding has to succeed
+        when(mockContext.bindServiceAsUser(any(), any(), anyInt(), any())).thenReturn(true);
+        TileLifecycleManager manager = new TileLifecycleManager(mHandler, mockContext,
+                mock(IQSService.class),
+                new Tile(),
+                mTileServiceIntent,
+                mUser,
+                mMockPackageManagerAdapter,
+                mMockBroadcastDispatcher);
+
+        manager.setBindService(true);
+
+        ArgumentCaptor<ServiceConnection> captor = ArgumentCaptor.forClass(ServiceConnection.class);
+        verify(mockContext).bindServiceAsUser(any(), captor.capture(), anyInt(), any());
+
+        captor.getValue().onNullBinding(mTileServiceComponentName);
+        verify(mockContext).unbindService(captor.getValue());
+    }
 }
diff --git a/services/autofill/java/com/android/server/autofill/Helper.java b/services/autofill/java/com/android/server/autofill/Helper.java
index 48113a8..5f36496 100644
--- a/services/autofill/java/com/android/server/autofill/Helper.java
+++ b/services/autofill/java/com/android/server/autofill/Helper.java
@@ -23,7 +23,10 @@
 import android.app.assist.AssistStructure;
 import android.app.assist.AssistStructure.ViewNode;
 import android.app.assist.AssistStructure.WindowNode;
+import android.app.slice.Slice;
+import android.app.slice.SliceItem;
 import android.content.ComponentName;
+import android.graphics.drawable.Icon;
 import android.metrics.LogMaker;
 import android.service.autofill.Dataset;
 import android.service.autofill.InternalSanitizer;
@@ -47,7 +50,6 @@
 import java.util.Arrays;
 import java.util.concurrent.atomic.AtomicBoolean;
 
-
 public final class Helper {
 
     private static final String TAG = "AutofillHelper";
@@ -85,7 +87,7 @@
         final AtomicBoolean permissionsOk = new AtomicBoolean(true);
 
         rView.visitUris(uri -> {
-            int uriOwnerId = android.content.ContentProvider.getUserIdFromUri(uri);
+            int uriOwnerId = android.content.ContentProvider.getUserIdFromUri(uri, userId);
             boolean allowed = uriOwnerId == userId;
             permissionsOk.set(allowed && permissionsOk.get());
         });
@@ -117,6 +119,48 @@
         return (ok ? rView : null);
     }
 
+    /**
+     * Checks the URI permissions of the icon in the slice, to see if the current userId is able to
+     * access it.
+     *
+     * <p>Returns null if slice contains user inaccessible icons
+     *
+     * <p>TODO: instead of returning a null Slice when the current userId cannot access an icon,
+     * return a reconstructed Slice without the icons. This is currently non-trivial since there are
+     * no public methods to generically add SliceItems to Slices
+     */
+    public static @Nullable Slice sanitizeSlice(Slice slice) {
+        if (slice == null) {
+            return null;
+        }
+
+        int userId = ActivityManager.getCurrentUser();
+
+        // Recontruct the Slice, filtering out bad icons
+        for (SliceItem sliceItem : slice.getItems()) {
+            if (!sliceItem.getFormat().equals(SliceItem.FORMAT_IMAGE)) {
+                // Not an image slice
+                continue;
+            }
+
+            Icon icon = sliceItem.getIcon();
+            if (icon.getType() !=  Icon.TYPE_URI
+                    && icon.getType() != Icon.TYPE_URI_ADAPTIVE_BITMAP) {
+                // No URIs to sanitize
+                continue;
+            }
+
+            int iconUriId = android.content.ContentProvider.getUserIdFromUri(icon.getUri(), userId);
+
+            if (iconUriId != userId) {
+                Slog.w(TAG, "sanitizeSlice() user: " + userId + " cannot access icons in Slice");
+                return null;
+            }
+        }
+
+        return slice;
+    }
+
 
     @Nullable
     static AutofillId[] toArray(@Nullable ArraySet<AutofillId> set) {
diff --git a/services/autofill/java/com/android/server/autofill/ui/RemoteInlineSuggestionViewConnector.java b/services/autofill/java/com/android/server/autofill/ui/RemoteInlineSuggestionViewConnector.java
index 46d435d..83caf74 100644
--- a/services/autofill/java/com/android/server/autofill/ui/RemoteInlineSuggestionViewConnector.java
+++ b/services/autofill/java/com/android/server/autofill/ui/RemoteInlineSuggestionViewConnector.java
@@ -27,6 +27,7 @@
 import android.util.Slog;
 
 import com.android.server.LocalServices;
+import com.android.server.autofill.Helper;
 import com.android.server.autofill.RemoteInlineSuggestionRenderService;
 import com.android.server.inputmethod.InputMethodManagerInternal;
 
@@ -39,12 +40,9 @@
 final class RemoteInlineSuggestionViewConnector {
     private static final String TAG = RemoteInlineSuggestionViewConnector.class.getSimpleName();
 
-    @Nullable
-    private final RemoteInlineSuggestionRenderService mRemoteRenderService;
-    @NonNull
-    private final InlinePresentation mInlinePresentation;
-    @Nullable
-    private final IBinder mHostInputToken;
+    @Nullable private final RemoteInlineSuggestionRenderService mRemoteRenderService;
+    @NonNull private final InlinePresentation mInlinePresentation;
+    @Nullable private final IBinder mHostInputToken;
     private final int mDisplayId;
     private final int mUserId;
     private final int mSessionId;
@@ -78,8 +76,12 @@
      *
      * @return true if the call is made to the remote renderer service, false otherwise.
      */
-    public boolean renderSuggestion(int width, int height,
-            @NonNull IInlineSuggestionUiCallback callback) {
+    public boolean renderSuggestion(
+            int width, int height, @NonNull IInlineSuggestionUiCallback callback) {
+        if (Helper.sanitizeSlice(mInlinePresentation.getSlice()) == null) {
+            if (sDebug) Slog.d(TAG, "Skipped rendering inline suggestion.");
+            return false;
+        }
         if (mRemoteRenderService != null) {
             if (sDebug) Slog.d(TAG, "Request to recreate the UI");
             mRemoteRenderService.renderSuggestion(callback, mInlinePresentation, width, height,
diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
index 49df4a8..39f5923 100644
--- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
@@ -427,7 +427,8 @@
                     }
                     final BatchUpdates batchUpdates = pair.second;
                     // First apply the updates...
-                    final RemoteViews templateUpdates = batchUpdates.getUpdates();
+                    final RemoteViews templateUpdates =
+                            Helper.sanitizeRemoteView(batchUpdates.getUpdates());
                     if (templateUpdates != null) {
                         if (sDebug) Slog.d(TAG, "Applying template updates for batch update #" + i);
                         templateUpdates.reapply(context, customSubtitleView);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index bcb5e0d..298f5f1 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -1325,29 +1325,38 @@
 
             final long origId = Binder.clearCallingIdentity();
             // TODO(b/64750076): Check if calling pid should really be -1.
-            final int res = getActivityStartController()
-                    .obtainStarter(intent, "startNextMatchingActivity")
-                    .setCaller(r.app.getThread())
-                    .setResolvedType(r.resolvedType)
-                    .setActivityInfo(aInfo)
-                    .setResultTo(resultTo != null ? resultTo.appToken : null)
-                    .setResultWho(resultWho)
-                    .setRequestCode(requestCode)
-                    .setCallingPid(-1)
-                    .setCallingUid(r.launchedFromUid)
-                    .setCallingPackage(r.launchedFromPackage)
-                    .setCallingFeatureId(r.launchedFromFeatureId)
-                    .setRealCallingPid(-1)
-                    .setRealCallingUid(r.launchedFromUid)
-                    .setActivityOptions(options)
-                    .execute();
-            Binder.restoreCallingIdentity(origId);
-
-            r.finishing = wasFinishing;
-            if (res != ActivityManager.START_SUCCESS) {
-                return false;
+            try {
+                if (options == null) {
+                    options = new SafeActivityOptions(ActivityOptions.makeBasic());
+                }
+                // Fixes b/230492947
+                // Prevents background activity launch through #startNextMatchingActivity
+                // An activity going into the background could still go back to the foreground
+                // if the intent used matches both:
+                // - the activity in the background
+                // - a second activity.
+                options.getOptions(r).setAvoidMoveToFront();
+                final int res = getActivityStartController()
+                        .obtainStarter(intent, "startNextMatchingActivity")
+                        .setCaller(r.app.getThread())
+                        .setResolvedType(r.resolvedType)
+                        .setActivityInfo(aInfo)
+                        .setResultTo(resultTo != null ? resultTo.appToken : null)
+                        .setResultWho(resultWho)
+                        .setRequestCode(requestCode)
+                        .setCallingPid(-1)
+                        .setCallingUid(r.launchedFromUid)
+                        .setCallingPackage(r.launchedFromPackage)
+                        .setCallingFeatureId(r.launchedFromFeatureId)
+                        .setRealCallingPid(-1)
+                        .setRealCallingUid(r.launchedFromUid)
+                        .setActivityOptions(options)
+                        .execute();
+                r.finishing = wasFinishing;
+                return res == ActivityManager.START_SUCCESS;
+            } finally {
+                Binder.restoreCallingIdentity(origId);
             }
-            return true;
         }
     }