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