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