Add NLS specific screens for notification listener approval

Fixes: 141689199
Fixes: 143639217
Test: atest

Change-Id: I4ead087e0015ad33d6be4f9357de50a4298b3347
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index 24963b3..c6b7e62 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -96,6 +96,7 @@
     public static class NotificationStationActivity extends SettingsActivity { /* empty */ }
     public static class UserSettingsActivity extends SettingsActivity { /* empty */ }
     public static class NotificationAccessSettingsActivity extends SettingsActivity { /* empty */ }
+    public static class NotificationAccessDetailsActivity extends SettingsActivity { /* empty */ }
     public static class VrListenersSettingsActivity extends SettingsActivity { /* empty */ }
     public static class PictureInPictureSettingsActivity extends SettingsActivity { /* empty */ }
     public static class AppPictureInPictureSettingsActivity extends SettingsActivity { /* empty */ }
diff --git a/src/com/android/settings/applications/specialaccess/notificationaccess/FriendlyWarningDialogFragment.java b/src/com/android/settings/applications/specialaccess/notificationaccess/FriendlyWarningDialogFragment.java
new file mode 100644
index 0000000..3577946
--- /dev/null
+++ b/src/com/android/settings/applications/specialaccess/notificationaccess/FriendlyWarningDialogFragment.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2019 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.settings.applications.specialaccess.notificationaccess;
+
+import android.app.Dialog;
+import android.app.settings.SettingsEnums;
+import android.content.ComponentName;
+import android.content.DialogInterface;
+import android.os.Bundle;
+
+import androidx.appcompat.app.AlertDialog;
+import androidx.fragment.app.Fragment;
+
+import com.android.settings.R;
+import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+
+public class FriendlyWarningDialogFragment extends InstrumentedDialogFragment {
+    static final String KEY_COMPONENT = "c";
+    static final String KEY_LABEL = "l";
+
+    public FriendlyWarningDialogFragment setServiceInfo(ComponentName cn, CharSequence label,
+            Fragment target) {
+        Bundle args = new Bundle();
+        args.putString(KEY_COMPONENT, cn.flattenToString());
+        args.putCharSequence(KEY_LABEL, label);
+        setArguments(args);
+        setTargetFragment(target, 0);
+        return this;
+    }
+
+    @Override
+    public int getMetricsCategory() {
+        return SettingsEnums.DIALOG_DISABLE_NOTIFICATION_ACCESS;
+    }
+
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        final Bundle args = getArguments();
+        final CharSequence label = args.getCharSequence(KEY_LABEL);
+        final ComponentName cn = ComponentName.unflattenFromString(args
+                .getString(KEY_COMPONENT));
+        NotificationAccessDetails parent = (NotificationAccessDetails) getTargetFragment();
+
+        final String summary = getResources().getString(
+                R.string.notification_listener_disable_warning_summary, label);
+        return new AlertDialog.Builder(getContext())
+                .setMessage(summary)
+                .setCancelable(true)
+                .setPositiveButton(R.string.notification_listener_disable_warning_confirm,
+                        new DialogInterface.OnClickListener() {
+                            @Override
+                            public void onClick(DialogInterface dialog, int which) {
+                                parent.disable(cn);
+                            }
+                        })
+                .setNegativeButton(R.string.notification_listener_disable_warning_cancel,
+                        (dialog, id) -> {
+                            // pass
+                        })
+                .create();
+    }
+}
diff --git a/src/com/android/settings/applications/specialaccess/notificationaccess/NotificationAccessController.java b/src/com/android/settings/applications/specialaccess/notificationaccess/NotificationAccessController.java
index f9e8fe3..6025da5 100644
--- a/src/com/android/settings/applications/specialaccess/notificationaccess/NotificationAccessController.java
+++ b/src/com/android/settings/applications/specialaccess/notificationaccess/NotificationAccessController.java
@@ -17,6 +17,8 @@
 package com.android.settings.applications.specialaccess.notificationaccess;
 
 import android.app.ActivityManager;
