• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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.applications.specialaccess.notificationaccess;
18 
19 import static com.android.settings.applications.AppInfoBase.ARG_PACKAGE_NAME;
20 
21 import android.app.Activity;
22 import android.app.NotificationManager;
23 import android.app.settings.SettingsEnums;
24 import android.companion.ICompanionDeviceManager;
25 import android.compat.annotation.ChangeId;
26 import android.compat.annotation.EnabledAfter;
27 import android.content.ComponentName;
28 import android.content.Context;
29 import android.content.Intent;
30 import android.content.pm.PackageInfo;
31 import android.content.pm.PackageManager;
32 import android.content.pm.ResolveInfo;
33 import android.content.pm.ServiceInfo;
34 import android.os.Build;
35 import android.os.Bundle;
36 import android.os.ServiceManager;
37 import android.os.UserHandle;
38 import android.os.UserManager;
39 import android.provider.Settings;
40 import android.service.notification.NotificationListenerFilter;
41 import android.service.notification.NotificationListenerService;
42 import android.util.Log;
43 import android.util.Slog;
44 
45 import androidx.preference.Preference;
46 import androidx.preference.PreferenceScreen;
47 
48 import com.android.settings.R;
49 import com.android.settings.SettingsActivity;
50 import com.android.settings.applications.AppInfoBase;
51 import com.android.settings.applications.manageapplications.ManageApplications;
52 import com.android.settings.bluetooth.Utils;
53 import com.android.settings.core.SubSettingLauncher;
54 import com.android.settings.dashboard.DashboardFragment;
55 import com.android.settings.notification.NotificationBackend;
56 import com.android.settingslib.RestrictedLockUtils;
57 import com.android.settingslib.RestrictedLockUtilsInternal;
58 
59 import java.util.List;
60 import java.util.Objects;
61 
62 public class NotificationAccessDetails extends DashboardFragment {
63     private static final String TAG = "NotifAccessDetails";
64 
65     private NotificationBackend mNm = new NotificationBackend();
66     private ComponentName mComponentName;
67     private CharSequence mServiceName;
68     protected ServiceInfo mServiceInfo;
69     protected PackageInfo mPackageInfo;
70     protected int mUserId;
71     protected String mPackageName;
72     protected RestrictedLockUtils.EnforcedAdmin mAppsControlDisallowedAdmin;
73     protected boolean mAppsControlDisallowedBySystem;
74     private boolean mIsNls;
75     private PackageManager mPm;
76 
77     @Override
onAttach(Context context)78     public void onAttach(Context context) {
79         super.onAttach(context);
80         final Intent intent = getIntent();
81         if (mComponentName == null && intent != null) {
82             String cn = intent.getStringExtra(Settings.EXTRA_NOTIFICATION_LISTENER_COMPONENT_NAME);
83             if (cn != null) {
84                 mComponentName = ComponentName.unflattenFromString(cn);
85                 if (mComponentName != null) {
86                     final Bundle args = getArguments();
87                     args.putString(ARG_PACKAGE_NAME, mComponentName.getPackageName());
88                 }
89             }
90         }
91         mPm = getPackageManager();
92         retrieveAppEntry();
93         loadNotificationListenerService();
94         NotificationBackend backend = new NotificationBackend();
95         int listenerTargetSdk = Build.VERSION_CODES.S;
96         try {
97             listenerTargetSdk = mPm.getTargetSdkVersion(mComponentName.getPackageName());
98         } catch (PackageManager.NameNotFoundException e){
99             // how did we get here?
100         }
101         use(ApprovalPreferenceController.class)
102                 .setPkgInfo(mPackageInfo)
103                 .setCn(mComponentName)
104                 .setNm(context.getSystemService(NotificationManager.class))
105                 .setPm(mPm)
106                 .setParent(this);
107         use(HeaderPreferenceController.class)
108                 .setFragment(this)
109                 .setPackageInfo(mPackageInfo)
110                 .setPm(context.getPackageManager())
111                 .setServiceName(mServiceName)
112                 .setBluetoothManager(Utils.getLocalBtManager(context))
113                 .setCdm(ICompanionDeviceManager.Stub.asInterface(
114                         ServiceManager.getService(Context.COMPANION_DEVICE_SERVICE)))
115                 .setCn(mComponentName)
116                 .setUserId(mUserId);
117         use(PreUpgradePreferenceController.class)
118                 .setNm(backend)
119                 .setCn(mComponentName)
120                 .setUserId(mUserId)
121                 .setTargetSdk(listenerTargetSdk);
122         use(BridgedAppsLinkPreferenceController.class)
123                 .setNm(backend)
124                 .setCn(mComponentName)
125                 .setUserId(mUserId)
126                 .setTargetSdk(listenerTargetSdk);
127         final int finalListenerTargetSdk = listenerTargetSdk;
128         getPreferenceControllers().forEach(controllers -> {
129             controllers.forEach(controller -> {
130                 if (controller instanceof TypeFilterPreferenceController) {
131                     TypeFilterPreferenceController tfpc =
132                             (TypeFilterPreferenceController) controller;
133                     tfpc.setNm(backend)
134                             .setCn(mComponentName)
135                             .setServiceInfo(mServiceInfo)
136                             .setUserId(mUserId)
137                             .setTargetSdk(finalListenerTargetSdk);
138                 }
139             });
140         });
141     }
142 
143     @Override
getMetricsCategory()144     public int getMetricsCategory() {
145         return SettingsEnums.NOTIFICATION_ACCESS_DETAIL;
146     }
147 
refreshUi()148     protected boolean refreshUi() {
149         if (mComponentName == null) {
150             // No service given
151             Slog.d(TAG, "No component name provided");
152             return false;
153         }
154         if (!mIsNls) {
155             // This component doesn't have the right androidmanifest definition to be an NLS
156             Slog.d(TAG, "Provided component name is not an NLS");
157             return false;
158         }
159         if (UserManager.get(getContext()).isManagedProfile()) {
160             // Apps in the work profile do not support notification listeners.
161             Slog.d(TAG, "NLSes aren't allowed in work profiles");
162             return false;
163         }
164         return true;
165     }
166 
167     @Override
onResume()168     public void onResume() {
169         super.onResume();
170         mAppsControlDisallowedAdmin = RestrictedLockUtilsInternal.checkIfRestrictionEnforced(
171                 getActivity(), UserManager.DISALLOW_APPS_CONTROL, mUserId);
172         mAppsControlDisallowedBySystem = RestrictedLockUtilsInternal.hasBaseUserRestriction(
173                 getActivity(), UserManager.DISALLOW_APPS_CONTROL, mUserId);
174 
175         if (!refreshUi()) {
176             setIntentAndFinish(true /* appChanged */);
177         }
178         Preference apps = getPreferenceScreen().findPreference(
179                 use(BridgedAppsLinkPreferenceController.class).getPreferenceKey());
180         if (apps != null) {
181 
182             apps.setOnPreferenceClickListener(preference -> {
183                 final Bundle args = new Bundle();
184                 args.putString(AppInfoBase.ARG_PACKAGE_NAME, mPackageName);
185                 args.putString(Settings.EXTRA_NOTIFICATION_LISTENER_COMPONENT_NAME,
186                         mComponentName.flattenToString());
187 
188                 new SubSettingLauncher(getContext())
189                         .setDestination(BridgedAppsSettings.class.getName())
190                         .setSourceMetricsCategory(getMetricsCategory())
191                         .setTitleRes(R.string.notif_listener_excluded_app_screen_title)
192                         .setArguments(args)
193                         .setUserHandle(UserHandle.of(mUserId))
194                         .launch();
195                 return true;
196             });
197         }
198     }
199 
setIntentAndFinish(boolean appChanged)200     protected void setIntentAndFinish(boolean appChanged) {
201         Log.i(TAG, "appChanged=" + appChanged);
202         Intent intent = new Intent();
203         intent.putExtra(ManageApplications.APP_CHG, appChanged);
204         SettingsActivity sa = (SettingsActivity) getActivity();
205         sa.finishPreferencePanel(Activity.RESULT_OK, intent);
206     }
207 
retrieveAppEntry()208     protected void retrieveAppEntry() {
209         final Bundle args = getArguments();
210         mPackageName = (args != null) ? args.getString(ARG_PACKAGE_NAME) : null;
211         Intent intent = (args == null) ?
212                 getIntent() : (Intent) args.getParcelable("intent");
213         if (mPackageName == null) {
214             if (intent != null && intent.getData() != null) {
215                 mPackageName = intent.getData().getSchemeSpecificPart();
216             }
217         }
218         if (intent != null && intent.hasExtra(Intent.EXTRA_USER_HANDLE)) {
219             mUserId = ((UserHandle) intent.getParcelableExtra(
220                     Intent.EXTRA_USER_HANDLE)).getIdentifier();
221         } else {
222             mUserId = UserHandle.myUserId();
223         }
224 
225         try {
226             mPackageInfo = mPm.getPackageInfoAsUser(mPackageName,
227                     PackageManager.MATCH_DISABLED_COMPONENTS |
228                             PackageManager.GET_SIGNING_CERTIFICATES |
229                             PackageManager.GET_PERMISSIONS, mUserId);
230         } catch (PackageManager.NameNotFoundException e) {
231             Log.e(TAG, "Exception when retrieving package:" + mPackageName, e);
232         }
233     }
234 
235     // Dialogs only have access to the parent fragment, not the controller, so pass the information
236     // along to keep business logic out of this file
disable(final ComponentName cn)237     public void disable(final ComponentName cn) {
238         final PreferenceScreen screen = getPreferenceScreen();
239         ApprovalPreferenceController apc = use(ApprovalPreferenceController.class);
240         apc.disable(cn);
241         apc.updateState(screen.findPreference(apc.getPreferenceKey()));
242         getPreferenceControllers().forEach(controllers -> {
243             controllers.forEach(controller -> {
244                 if (controller instanceof TypeFilterPreferenceController) {
245                     TypeFilterPreferenceController tfpc =
246                             (TypeFilterPreferenceController) controller;
247                     tfpc.updateState(screen.findPreference(tfpc.getPreferenceKey()));
248                 }
249             });
250         });
251     }
252 
enable(ComponentName cn)253     protected void enable(ComponentName cn) {
254         final PreferenceScreen screen = getPreferenceScreen();
255         ApprovalPreferenceController apc = use(ApprovalPreferenceController.class);
256         apc.enable(cn);
257         apc.updateState(screen.findPreference(apc.getPreferenceKey()));
258         getPreferenceControllers().forEach(controllers -> {
259             controllers.forEach(controller -> {
260                 if (controller instanceof TypeFilterPreferenceController) {
261                     TypeFilterPreferenceController tfpc =
262                             (TypeFilterPreferenceController) controller;
263                     tfpc.updateState(screen.findPreference(tfpc.getPreferenceKey()));
264                 }
265             });
266         });
267     }
268 
269     // To save binder calls, load this in the fragment rather than each preference controller
loadNotificationListenerService()270     protected void loadNotificationListenerService() {
271         mIsNls = false;
272 
273         if (mComponentName == null) {
274             return;
275         }
276         Intent intent = new Intent(NotificationListenerService.SERVICE_INTERFACE)
277                 .setComponent(mComponentName);
278         List<ResolveInfo> installedServices = mPm.queryIntentServicesAsUser(
279                 intent, PackageManager.GET_SERVICES | PackageManager.GET_META_DATA, mUserId);
280         for (ResolveInfo resolveInfo : installedServices) {
281             ServiceInfo info = resolveInfo.serviceInfo;
282             if (android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE.equals(
283                     info.permission)) {
284                 if (Objects.equals(mComponentName, info.getComponentName())) {
285                     mIsNls = true;
286                     mServiceName = info.loadLabel(mPm);
287                     mServiceInfo = info;
288                     break;
289                 }
290             }
291         }
292     }
293 
294     @Override
getPreferenceScreenResId()295     protected int getPreferenceScreenResId() {
296         return R.xml.notification_access_permission_details;
297     }
298 
299     @Override
getLogTag()300     protected String getLogTag() {
301         return TAG;
302     }
303 }