1 /*
2  * Copyright (C) 2017 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.tv.settings.device.apps.specialaccess;
18 
19 import android.app.NotificationManager;
20 import android.app.tvsettings.TvSettingsEnums;
21 import android.content.ComponentName;
22 import android.content.Context;
23 import android.content.pm.PackageItemInfo;
24 import android.content.pm.PackageManager;
25 import android.content.pm.ServiceInfo;
26 import android.os.Bundle;
27 import android.os.UserHandle;
28 import android.provider.Settings;
29 import android.service.notification.NotificationListenerService;
30 import android.util.ArraySet;
31 import android.util.IconDrawableFactory;
32 import android.util.Log;
33 
34 import androidx.annotation.Keep;
35 import androidx.preference.Preference;
36 import androidx.preference.PreferenceScreen;
37 import androidx.preference.SwitchPreference;
38 
39 import com.android.settingslib.applications.ServiceListing;
40 import com.android.tv.settings.R;
41 import com.android.tv.settings.SettingsPreferenceFragment;
42 
43 import java.util.List;
44 
45 /**
46  * Settings screen for managing notification listener permissions
47  */
48 @Keep
49 public class NotificationAccess extends SettingsPreferenceFragment {
50     private static final String TAG = "NotificationAccess";
51 
52     private static final String HEADER_KEY = "header";
53 
54     private static final String DEFAULT_PACKAGES_SEPARATOR = ":";
55     private ArraySet<String> mDefaultPackages;
56 
57     private NotificationManager mNotificationManager;
58     private PackageManager mPackageManager;
59     private ServiceListing mServiceListing;
60     private IconDrawableFactory mIconDrawableFactory;
61 
62     @Override
onAttach(Context context)63     public void onAttach(Context context) {
64         super.onAttach(context);
65         mPackageManager = context.getPackageManager();
66         mNotificationManager = context.getSystemService(NotificationManager.class);
67         mIconDrawableFactory = IconDrawableFactory.newInstance(context);
68     }
69 
70     @Override
onCreate(Bundle savedInstanceState)71     public void onCreate(Bundle savedInstanceState) {
72         super.onCreate(savedInstanceState);
73         mServiceListing = new ServiceListing.Builder(getContext())
74                 .setTag(TAG)
75                 .setSetting(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS)
76                 .setIntentAction(NotificationListenerService.SERVICE_INTERFACE)
77                 .setPermission(android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE)
78                 .setNoun("notification listener")
79                 .build();
80         mServiceListing.addCallback(this::updateList);
81     }
82 
83     @Override
onResume()84     public void onResume() {
85         super.onResume();
86         mServiceListing.reload();
87         mServiceListing.setListening(true);
88     }
89 
90     @Override
onPause()91     public void onPause() {
92         super.onPause();
93         mServiceListing.setListening(false);
94     }
95 
96     @Override
onCreatePreferences(Bundle savedInstanceState, String rootKey)97     public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
98         setPreferencesFromResource(R.xml.notification_access, null);
99 
100         String packages = getString(
101                 com.android.internal.R.string.config_defaultListenerAccessPackages);
102         mDefaultPackages = new ArraySet<String>(packages.split(DEFAULT_PACKAGES_SEPARATOR));
103     }
104 
updateList(List<ServiceInfo> services)105     private void updateList(List<ServiceInfo> services) {
106         final PreferenceScreen screen = getPreferenceScreen();
107         final Preference header = screen.findPreference(HEADER_KEY);
108         screen.removeAll();
109         if (header != null) {
110             screen.addPreference(header);
111         }
112         services.sort(new PackageItemInfo.DisplayNameComparator(mPackageManager));
113         for (ServiceInfo service : services) {
114             final ComponentName cn = new ComponentName(service.packageName, service.name);
115             CharSequence title = null;
116             try {
117                 title = mPackageManager.getApplicationInfo(
118                         service.packageName, 0).loadLabel(mPackageManager);
119             } catch (PackageManager.NameNotFoundException e) {
120                 // unlikely, as we are iterating over live services.
121                 Log.w(TAG, "can't find package name", e);
122             }
123             final String summary = service.loadLabel(mPackageManager).toString();
124             final SwitchPreference pref = new SwitchPreference(getPreferenceManager().getContext());
125             pref.setPersistent(false);
126             pref.setIcon(mIconDrawableFactory.getBadgedIcon(service, service.applicationInfo,
127                     UserHandle.getUserId(service.applicationInfo.uid)));
128             if (title != null && !title.equals(summary)) {
129                 pref.setTitle(title);
130                 pref.setSummary(summary);
131             } else {
132                 pref.setTitle(summary);
133             }
134             pref.setKey(cn.flattenToString());
135             pref.setChecked(mNotificationManager.isNotificationListenerAccessGranted(cn));
136             // Prevent the user from removing access from a default notification listener.
137             if (mDefaultPackages.contains(cn.getPackageName()) && pref.isChecked()) {
138                 pref.setEnabled(false);
139                 pref.setSummary(R.string.default_notification_access_package_summary);
140             }
141             pref.setOnPreferenceChangeListener((preference, newValue) -> {
142                 final boolean enable = (boolean) newValue;
143                 mNotificationManager.setNotificationListenerAccessGranted(cn, enable);
144                 return true;
145             });
146             screen.addPreference(pref);
147         }
148         if (services.isEmpty()) {
149             final Preference preference = new Preference(getPreferenceManager().getContext());
150             preference.setTitle(R.string.no_notification_listeners);
151         }
152     }
153 
154     @Override
getPageId()155     protected int getPageId() {
156         return TvSettingsEnums.APPS_SPECIAL_APP_ACCESS_NOTIFICATION_ACCESS;
157     }
158 }
159