+import android.app.NotificationManager;
+import android.content.ComponentName;
 import android.content.Context;
 
 import com.android.settings.core.BasePreferenceController;
@@ -33,4 +35,9 @@
                 ? AVAILABLE_UNSEARCHABLE
                 : UNSUPPORTED_ON_DEVICE;
     }
+
+    public static boolean hasAccess(Context context, ComponentName cn) {
+        return context.getSystemService(NotificationManager.class)
+                .isNotificationListenerAccessGranted(cn);
+    }
 }
diff --git a/src/com/android/settings/applications/specialaccess/notificationaccess/NotificationAccessDetails.java b/src/com/android/settings/applications/specialaccess/notificationaccess/NotificationAccessDetails.java
new file mode 100644
index 0000000..dc0a1cb
--- /dev/null
+++ b/src/com/android/settings/applications/specialaccess/notificationaccess/NotificationAccessDetails.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2019 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.settings.applications.specialaccess.notificationaccess;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.NotificationManager;
+import android.app.settings.SettingsEnums;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.service.notification.NotificationListenerService;
+import android.util.IconDrawableFactory;
+import android.util.Log;
+import android.util.Slog;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.appcompat.app.AlertDialog;
+import androidx.preference.Preference;
+import androidx.preference.SwitchPreference;
+
+import com.android.settings.R;
+import com.android.settings.applications.AppInfoBase;
+import com.android.settings.overlay.FeatureFactory;
+import com.android.settings.widget.EntityHeaderController;
+import com.android.settingslib.applications.AppUtils;
+
+import java.util.List;
+import java.util.Objects;
+
+public class NotificationAccessDetails extends AppInfoBase {
+    private static final String TAG = "NotifAccessDetails";
+    private static final String SWITCH_PREF_KEY = "notification_access_switch";
+
+    private boolean mCreated;
+    private ComponentName mComponentName;
+    private CharSequence mServiceName;
+    private boolean mIsNls;
+
+    private NotificationManager mNm;
+    private PackageManager mPm;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        final Intent intent = getIntent();
+        if (mComponentName == null && intent != null) {
+            String cn = intent.getStringExtra(Settings.EXTRA_NOTIFICATION_LISTENER_COMPONENT_NAME);
+            if (cn != null) {
+                mComponentName = ComponentName.unflattenFromString(cn);
+                if (mComponentName != null) {
+                    final Bundle args = getArguments();
+                    args.putString(ARG_PACKAGE_NAME, mComponentName.getPackageName());
+                }
+            }
+        }
+        super.onCreate(savedInstanceState);
+        mNm = getContext().getSystemService(NotificationManager.class);
+        mPm = getPackageManager();
+        addPreferencesFromResource(R.xml.notification_access_permission_details);
+    }
+
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+        if (mCreated) {
+            Log.w(TAG, "onActivityCreated: ignoring duplicate call");
+            return;
+        }
+        mCreated = true;
+        if (mPackageInfo == null) return;
+        loadNotificationListenerService();
+        final Activity activity = getActivity();
+        final Preference pref = EntityHeaderController
+                .newInstance(activity, this, null /* header */)
+                .setRecyclerView(getListView(), getSettingsLifecycle())
+                .setIcon(IconDrawableFactory.newInstance(getContext())
+                        .getBadgedIcon(mPackageInfo.applicationInfo))
+                .setLabel(mPackageInfo.applicationInfo.loadLabel(mPm))
+                .setSummary(mServiceName)
+                .setIsInstantApp(AppUtils.isInstant(mPackageInfo.applicationInfo))
+                .setPackageName(mPackageName)
+                .setUid(mPackageInfo.applicationInfo.uid)
+                .setHasAppInfoLink(true)
+                .setButtonActions(EntityHeaderController.ActionType.ACTION_NONE,
+                        EntityHeaderController.ActionType.ACTION_NONE)
+                .done(activity, getPrefContext());
+        getPreferenceScreen().addPreference(pref);
+    }
+
+    @Override
+    public int getMetricsCategory() {
+        return SettingsEnums.NOTIFICATION_ACCESS_DETAIL;
+    }
+
+    @Override
+    protected boolean refreshUi() {
+        final Context context = getContext();
+        if (context.getSystemService(ActivityManager.class).isLowRamDeviceStatic()) {
+            Slog.d(TAG, "not available on low ram devices");
+            return false;
+        }
+        if (mComponentName == null) {
+            // No service given
+            Slog.d(TAG, "No component name provided");
+            return false;
+        }
+        if (!mIsNls) {
+            // This component doesn't have the right androidmanifest definition to be an NLS
+            Slog.d(TAG, "Provided component name is not an NLS");
+            return false;
+        }
+        if (UserManager.get(getContext()).isManagedProfile()) {
+            // Apps in the work profile do not support notification listeners.
+            Slog.d(TAG, "NLSes aren't allowed in work profiles");
+            return false;
+        }
+        updatePreference(findPreference(SWITCH_PREF_KEY));
+        return true;
+    }
+
+    @Override
+    protected AlertDialog createDialog(int id, int errorCode) {
+        return null;
+    }
+
+    public void updatePreference(SwitchPreference preference) {
+        final CharSequence label = mPackageInfo.applicationInfo.loadLabel(mPm);
+        preference.setChecked(isServiceEnabled(mComponentName));
+        preference.setOnPreferenceChangeListener((p, newValue) -> {
+            final boolean access = (Boolean) newValue;
+            if (!access) {
+                if (!isServiceEnabled(mComponentName)) {
+                    return true; // already disabled
+                }
+                // show a friendly dialog
+                new FriendlyWarningDialogFragment()
+                        .setServiceInfo(mComponentName, label, this)
+                        .show(getFragmentManager(), "friendlydialog");
+                return false;
+            } else {
+                if (isServiceEnabled(mComponentName)) {
+                    return true; // already enabled
+                }
+                // show a scary dialog
+                new ScaryWarningDialogFragment()
+                        .setServiceInfo(mComponentName, label, this)
+                        .show(getFragmentManager(), "dialog");
+                return false;
+            }
+        });
+    }
+
+    @VisibleForTesting
+    void logSpecialPermissionChange(boolean enable, String packageName) {
+        int logCategory = enable ? SettingsEnums.APP_SPECIAL_PERMISSION_NOTIVIEW_ALLOW
+                : SettingsEnums.APP_SPECIAL_PERMISSION_NOTIVIEW_DENY;
+        FeatureFactory.getFactory(getContext()).getMetricsFeatureProvider().action(getContext(),
+                logCategory, packageName);
+    }
+
+    public void disable(final ComponentName cn) {
+        logSpecialPermissionChange(true, cn.getPackageName());
+        mNm.setNotificationListenerAccessGranted(cn, false);
+        AsyncTask.execute(() -> {
+            if (!mNm.isNotificationPolicyAccessGrantedForPackage(
+                    cn.getPackageName())) {
+                mNm.removeAutomaticZenRules(cn.getPackageName());
+            }
+        });
+        refreshUi();
+    }
+
+    protected void enable(ComponentName cn) {
+        logSpecialPermissionChange(true, cn.getPackageName());
+        mNm.setNotificationListenerAccessGranted(cn, true);
+        refreshUi();
+    }
+
+    protected boolean isServiceEnabled(ComponentName cn) {
+        return mNm.isNotificationListenerAccessGranted(cn);
+    }
+
+    protected void loadNotificationListenerService() {
+        mIsNls = false;
+
+        if (mComponentName == null) {
+            return;
+        }
+        Intent intent = new Intent(NotificationListenerService.SERVICE_INTERFACE)
+                .setComponent(mComponentName);
+        List<ResolveInfo> installedServices = mPm.queryIntentServicesAsUser(
+                intent, PackageManager.GET_SERVICES | PackageManager.GET_META_DATA, mUserId);
+        for (ResolveInfo resolveInfo : installedServices) {
+            ServiceInfo info = resolveInfo.serviceInfo;
+            if (android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE.equals(
+                    info.permission)) {
+                if (Objects.equals(mComponentName, info.getComponentName())) {
+                    mIsNls = true;
+                    mServiceName = info.loadLabel(mPm);
+                    break;
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/applications/specialaccess/notificationaccess/ScaryWarningDialogFragment.java b/src/com/android/settings/applications/specialaccess/notificationaccess/ScaryWarningDialogFragment.java
new file mode 100644
index 0000000..6613f96e7
--- /dev/null
+++ b/src/com/android/settings/applications/specialaccess/notificationaccess/ScaryWarningDialogFragment.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2019 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.settings.applications.specialaccess.notificationaccess;
+
+import android.app.Dialog;
+import android.app.settings.SettingsEnums;
+import android.content.ComponentName;
+import android.content.DialogInterface;
+import android.os.Bundle;
+
+import androidx.appcompat.app.AlertDialog;
+import androidx.fragment.app.Fragment;
+
+import com.android.settings.R;
+import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+
+
+public class ScaryWarningDialogFragment extends InstrumentedDialogFragment {
+    private static final String KEY_COMPONENT = "c";
+    private static final String KEY_LABEL = "l";
+
+    @Override
+    public int getMetricsCategory() {
+        return SettingsEnums.DIALOG_SERVICE_ACCESS_WARNING;
+    }
+
+    public ScaryWarningDialogFragment setServiceInfo(ComponentName cn, CharSequence label,
+            Fragment target) {
+        Bundle args = new Bundle();
+        args.putString(KEY_COMPONENT, cn.flattenToString());
+        args.putCharSequence(KEY_LABEL, label);
+        setArguments(args);
+        setTargetFragment(target, 0);
+        return this;
+    }
+
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        final Bundle args = getArguments();
+        final CharSequence label = args.getCharSequence(KEY_LABEL);
+        final ComponentName cn = ComponentName.unflattenFromString(args
+                .getString(KEY_COMPONENT));
+        NotificationAccessDetails parent = (NotificationAccessDetails) getTargetFragment();
+
+        final String title = getResources().getString(
+                R.string.notification_listener_security_warning_title, label);
+        final String summary = getResources().getString(
+                R.string.notification_listener_security_warning_summary, label);
+        return new AlertDialog.Builder(getContext())
+                .setMessage(summary)
+                .setTitle(title)
+                .setCancelable(true)
+                .setPositiveButton(R.string.allow,
+                        new DialogInterface.OnClickListener() {
+                            @Override
+                            public void onClick(DialogInterface dialog, int which) {
+                                parent.enable(cn);
+                            }
+                        })
+                .setNegativeButton(R.string.deny,
+                        (dialog, id) -> {
+                            // pass
+                        })
+                .create();
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/core/gateway/SettingsGateway.java b/src/com/android/settings/core/gateway/SettingsGateway.java
index a318037..0934ba9 100644
--- a/src/com/android/settings/core/gateway/SettingsGateway.java
+++ b/src/com/android/settings/core/gateway/SettingsGateway.java
@@ -45,6 +45,7 @@
 import com.android.settings.applications.manageapplications.ManageApplications;
 import com.android.settings.applications.managedomainurls.ManageDomainUrls;
 import com.android.settings.applications.specialaccess.deviceadmin.DeviceAdminSettings;
+import com.android.settings.applications.specialaccess.notificationaccess.NotificationAccessDetails;
 import com.android.settings.applications.specialaccess.pictureinpicture.PictureInPictureDetails;
 import com.android.settings.applications.specialaccess.pictureinpicture.PictureInPictureSettings;
 import com.android.settings.applications.specialaccess.vrlistener.VrListenerSettings;
@@ -217,6 +218,7 @@
             DreamSettings.class.getName(),
             UserSettings.class.getName(),
             NotificationAccessSettings.class.getName(),
+            NotificationAccessDetails.class.getName(),
             AppBubbleNotificationSettings.class.getName(),
             ZenAccessSettings.class.getName(),
             ZenAccessDetails.class.getName(),
diff --git a/src/com/android/settings/notification/NotificationAccessSettings.java b/src/com/android/settings/notification/NotificationAccessSettings.java
index 4c20f32..7f5f536 100644
--- a/src/com/android/settings/notification/NotificationAccessSettings.java
+++ b/src/com/android/settings/notification/NotificationAccessSettings.java
@@ -16,61 +16,174 @@
 
 package com.android.settings.notification;
 
-import android.app.Dialog;
+import android.annotation.Nullable;
+import android.app.ActivityManager;
 import android.app.NotificationManager;
+import android.app.admin.DevicePolicyManager;
 import android.app.settings.SettingsEnums;
 import android.content.ComponentName;
 import android.content.Context;
-import android.os.AsyncTask;
+import android.content.pm.PackageItemInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
 import android.os.Bundle;
+import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
 import android.service.notification.NotificationListenerService;
+import android.util.IconDrawableFactory;
+import android.util.Log;
+import android.view.View;
 import android.widget.Toast;
 
-import androidx.annotation.VisibleForTesting;
-import androidx.appcompat.app.AlertDialog;
-import androidx.fragment.app.Fragment;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
 
 import com.android.settings.R;
-import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
-import com.android.settings.overlay.FeatureFactory;
+import com.android.settings.Utils;
+import com.android.settings.applications.AppInfoBase;
+import com.android.settings.applications.specialaccess.notificationaccess.NotificationAccessDetails;
+import com.android.settings.core.SubSettingLauncher;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settings.utils.ManagedServiceSettings;
+import com.android.settings.widget.EmptyTextSettings;
+import com.android.settingslib.applications.ServiceListing;
 import com.android.settingslib.search.SearchIndexable;
 
+import java.util.List;
+
 /**
  * Settings screen for managing notification listener permissions
  */
 @SearchIndexable
-public class NotificationAccessSettings extends ManagedServiceSettings {
-    private static final String TAG = "NotificationAccessSettings";
-    private static final Config CONFIG = new Config.Builder()
-            .setTag(TAG)
-            .setSetting(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS)
-            .setIntentAction(NotificationListenerService.SERVICE_INTERFACE)
-            .setPermission(android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE)
-            .setNoun("notification listener")
-            .setWarningDialogTitle(R.string.notification_listener_security_warning_title)
-            .setWarningDialogSummary(R.string.notification_listener_security_warning_summary)
-            .setEmptyText(R.string.no_notification_listeners)
-            .build();
+public class NotificationAccessSettings extends EmptyTextSettings {
+    private static final String TAG = "NotifAccessSettings";
+    private static final ManagedServiceSettings.Config CONFIG =
+            new ManagedServiceSettings.Config.Builder()
+                    .setTag(TAG)
+                    .setSetting(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS)
+                    .setIntentAction(NotificationListenerService.SERVICE_INTERFACE)
+                    .setPermission(android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE)
+                    .setNoun("notification listener")
+                    .setWarningDialogTitle(R.string.notification_listener_security_warning_title)
+                    .setWarningDialogSummary(
+                            R.string.notification_listener_security_warning_summary)
+                    .setEmptyText(R.string.no_notification_listeners)
+                    .build();
 
     private NotificationManager mNm;
+    protected Context mContext;
+    private PackageManager mPm;
+    private DevicePolicyManager mDpm;
+    private ServiceListing mServiceListing;
+    private IconDrawableFactory mIconDrawableFactory;
 
     @Override
     public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
-        final Context ctx = getContext();
-        if (UserManager.get(ctx).isManagedProfile()) {
+
+        mContext = getActivity();
+        mPm = mContext.getPackageManager();
+        mDpm = (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
+        mIconDrawableFactory = IconDrawableFactory.newInstance(mContext);
+        mServiceListing = new ServiceListing.Builder(mContext)
+                .setPermission(CONFIG.permission)
+                .setIntentAction(CONFIG.intentAction)
+                .setNoun(CONFIG.noun)
+                .setSetting(CONFIG.setting)
+                .setTag(CONFIG.tag)
+                .build();
+        mServiceListing.addCallback(this::updateList);
+        setPreferenceScreen(getPreferenceManager().createPreferenceScreen(mContext));
+
+        if (UserManager.get(mContext).isManagedProfile()) {
             // Apps in the work profile do not support notification listeners.
-            Toast.makeText(ctx, R.string.notification_settings_work_profile, Toast.LENGTH_SHORT)
-                .show();
+            Toast.makeText(mContext, R.string.notification_settings_work_profile,
+                    Toast.LENGTH_SHORT).show();
             finish();
         }
     }
 
     @Override
+    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+        setEmptyText(CONFIG.emptyText);
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        if (!ActivityManager.isLowRamDeviceStatic()) {
+            mServiceListing.reload();
+            mServiceListing.setListening(true);
+        } else {
+            setEmptyText(R.string.disabled_low_ram_device);
+        }
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        mServiceListing.setListening(false);
+    }
+
+    private void updateList(List<ServiceInfo> services) {
+        final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+        final int managedProfileId = Utils.getManagedProfileId(um, UserHandle.myUserId());
+
+        final PreferenceScreen screen = getPreferenceScreen();
+        screen.removeAll();
+        services.sort(new PackageItemInfo.DisplayNameComparator(mPm));
+        for (ServiceInfo service : services) {
+            final ComponentName cn = new ComponentName(service.packageName, service.name);
+            CharSequence title = null;
+            try {
+                title = mPm.getApplicationInfoAsUser(
+                        service.packageName, 0, UserHandle.myUserId()).loadLabel(mPm);
+            } catch (PackageManager.NameNotFoundException e) {
+                // unlikely, as we are iterating over live services.
+                Log.e(TAG, "can't find package name", e);
+            }
+
+            final Preference pref = new Preference(getPrefContext());
+            pref.setTitle(title);
+            pref.setIcon(mIconDrawableFactory.getBadgedIcon(service, service.applicationInfo,
+                    UserHandle.getUserId(service.applicationInfo.uid)));
+            pref.setKey(cn.flattenToString());
+            pref.setSummary(mNm.isNotificationListenerAccessGranted(cn)
+                    ? R.string.app_permission_summary_allowed
+                    : R.string.app_permission_summary_not_allowed);
+            if (managedProfileId != UserHandle.USER_NULL
+                    && !mDpm.isNotificationListenerServicePermitted(
+                    service.packageName, managedProfileId)) {
+                pref.setSummary(R.string.work_profile_notification_access_blocked_summary);
+            }
+            pref.setOnPreferenceClickListener(preference -> {
+                final Bundle args = new Bundle();
+                args.putString(AppInfoBase.ARG_PACKAGE_NAME, cn.getPackageName());
+                args.putInt(AppInfoBase.ARG_PACKAGE_UID, service.applicationInfo.uid);
+
+                Bundle extras = new Bundle();
+                extras.putString(Settings.EXTRA_NOTIFICATION_LISTENER_COMPONENT_NAME,
+                        cn.flattenToString());
+
+                new SubSettingLauncher(getContext())
+                        .setDestination(NotificationAccessDetails.class.getName())
+                        .setSourceMetricsCategory(getMetricsCategory())
+                        .setTitleRes(R.string.manage_zen_access_title)
+                        .setArguments(args)
+                        .setExtras(extras)
+                        .setUserHandle(UserHandle.getUserHandleForUid(service.applicationInfo.uid))
+                        .launch();
+                        return true;
+                    });
+            pref.setKey(cn.flattenToString());
+            screen.addPreference(pref);
+        }
+        highlightPreferenceIfNeeded();
+    }
+
+    @Override
     public int getMetricsCategory() {
         return SettingsEnums.NOTIFICATION_ACCESS;
     }
@@ -82,109 +195,10 @@
     }
 
     @Override
-    protected Config getConfig() {
-        return CONFIG;
-    }
-
-    @Override
-    protected boolean setEnabled(ComponentName service, String title, boolean enable) {
-        logSpecialPermissionChange(enable, service.getPackageName());
-        if (!enable) {
-            if (!isServiceEnabled(service)) {
-                return true; // already disabled
-            }
-            // show a friendly dialog
-            new FriendlyWarningDialogFragment()
-                    .setServiceInfo(service, title, this)
-                    .show(getFragmentManager(), "friendlydialog");
-            return false;
-        } else {
-            if (isServiceEnabled(service)) {
-                return true; // already enabled
-            }
-            // show a scary dialog
-            new ScaryWarningDialogFragment()
-                    .setServiceInfo(service, title, this)
-                    .show(getFragmentManager(), "dialog");
-            return false;
-        }
-    }
-
-    @Override
-    protected boolean isServiceEnabled(ComponentName cn) {
-        return mNm.isNotificationListenerAccessGranted(cn);
-    }
-
-    @Override
-    protected void enable(ComponentName service) {
-        mNm.setNotificationListenerAccessGranted(service, true);
-    }
-
-    @Override
     protected int getPreferenceScreenResId() {
         return R.xml.notification_access_settings;
     }
 
-    @VisibleForTesting
-    void logSpecialPermissionChange(boolean enable, String packageName) {
-        int logCategory = enable ? SettingsEnums.APP_SPECIAL_PERMISSION_NOTIVIEW_ALLOW
-                : SettingsEnums.APP_SPECIAL_PERMISSION_NOTIVIEW_DENY;
-        FeatureFactory.getFactory(getContext()).getMetricsFeatureProvider().action(getContext(),
-                logCategory, packageName);
-    }
-
-    private static void disable(final NotificationAccessSettings parent, final ComponentName cn) {
-        parent.mNm.setNotificationListenerAccessGranted(cn, false);
-        AsyncTask.execute(() -> {
-            if (!parent.mNm.isNotificationPolicyAccessGrantedForPackage(
-                    cn.getPackageName())) {
-                parent.mNm.removeAutomaticZenRules(cn.getPackageName());
-            }
-        });
-    }
-
-    public static class FriendlyWarningDialogFragment extends InstrumentedDialogFragment {
-        static final String KEY_COMPONENT = "c";
-        static final String KEY_LABEL = "l";
-
-        public FriendlyWarningDialogFragment setServiceInfo(ComponentName cn, String label,
-                Fragment target) {
-            Bundle args = new Bundle();
-            args.putString(KEY_COMPONENT, cn.flattenToString());
-            args.putString(KEY_LABEL, label);
-            setArguments(args);
-            setTargetFragment(target, 0);
-            return this;
-        }
-
-        @Override
-        public int getMetricsCategory() {
-            return SettingsEnums.DIALOG_DISABLE_NOTIFICATION_ACCESS;
-        }
-
-        @Override
-        public Dialog onCreateDialog(Bundle savedInstanceState) {
-            final Bundle args = getArguments();
-            final String label = args.getString(KEY_LABEL);
-            final ComponentName cn = ComponentName.unflattenFromString(args
-                    .getString(KEY_COMPONENT));
-            NotificationAccessSettings parent = (NotificationAccessSettings) getTargetFragment();
-
-            final String summary = getResources().getString(
-                    R.string.notification_listener_disable_warning_summary, label);
-            return new AlertDialog.Builder(getContext())
-                    .setMessage(summary)
-                    .setCancelable(true)
-                    .setPositiveButton(R.string.notification_listener_disable_warning_confirm,
-                            (dialog, id) -> disable(parent, cn))
-                    .setNegativeButton(R.string.notification_listener_disable_warning_cancel,
-                            (dialog, id) -> {
-                                // pass
-                            })
-                    .create();
-        }
-    }
-
     public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
             new BaseSearchIndexProvider(R.xml.notification_access_settings);
 }