Only show smart actions for whitelisted apps in lock task mode.

In lock task mode only apps from a specific whitelist can be started. To
avoid showing buttons that won't do anything when clicked we remove
smart actions linking to apps that are not whitelisted.

In this change we add several IPC calls during smart suggestions (in
notification) inflation - one in the common code flow, and several
others only for the case where lock task kiosk mode is enabled. This is
OK from a performance perspective because we inflate smart suggestions
on a background thread.

Bug: 117976013
Test: atest InflatedSmartRepliesTest
Test: start lock-task mode with 1. chrome whitelisted -> chrome actions
show up, 2. chrome not whitelisted -> chrome actions don't show up.
Test: ensure smart replies are still enabled in lock task mode.
Change-Id: I664ff2cdcfd1b212744d85d36d7a2b305bf4b3a9
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 87f004f..f946cc1 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -48,6 +48,9 @@
 import com.android.systemui.privacy.PrivacyItemController;
 import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.DevicePolicyManagerWrapper;
+import com.android.systemui.shared.system.PackageManagerWrapper;
 import com.android.systemui.statusbar.AmbientPulseManager;
 import com.android.systemui.statusbar.NavigationBarController;
 import com.android.systemui.statusbar.NotificationListener;
@@ -285,6 +288,9 @@
     @Nullable
     @Inject @Named(LEAK_REPORT_EMAIL_NAME) Lazy<String> mLeakReportEmail;
     @Inject Lazy<ClockManager> mClockManager;
+    @Inject Lazy<ActivityManagerWrapper> mActivityManagerWrapper;
+    @Inject Lazy<DevicePolicyManagerWrapper> mDevicePolicyManagerWrapper;
+    @Inject Lazy<PackageManagerWrapper> mPackageManagerWrapper;
 
     @Inject
     public Dependency() {
@@ -452,6 +458,9 @@
                 mForegroundServiceNotificationListener::get);
         mProviders.put(ClockManager.class, mClockManager::get);
         mProviders.put(PrivacyItemController.class, mPrivacyItemController::get);
+        mProviders.put(ActivityManagerWrapper.class, mActivityManagerWrapper::get);
+        mProviders.put(DevicePolicyManagerWrapper.class, mDevicePolicyManagerWrapper::get);
+        mProviders.put(PackageManagerWrapper.class, mPackageManagerWrapper::get);
 
 
         // TODO(b/118592525): to support multi-display , we start to add something which is
diff --git a/packages/SystemUI/src/com/android/systemui/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/DependencyProvider.java
index a517d7c..895f9b9 100644
--- a/packages/SystemUI/src/com/android/systemui/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/DependencyProvider.java
@@ -41,6 +41,9 @@
 import com.android.systemui.plugins.PluginInitializerImpl;
 import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.shared.plugins.PluginManagerImpl;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.DevicePolicyManagerWrapper;
+import com.android.systemui.shared.system.PackageManagerWrapper;
 import com.android.systemui.statusbar.NavigationBarController;
 import com.android.systemui.statusbar.phone.AutoHideController;
 import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
@@ -181,4 +184,22 @@
             @Named(MAIN_HANDLER_NAME) Handler mainHandler) {
         return new AutoHideController(context, mainHandler);
     }
+
+    @Singleton
+    @Provides
+    public ActivityManagerWrapper provideActivityManagerWrapper() {
+        return ActivityManagerWrapper.getInstance();
+    }
+
+    @Singleton
+    @Provides
+    public DevicePolicyManagerWrapper provideDevicePolicyManagerWrapper() {
+        return DevicePolicyManagerWrapper.getInstance();
+    }
+
+    @Singleton
+    @Provides
+    public PackageManagerWrapper providePackageManagerWrapper() {
+        return PackageManagerWrapper.getInstance();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplies.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplies.java
index d8ea1f6..5b2e398 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplies.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplies.java
@@ -21,12 +21,18 @@
 import android.app.Notification;
 import android.app.RemoteInput;
 import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
 import android.os.Build;
 import android.util.Log;
 import android.util.Pair;
 import android.widget.Button;
 
 import com.android.internal.util.ArrayUtils;
+import com.android.systemui.Dependency;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.DevicePolicyManagerWrapper;
+import com.android.systemui.shared.system.PackageManagerWrapper;
 import com.android.systemui.statusbar.SmartReplyController;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
@@ -191,14 +197,47 @@
             boolean useSmartActions = !ArrayUtils.isEmpty(entry.systemGeneratedSmartActions)
                     && notification.getAllowSystemGeneratedContextualActions();
             if (useSmartActions) {
+                List<Notification.Action> systemGeneratedActions =
+                        entry.systemGeneratedSmartActions;
+                // Filter actions if we're in kiosk-mode - we don't care about screen pinning mode,
+                // since notifications aren't shown there anyway.
+                ActivityManagerWrapper activityManagerWrapper =
+                        Dependency.get(ActivityManagerWrapper.class);
+                if (activityManagerWrapper.isLockTaskKioskModeActive()) {
+                    systemGeneratedActions = filterWhiteListedLockTaskApps(systemGeneratedActions);
+                }
                 smartActions = new SmartReplyView.SmartActions(
-                        entry.systemGeneratedSmartActions, true /* fromAssistant */);
+                        systemGeneratedActions, true /* fromAssistant */);
             }
         }
         return new SmartRepliesAndActions(smartReplies, smartActions);
     }
 
     /**
+     * Filter actions so that only actions pointing to whitelisted apps are allowed.
+     * This filtering is only meaningful when in lock-task mode.
+     */
+    private static List<Notification.Action> filterWhiteListedLockTaskApps(
+            List<Notification.Action> actions) {
+        PackageManagerWrapper packageManagerWrapper = Dependency.get(PackageManagerWrapper.class);
+        DevicePolicyManagerWrapper devicePolicyManagerWrapper =
+                Dependency.get(DevicePolicyManagerWrapper.class);
+        List<Notification.Action> filteredActions = new ArrayList<>();
+        for (Notification.Action action : actions) {
+            if (action.actionIntent == null) continue;
+            Intent intent = action.actionIntent.getIntent();
+            //  Only allow actions that are explicit (implicit intents are not handled in lock-task
+            //  mode), and link to whitelisted apps.
+            ResolveInfo resolveInfo = packageManagerWrapper.resolveActivity(intent, 0 /* flags */);
+            if (resolveInfo != null && devicePolicyManagerWrapper.isLockTaskPermitted(
+                    resolveInfo.activityInfo.packageName)) {
+                filteredActions.add(action);
+            }
+        }
+        return filteredActions;
+    }
+
+    /**
      * Returns whether the {@link Notification} represented by entry has a free-form remote input.
      * Such an input can be used e.g. to implement smart reply buttons - by passing the replies
      * through the remote input.