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