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