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