Merge "Validate pending intents"
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 269bd20..23798c0 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -421,6 +421,7 @@
     private IActivityManager mAm;
     private ActivityTaskManagerInternal mAtm;
     private ActivityManager mActivityManager;
+    private ActivityManagerInternal mAmi;
     private IPackageManager mPackageManager;
     private PackageManager mPackageManagerClient;
     AudioManager mAudioManager;
@@ -1897,7 +1898,7 @@
             DevicePolicyManagerInternal dpm, IUriGrantsManager ugm,
             UriGrantsManagerInternal ugmInternal, AppOpsManager appOps, UserManager userManager,
             NotificationHistoryManager historyManager, StatsManager statsManager,
-            TelephonyManager telephonyManager) {
+            TelephonyManager telephonyManager, ActivityManagerInternal ami) {
         mHandler = handler;
         Resources resources = getContext().getResources();
         mMaxPackageEnqueueRate = Settings.Global.getFloat(getContext().getContentResolver(),
@@ -1919,6 +1920,7 @@
         mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
         mCompanionManager = companionManager;
         mActivityManager = activityManager;
+        mAmi = ami;
         mDeviceIdleManager = getContext().getSystemService(DeviceIdleManager.class);
         mDpm = dpm;
         mUm = userManager;
@@ -2197,7 +2199,8 @@
                 new NotificationHistoryManager(getContext(), handler),
                 mStatsManager = (StatsManager) getContext().getSystemService(
                         Context.STATS_MANAGER),
-                getContext().getSystemService(TelephonyManager.class));
+                getContext().getSystemService(TelephonyManager.class),
+                LocalServices.getService(ActivityManagerInternal.class));
 
         publishBinderService(Context.NOTIFICATION_SERVICE, mService, /* allowIsolated= */ false,
                 DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL);
@@ -5239,8 +5242,8 @@
             if (!summaries.containsKey(pkg)) {
                 // Add summary
                 final ApplicationInfo appInfo =
-                       adjustedSbn.getNotification().extras.getParcelable(
-                               Notification.EXTRA_BUILDER_APPLICATION_INFO);
+                        adjustedSbn.getNotification().extras.getParcelable(
+                                Notification.EXTRA_BUILDER_APPLICATION_INFO);
                 final Bundle extras = new Bundle();
                 extras.putParcelable(Notification.EXTRA_BUILDER_APPLICATION_INFO, appInfo);
                 final String channelId = notificationRecord.getChannel().getId();
@@ -5276,11 +5279,11 @@
                         notificationRecord.getIsAppImportanceLocked());
                 summaries.put(pkg, summarySbn.getKey());
             }
-        }
-        if (summaryRecord != null && checkDisqualifyingFeatures(userId, MY_UID,
-                summaryRecord.getSbn().getId(), summaryRecord.getSbn().getTag(), summaryRecord,
-                true)) {
-            mHandler.post(new EnqueueNotificationRunnable(userId, summaryRecord, isAppForeground));
+            if (summaryRecord != null && checkDisqualifyingFeatures(userId, MY_UID,
+                    summaryRecord.getSbn().getId(), summaryRecord.getSbn().getTag(), summaryRecord,
+                    true)) {
+                mHandler.post(new EnqueueNotificationRunnable(userId, summaryRecord, isAppForeground));
+            }
         }
     }
 
@@ -6019,13 +6022,17 @@
                 + " cannot post for pkg " + targetPkg + " in user " + userId);
     }
 
+    public boolean hasFlag(final int flags, final int flag) {
+        return (flags & flag) != 0;
+    }
     /**
      * Checks if a notification can be posted. checks rate limiter, snooze helper, and blocking.
      *
      * Has side effects.
      */
