1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settings.notification; 18 19 import android.app.AlertDialog; 20 import android.app.Dialog; 21 import android.app.Fragment; 22 import android.app.NotificationManager; 23 import android.content.ComponentName; 24 import android.content.Context; 25 import android.os.AsyncTask; 26 import android.os.Bundle; 27 import android.provider.Settings; 28 import android.service.notification.NotificationListenerService; 29 30 import com.android.internal.annotations.VisibleForTesting; 31 import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 32 import com.android.settings.R; 33 import com.android.settings.core.instrumentation.InstrumentedDialogFragment; 34 import com.android.settings.overlay.FeatureFactory; 35 import com.android.settings.utils.ManagedServiceSettings; 36 37 /** 38 * Settings screen for managing notification listener permissions 39 */ 40 public class NotificationAccessSettings extends ManagedServiceSettings { 41 private static final String TAG = NotificationAccessSettings.class.getSimpleName(); 42 private static final Config CONFIG = new Config.Builder() 43 .setTag(TAG) 44 .setSetting(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS) 45 .setIntentAction(NotificationListenerService.SERVICE_INTERFACE) 46 .setPermission(android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE) 47 .setNoun("notification listener") 48 .setWarningDialogTitle(R.string.notification_listener_security_warning_title) 49 .setWarningDialogSummary(R.string.notification_listener_security_warning_summary) 50 .setEmptyText(R.string.no_notification_listeners) 51 .build(); 52 53 private NotificationManager mNm; 54 55 @Override getMetricsCategory()56 public int getMetricsCategory() { 57 return MetricsEvent.NOTIFICATION_ACCESS; 58 } 59 60 @Override onAttach(Context context)61 public void onAttach(Context context) { 62 super.onAttach(context); 63 mNm = context.getSystemService(NotificationManager.class); 64 } 65 66 @Override getConfig()67 protected Config getConfig() { 68 return CONFIG; 69 } 70 71 @Override setEnabled(ComponentName service, String title, boolean enable)72 protected boolean setEnabled(ComponentName service, String title, boolean enable) { 73 logSpecialPermissionChange(enable, service.getPackageName()); 74 if (!enable) { 75 if (!isServiceEnabled(service)) { 76 return true; // already disabled 77 } 78 // show a friendly dialog 79 new FriendlyWarningDialogFragment() 80 .setServiceInfo(service, title, this) 81 .show(getFragmentManager(), "friendlydialog"); 82 return false; 83 } else { 84 if (isServiceEnabled(service)) { 85 return true; // already enabled 86 } 87 // show a scary dialog 88 new ScaryWarningDialogFragment() 89 .setServiceInfo(service, title, this) 90 .show(getFragmentManager(), "dialog"); 91 return false; 92 } 93 } 94 95 @Override isServiceEnabled(ComponentName cn)96 protected boolean isServiceEnabled(ComponentName cn) { 97 return mNm.isNotificationListenerAccessGranted(cn); 98 } 99 100 @Override enable(ComponentName service)101 protected void enable(ComponentName service) { 102 mNm.setNotificationListenerAccessGranted(service, true); 103 } 104 105 @Override getPreferenceScreenResId()106 protected int getPreferenceScreenResId() { 107 return R.xml.notification_access_settings; 108 } 109 110 @VisibleForTesting logSpecialPermissionChange(boolean enable, String packageName)111 void logSpecialPermissionChange(boolean enable, String packageName) { 112 int logCategory = enable ? MetricsEvent.APP_SPECIAL_PERMISSION_NOTIVIEW_ALLOW 113 : MetricsEvent.APP_SPECIAL_PERMISSION_NOTIVIEW_DENY; 114 FeatureFactory.getFactory(getContext()).getMetricsFeatureProvider().action(getContext(), 115 logCategory, packageName); 116 } 117 disable(final NotificationAccessSettings parent, final ComponentName cn)118 private static void disable(final NotificationAccessSettings parent, final ComponentName cn) { 119 parent.mNm.setNotificationListenerAccessGranted(cn, false); 120 AsyncTask.execute(() -> { 121 if (!parent.mNm.isNotificationPolicyAccessGrantedForPackage( 122 cn.getPackageName())) { 123 parent.mNm.removeAutomaticZenRules(cn.getPackageName()); 124 } 125 }); 126 } 127 128 public static class FriendlyWarningDialogFragment extends InstrumentedDialogFragment { 129 static final String KEY_COMPONENT = "c"; 130 static final String KEY_LABEL = "l"; 131 setServiceInfo(ComponentName cn, String label, Fragment target)132 public FriendlyWarningDialogFragment setServiceInfo(ComponentName cn, String label, 133 Fragment target) { 134 Bundle args = new Bundle(); 135 args.putString(KEY_COMPONENT, cn.flattenToString()); 136 args.putString(KEY_LABEL, label); 137 setArguments(args); 138 setTargetFragment(target, 0); 139 return this; 140 } 141 142 @Override getMetricsCategory()143 public int getMetricsCategory() { 144 return MetricsEvent.DIALOG_DISABLE_NOTIFICATION_ACCESS; 145 } 146 147 @Override onCreateDialog(Bundle savedInstanceState)148 public Dialog onCreateDialog(Bundle savedInstanceState) { 149 final Bundle args = getArguments(); 150 final String label = args.getString(KEY_LABEL); 151 final ComponentName cn = ComponentName.unflattenFromString(args 152 .getString(KEY_COMPONENT)); 153 NotificationAccessSettings parent = (NotificationAccessSettings) getTargetFragment(); 154 155 final String summary = getResources().getString( 156 R.string.notification_listener_disable_warning_summary, label); 157 return new AlertDialog.Builder(getContext()) 158 .setMessage(summary) 159 .setCancelable(true) 160 .setPositiveButton(R.string.notification_listener_disable_warning_confirm, 161 (dialog, id) -> disable(parent, cn)) 162 .setNegativeButton(R.string.notification_listener_disable_warning_cancel, 163 (dialog, id) -> { 164 // pass 165 }) 166 .create(); 167 } 168 } 169 } 170