• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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