-    private boolean checkDisqualifyingFeatures(int userId, int uid, int id, String tag,
+    boolean checkDisqualifyingFeatures(int userId, int uid, int id, String tag,
             NotificationRecord r, boolean isAutogroup) {
+        Notification n = r.getNotification();
         final String pkg = r.getSbn().getPackageName();
         final boolean isSystemNotification =
                 isUidSystemOrPhone(uid) || ("android".equals(pkg));
@@ -6034,71 +6041,101 @@
         // Limit the number of notifications that any given package except the android
         // package or a registered listener can enqueue.  Prevents DOS attacks and deals with leaks.
         if (!isSystemNotification && !isNotificationFromListener) {
-            synchronized (mNotificationLock) {
-                final int callingUid = Binder.getCallingUid();
-                if (mNotificationsByKey.get(r.getSbn().getKey()) == null
-                        && isCallerInstantApp(callingUid, userId)) {
-                    // Ephemeral apps have some special constraints for notifications.
-                    // They are not allowed to create new notifications however they are allowed to
-                    // update notifications created by the system (e.g. a foreground service
-                    // notification).
-                    throw new SecurityException("Instant app " + pkg
-                            + " cannot create notifications");
-                }
+            final int callingUid = Binder.getCallingUid();
+            if (mNotificationsByKey.get(r.getSbn().getKey()) == null
+                    && isCallerInstantApp(callingUid, userId)) {
+                // Ephemeral apps have some special constraints for notifications.
+                // They are not allowed to create new notifications however they are allowed to
+                // update notifications created by the system (e.g. a foreground service
+                // notification).
+                throw new SecurityException("Instant app " + pkg
+                        + " cannot create notifications");
+            }
 
-                // rate limit updates that aren't completed progress notifications
-                if (mNotificationsByKey.get(r.getSbn().getKey()) != null
-                        && !r.getNotification().hasCompletedProgress()
-                        && !isAutogroup) {
+            // rate limit updates that aren't completed progress notifications
+            if (mNotificationsByKey.get(r.getSbn().getKey()) != null
+                    && !r.getNotification().hasCompletedProgress()
+                    && !isAutogroup) {
 
-                    final float appEnqueueRate = mUsageStats.getAppEnqueueRate(pkg);
-                    if (appEnqueueRate > mMaxPackageEnqueueRate) {
-                        mUsageStats.registerOverRateQuota(pkg);
-                        final long now = SystemClock.elapsedRealtime();
-                        if ((now - mLastOverRateLogTime) > MIN_PACKAGE_OVERRATE_LOG_INTERVAL) {
-                            Slog.e(TAG, "Package enqueue rate is " + appEnqueueRate
-                                    + ". Shedding " + r.getSbn().getKey() + ". package=" + pkg);
-                            mLastOverRateLogTime = now;
-                        }
-                        return false;
+                final float appEnqueueRate = mUsageStats.getAppEnqueueRate(pkg);
+                if (appEnqueueRate > mMaxPackageEnqueueRate) {
+                    mUsageStats.registerOverRateQuota(pkg);
+                    final long now = SystemClock.elapsedRealtime();
+                    if ((now - mLastOverRateLogTime) > MIN_PACKAGE_OVERRATE_LOG_INTERVAL) {
+                        Slog.e(TAG, "Package enqueue rate is " + appEnqueueRate
+                                + ". Shedding " + r.getSbn().getKey() + ". package=" + pkg);
+                        mLastOverRateLogTime = now;
                     }
+                    return false;
                 }
+            }
 
-                // limit the number of non-fgs outstanding notificationrecords an app can have
-                if (!r.getNotification().isForegroundService()) {
-                    int count = getNotificationCountLocked(pkg, userId, id, tag);
-                    if (count >= MAX_PACKAGE_NOTIFICATIONS) {
-                        mUsageStats.registerOverCountQuota(pkg);
-                        Slog.e(TAG, "Package has already posted or enqueued " + count
-                                + " notifications.  Not showing more.  package=" + pkg);
-                        return false;
-                    }
+            // limit the number of non-fgs outstanding notificationrecords an app can have
+            if (!n.isForegroundService()) {
+                int count = getNotificationCountLocked(pkg, userId, id, tag);
+                if (count >= MAX_PACKAGE_NOTIFICATIONS) {
+                    mUsageStats.registerOverCountQuota(pkg);
+                    Slog.e(TAG, "Package has already posted or enqueued " + count
+                            + " notifications.  Not showing more.  package=" + pkg);
+                    return false;
                 }
             }
         }
 
-        synchronized (mNotificationLock) {
-            // snoozed apps
-            if (mSnoozeHelper.isSnoozed(userId, pkg, r.getKey())) {
-                MetricsLogger.action(r.getLogMaker()
-                        .setType(MetricsProto.MetricsEvent.TYPE_UPDATE)
-                        .setCategory(MetricsProto.MetricsEvent.NOTIFICATION_SNOOZED));
-                mNotificationRecordLogger.log(
-                        NotificationRecordLogger.NotificationEvent.NOTIFICATION_NOT_POSTED_SNOOZED,
-                        r);
-                if (DBG) {
-                    Slog.d(TAG, "Ignored enqueue for snoozed notification " + r.getKey());
+        // bubble or inline reply that's immutable?
+        if (n.getBubbleMetadata() != null
+                && n.getBubbleMetadata().getIntent() != null
+                && hasFlag(mAmi.getPendingIntentFlags(
+                        n.getBubbleMetadata().getIntent().getTarget()),
+                        PendingIntent.FLAG_IMMUTABLE)) {
+            throw new IllegalArgumentException(r.getKey() + " Not posted."
+                    + " PendingIntents attached to bubbles must be mutable");
+        }
+
+        if (n.actions != null) {
+            for (Notification.Action action : n.actions) {
+                if ((action.getRemoteInputs() != null || action.getDataOnlyRemoteInputs() != null)
+                        && hasFlag(mAmi.getPendingIntentFlags(action.actionIntent.getTarget()),
+                        PendingIntent.FLAG_IMMUTABLE)) {
+                    throw new IllegalArgumentException(r.getKey() + " Not posted."
+                            + " PendingIntents attached to actions with remote"
+                            + " inputs must be mutable");
                 }
-                mSnoozeHelper.update(userId, r);
-                handleSavePolicyFile();
-                return false;
             }
+        }
+
+        if (r.getSystemGeneratedSmartActions() != null) {
+            for (Notification.Action action : r.getSystemGeneratedSmartActions()) {
+                if ((action.getRemoteInputs() != null || action.getDataOnlyRemoteInputs() != null)
+                        && hasFlag(mAmi.getPendingIntentFlags(action.actionIntent.getTarget()),
+                        PendingIntent.FLAG_IMMUTABLE)) {
+                    throw new IllegalArgumentException(r.getKey() + " Not posted."
+                            + " PendingIntents attached to contextual actions with remote inputs"
+                            + " must be mutable");
+                }
+            }
+        }
+
+        // snoozed apps
+        if (mSnoozeHelper.isSnoozed(userId, pkg, r.getKey())) {
+            MetricsLogger.action(r.getLogMaker()
+                    .setType(MetricsProto.MetricsEvent.TYPE_UPDATE)
+                    .setCategory(MetricsProto.MetricsEvent.NOTIFICATION_SNOOZED));
+            mNotificationRecordLogger.log(
+                    NotificationRecordLogger.NotificationEvent.NOTIFICATION_NOT_POSTED_SNOOZED,
+                    r);
+            if (DBG) {
+                Slog.d(TAG, "Ignored enqueue for snoozed notification " + r.getKey());
+            }
+            mSnoozeHelper.update(userId, r);
+            handleSavePolicyFile();
+            return false;
+        }
 
 
-            // blocked apps
-            if (isBlocked(r, mUsageStats)) {
-                return false;
-            }
+        // blocked apps
+        if (isBlocked(r, mUsageStats)) {
+            return false;
         }
 
         return true;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index ed6a20b..6ee5831 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -43,6 +43,9 @@
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
+import static android.app.PendingIntent.FLAG_IMMUTABLE;
+import static android.app.PendingIntent.FLAG_MUTABLE;
+import static android.app.PendingIntent.FLAG_ONE_SHOT;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
 import static android.content.pm.PackageManager.FEATURE_WATCH;
 import static android.content.pm.PackageManager.PERMISSION_DENIED;
@@ -110,6 +113,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.IIntentSender;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
@@ -248,6 +252,11 @@
     Resources mResources;
     @Mock
     RankingHandler mRankingHandler;
+    @Mock
+    ActivityManagerInternal mAmi;
+
+    @Mock
+    IIntentSender pi1;
 
     private static final int MAX_POST_DELAY = 1000;
 
@@ -392,7 +401,6 @@
 
         DeviceIdleInternal deviceIdleInternal = mock(DeviceIdleInternal.class);
         when(deviceIdleInternal.getNotificationAllowlistDuration()).thenReturn(3000L);
-        ActivityManagerInternal activityManagerInternal = mock(ActivityManagerInternal.class);
 
         LocalServices.removeServiceForTest(UriGrantsManagerInternal.class);
         LocalServices.addService(UriGrantsManagerInternal.class, mUgmInternal);
@@ -403,7 +411,7 @@
         LocalServices.removeServiceForTest(DeviceIdleInternal.class);
         LocalServices.addService(DeviceIdleInternal.class, deviceIdleInternal);
         LocalServices.removeServiceForTest(ActivityManagerInternal.class);
-        LocalServices.addService(ActivityManagerInternal.class, activityManagerInternal);
+        LocalServices.addService(ActivityManagerInternal.class, mAmi);
 
         doNothing().when(mContext).sendBroadcastAsUser(any(), any(), any());
 
@@ -477,7 +485,7 @@
                 mGroupHelper, mAm, mAtm, mAppUsageStats,
                 mock(DevicePolicyManagerInternal.class), mUgm, mUgmInternal,
                 mAppOpsManager, mUm, mHistoryManager, mStatsManager,
-                mock(TelephonyManager.class));
+                mock(TelephonyManager.class), mAmi);
         mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
 
         mService.setAudioManager(mAudioManager);
@@ -674,7 +682,8 @@
         }
         Notification.Builder nb = new Notification.Builder(mContext, channel.getId())
                 .setContentTitle("foo")
-                .setSmallIcon(android.R.drawable.sym_def_app_icon);
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .addAction(new Notification.Action.Builder(null, "test", null).build());
         if (extender != null) {
             nb.extend(extender);
         }
@@ -810,6 +819,7 @@
         PendingIntent pendingIntent = mock(PendingIntent.class);
         Intent intent = mock(Intent.class);
         when(pendingIntent.getIntent()).thenReturn(intent);
+        when(pendingIntent.getTarget()).thenReturn(pi1);
 
         ActivityInfo info = new ActivityInfo();
         info.resizeMode = RESIZE_MODE_RESIZEABLE;
@@ -7134,4 +7144,159 @@
         inOrder.verify(parent).recordDismissalSentiment(anyInt());
         inOrder.verify(child).recordDismissalSentiment(anyInt());
     }
+
+    @Test
+    public void testImmutableBubbleIntent() throws Exception {
+        when(mAmi.getPendingIntentFlags(pi1))
+                .thenReturn(FLAG_IMMUTABLE | FLAG_ONE_SHOT);
+        NotificationRecord r = generateMessageBubbleNotifRecord(true,
+                mTestNotificationChannel, 7, "testImmutableBubbleIntent", null, false);
+        try {
+            mBinderService.enqueueNotificationWithTag(PKG, PKG, r.getSbn().getTag(),
+                    r.getSbn().getId(), r.getNotification(), r.getSbn().getUserId());
+
+            waitForIdle();
+            fail("Allowed a bubble with an immutable intent to be posted");
+        } catch (IllegalArgumentException e) {
+            // good
+        }
+    }
+
+    @Test
+    public void testMutableBubbleIntent() throws Exception {
+        when(mAmi.getPendingIntentFlags(pi1))
+                .thenReturn(FLAG_MUTABLE | FLAG_ONE_SHOT);
+        NotificationRecord r = generateMessageBubbleNotifRecord(true,
+                mTestNotificationChannel, 7, "testMutableBubbleIntent", null, false);
+
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, r.getSbn().getTag(),
+                r.getSbn().getId(), r.getNotification(), r.getSbn().getUserId());
+
+        waitForIdle();
+        StatusBarNotification[] notifs =
+                mBinderService.getActiveNotifications(r.getSbn().getPackageName());
+        assertEquals(1, notifs.length);
+    }
+
+    @Test
+    public void testImmutableDirectReplyActionIntent() throws Exception {
+        when(mAmi.getPendingIntentFlags(any(IIntentSender.class)))
+                .thenReturn(FLAG_IMMUTABLE | FLAG_ONE_SHOT);
+        NotificationRecord r = generateMessageBubbleNotifRecord(false,
+                mTestNotificationChannel, 7, "testImmutableDirectReplyActionIntent", null, false);
+        try {
+            mBinderService.enqueueNotificationWithTag(PKG, PKG, r.getSbn().getTag(),
+                    r.getSbn().getId(), r.getNotification(), r.getSbn().getUserId());
+
+            waitForIdle();
+            fail("Allowed a direct reply with an immutable intent to be posted");
+        } catch (IllegalArgumentException e) {
+            // good
+        }
+    }
+
+    @Test
+    public void testMutableDirectReplyActionIntent() throws Exception {
+        when(mAmi.getPendingIntentFlags(any(IIntentSender.class)))
+                .thenReturn(FLAG_MUTABLE | FLAG_ONE_SHOT);
+        NotificationRecord r = generateMessageBubbleNotifRecord(false,
+                mTestNotificationChannel, 7, "testMutableDirectReplyActionIntent", null, false);
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, r.getSbn().getTag(),
+                r.getSbn().getId(), r.getNotification(), r.getSbn().getUserId());
+
+        waitForIdle();
+        StatusBarNotification[] notifs =
+                mBinderService.getActiveNotifications(r.getSbn().getPackageName());
+        assertEquals(1, notifs.length);
+    }
+
+    @Test
+    public void testImmutableDirectReplyContextualActionIntent() throws Exception {
+        when(mAmi.getPendingIntentFlags(any(IIntentSender.class)))
+                .thenReturn(FLAG_IMMUTABLE | FLAG_ONE_SHOT);
+        when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
+
+        NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+        ArrayList<Notification.Action> extraAction = new ArrayList<>();
+        RemoteInput remoteInput = new RemoteInput.Builder("reply_key").setLabel("reply").build();
+        PendingIntent inputIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
+        Icon icon = Icon.createWithResource(mContext, android.R.drawable.sym_def_app_icon);
+        Notification.Action replyAction = new Notification.Action.Builder(icon, "Reply",
+                inputIntent).addRemoteInput(remoteInput)
+                .build();
+        extraAction.add(replyAction);
+        Bundle signals = new Bundle();
+        signals.putParcelableArrayList(Adjustment.KEY_CONTEXTUAL_ACTIONS, extraAction);
+        Adjustment adjustment = new Adjustment(r.getSbn().getPackageName(), r.getKey(), signals, "",
+                r.getUser());
+        r.addAdjustment(adjustment);
+        r.applyAdjustments();
+
+        try {
+            mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(), r.getSbn().getId(),
+                    r.getSbn().getTag(), r,false);
+            fail("Allowed a contextual direct reply with an immutable intent to be posted");
+        } catch (IllegalArgumentException e) {
+            // good
+        }
+    }
+
+    @Test
+    public void testMutableDirectReplyContextualActionIntent() throws Exception {
+        when(mAmi.getPendingIntentFlags(any(IIntentSender.class)))
+                .thenReturn(FLAG_MUTABLE | FLAG_ONE_SHOT);
+        when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
+        NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+        ArrayList<Notification.Action> extraAction = new ArrayList<>();
+        RemoteInput remoteInput = new RemoteInput.Builder("reply_key").setLabel("reply").build();
+        PendingIntent inputIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
+        Icon icon = Icon.createWithResource(mContext, android.R.drawable.sym_def_app_icon);
+        Notification.Action replyAction = new Notification.Action.Builder(icon, "Reply",
+                inputIntent).addRemoteInput(remoteInput)
+                .build();
+        extraAction.add(replyAction);
+        Bundle signals = new Bundle();
+        signals.putParcelableArrayList(Adjustment.KEY_CONTEXTUAL_ACTIONS, extraAction);
+        Adjustment adjustment = new Adjustment(r.getSbn().getPackageName(), r.getKey(), signals, "",
+                r.getUser());
+        r.addAdjustment(adjustment);
+        r.applyAdjustments();
+
+        mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(), r.getSbn().getId(),
+                r.getSbn().getTag(), r,false);
+    }
+
+    @Test
+    public void testImmutableActionIntent() throws Exception {
+        when(mAmi.getPendingIntentFlags(any(IIntentSender.class)))
+                .thenReturn(FLAG_IMMUTABLE | FLAG_ONE_SHOT);
+        NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, r.getSbn().getTag(),
+                r.getSbn().getId(), r.getNotification(), r.getSbn().getUserId());
+
+        waitForIdle();
+        StatusBarNotification[] notifs =
+                mBinderService.getActiveNotifications(r.getSbn().getPackageName());
+        assertEquals(1, notifs.length);
+    }
+
+    @Test
+    public void testImmutableContextualActionIntent() throws Exception {
+        when(mAmi.getPendingIntentFlags(any(IIntentSender.class)))
+                .thenReturn(FLAG_IMMUTABLE | FLAG_ONE_SHOT);
+        when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
+        NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+        ArrayList<Notification.Action> extraAction = new ArrayList<>();
+        extraAction.add(new Notification.Action(0, "hello", null));
+        Bundle signals = new Bundle();
+        signals.putParcelableArrayList(Adjustment.KEY_CONTEXTUAL_ACTIONS, extraAction);
+        Adjustment adjustment = new Adjustment(r.getSbn().getPackageName(), r.getKey(), signals, "",
+                r.getUser());
+        r.addAdjustment(adjustment);
+        r.applyAdjustments();
+
+        mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(), r.getSbn().getId(),
+                    r.getSbn().getTag(), r,false);
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
index 3281c3f..a80f62a 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
@@ -32,6 +32,7 @@
 import static org.mockito.Mockito.when;
 
 import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
 import android.app.AppOpsManager;
 import android.app.IActivityManager;
 import android.app.IUriGrantsManager;
@@ -154,7 +155,8 @@
                     mock(DevicePolicyManagerInternal.class), mock(IUriGrantsManager.class),
                     mock(UriGrantsManagerInternal.class),
                     mock(AppOpsManager.class), mUm, mock(NotificationHistoryManager.class),
-                    mock(StatsManager.class), mock(TelephonyManager.class));
+                    mock(StatsManager.class), mock(TelephonyManager.class),
+                    mock(ActivityManagerInternal.class));
         } catch (SecurityException e) {
             if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
                 throw e;