• 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.annotation.Nullable;
20 import android.app.NotificationManager;
21 import android.app.admin.DevicePolicyManager;
22 import android.app.settings.SettingsEnums;
23 import android.companion.ICompanionDeviceManager;
24 import android.content.ComponentName;
25 import android.content.Context;
26 import android.content.pm.PackageItemInfo;
27 import android.content.pm.PackageManager;
28 import android.content.pm.ServiceInfo;
29 import android.os.Bundle;
30 import android.os.ServiceManager;
31 import android.os.UserHandle;
32 import android.os.UserManager;
33 import android.provider.Settings;
34 import android.service.notification.NotificationListenerService;
35 import android.util.IconDrawableFactory;
36 import android.util.Log;
37 import android.view.View;
38 import android.widget.Toast;
39 
40 import androidx.preference.PreferenceCategory;
41 import androidx.preference.PreferenceScreen;
42 
43 import com.android.settings.R;
44 import com.android.settings.Utils;
45 import com.android.settings.applications.AppInfoBase;
46 import com.android.settings.applications.specialaccess.notificationaccess.NotificationAccessDetails;
47 import com.android.settings.core.SubSettingLauncher;
48 import com.android.settings.search.BaseSearchIndexProvider;
49 import com.android.settings.utils.ManagedServiceSettings;
50 import com.android.settings.widget.EmptyTextSettings;
51 import com.android.settingslib.applications.ServiceListing;
52 import com.android.settingslib.search.SearchIndexable;
53 import com.android.settingslib.widget.AppPreference;
54 
55 import java.util.List;
56 
57 /**
58  * Settings screen for managing notification listener permissions
59  */
60 @SearchIndexable
61 public class NotificationAccessSettings extends EmptyTextSettings {
62     private static final String TAG = "NotifAccessSettings";
63     private static final String ALLOWED_KEY = "allowed";
64     private static final String NOT_ALLOWED_KEY = "not_allowed";
65 
66     private static final ManagedServiceSettings.Config CONFIG =
67             new ManagedServiceSettings.Config.Builder()
68                     .setTag(TAG)
69                     .setSetting(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS)
70                     .setIntentAction(NotificationListenerService.SERVICE_INTERFACE)
71                     .setPermission(android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE)
72                     .setNoun("notification listener")
73                     .setWarningDialogTitle(R.string.notification_listener_security_warning_title)
74                     .setWarningDialogSummary(
75                             R.string.notification_listener_security_warning_summary)
76                     .setEmptyText(R.string.no_notification_listeners)
77                     .build();
78 
79     private NotificationManager mNm;
80     protected Context mContext;
81     private PackageManager mPm;
82     private DevicePolicyManager mDpm;
83     private ServiceListing mServiceListing;
84     private IconDrawableFactory mIconDrawableFactory;
85     private NotificationBackend mBackend = new NotificationBackend();
86 
87     @Override
onCreate(Bundle icicle)88     public void onCreate(Bundle icicle) {
89         super.onCreate(icicle);
90 
91         mContext = getActivity();
92         mPm = mContext.getPackageManager();
93         mDpm = (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
94         mIconDrawableFactory = IconDrawableFactory.newInstance(mContext);
95         mServiceListing = new ServiceListing.Builder(mContext)
96                 .setPermission(CONFIG.permission)
97                 .setIntentAction(CONFIG.intentAction)
98                 .setNoun(CONFIG.noun)
99                 .setSetting(CONFIG.setting)
100                 .setTag(CONFIG.tag)
101                 .build();
102         mServiceListing.addCallback(this::updateList);
103 
104         if (UserManager.get(mContext).isManagedProfile()) {
105             // Apps in the work profile do not support notification listeners.
106             Toast.makeText(mContext, R.string.notification_settings_work_profile,
107                     Toast.LENGTH_SHORT).show();
108             finish();
109         }
110     }
111 
112     @Override
onViewCreated(View view, @Nullable Bundle savedInstanceState)113     public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
114         super.onViewCreated(view, savedInstanceState);
115         setEmptyText(CONFIG.emptyText);
116     }
117 
118     @Override
onResume()119     public void onResume() {
120         super.onResume();
121         mServiceListing.reload();
122         mServiceListing.setListening(true);
123     }
124 
125     @Override
onPause()126     public void onPause() {
127         super.onPause();
128         mServiceListing.setListening(false);
129     }
130 
updateList(List<ServiceInfo> services)131     private void updateList(List<ServiceInfo> services) {
132         final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
133         final int managedProfileId = Utils.getManagedProfileId(um, UserHandle.myUserId());
134 
135         final PreferenceScreen screen = getPreferenceScreen();
136         final PreferenceCategory allowedCategory = screen.findPreference(ALLOWED_KEY);
137         allowedCategory.removeAll();
138         final PreferenceCategory notAllowedCategory = screen.findPreference(NOT_ALLOWED_KEY);
139         notAllowedCategory.removeAll();
140 
141         services.sort(new PackageItemInfo.DisplayNameComparator(mPm));
142         for (ServiceInfo service : services) {
143             final ComponentName cn = new ComponentName(service.packageName, service.name);
144             CharSequence title = null;
145             try {
146                 title = mPm.getApplicationInfoAsUser(
147                         service.packageName, 0, UserHandle.myUserId()).loadLabel(mPm);
148             } catch (PackageManager.NameNotFoundException e) {
149                 // unlikely, as we are iterating over live services.
150                 Log.e(TAG, "can't find package name", e);
151             }
152 
153             final AppPreference pref = new AppPreference(getPrefContext());
154             pref.setTitle(title);
155             pref.setIcon(mIconDrawableFactory.getBadgedIcon(service, service.applicationInfo,
156                     UserHandle.getUserId(service.applicationInfo.uid)));
157             pref.setKey(cn.flattenToString());
158             pref.setSummary(mBackend.getDeviceList(ICompanionDeviceManager.Stub.asInterface(
159                     ServiceManager.getService(Context.COMPANION_DEVICE_SERVICE)),
160                     com.android.settings.bluetooth.Utils.getLocalBtManager(mContext),
161                     service.packageName,
162                     UserHandle.myUserId()));
163             if (managedProfileId != UserHandle.USER_NULL
164                     && !mDpm.isNotificationListenerServicePermitted(
165                     service.packageName, managedProfileId)) {
166                 pref.setSummary(R.string.work_profile_notification_access_blocked_summary);
167             }
168             pref.setOnPreferenceClickListener(preference -> {
169                 final Bundle args = new Bundle();
170                 args.putString(AppInfoBase.ARG_PACKAGE_NAME, cn.getPackageName());
171                 args.putInt(AppInfoBase.ARG_PACKAGE_UID, service.applicationInfo.uid);
172 
173                 Bundle extras = new Bundle();
174                 extras.putString(Settings.EXTRA_NOTIFICATION_LISTENER_COMPONENT_NAME,
175                         cn.flattenToString());
176 
177                 new SubSettingLauncher(getContext())
178                         .setDestination(NotificationAccessDetails.class.getName())
179                         .setSourceMetricsCategory(getMetricsCategory())
180                         .setTitleRes(R.string.manage_notification_access_title)
181                         .setArguments(args)
182                         .setExtras(extras)
183                         .setUserHandle(UserHandle.getUserHandleForUid(service.applicationInfo.uid))
184                         .launch();
185                         return true;
186                     });
187             pref.setKey(cn.flattenToString());
188             if (mNm.isNotificationListenerAccessGranted(cn)) {
189                 allowedCategory.addPreference(pref);
190             } else {
191                 notAllowedCategory.addPreference(pref);
192             }
193         }
194         highlightPreferenceIfNeeded();
195     }
196 
197     @Override
getMetricsCategory()198     public int getMetricsCategory() {
199         return SettingsEnums.NOTIFICATION_ACCESS;
200     }
201 
202     @Override
onAttach(Context context)203     public void onAttach(Context context) {
204         super.onAttach(context);
205         mNm = context.getSystemService(NotificationManager.class);
206     }
207 
208     @Override
getPreferenceScreenResId()209     protected int getPreferenceScreenResId() {
210         return R.xml.notification_access_settings;
211     }
212 
213     public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
214             new BaseSearchIndexProvider(R.xml.notification_access_settings);
215 }
216