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 finish(); 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 retrieveAppEntry()200 protected void retrieveAppEntry() { 201 final Bundle args = getArguments(); 202 mPackageName = (args != null) ? args.getString(ARG_PACKAGE_NAME) : null; 203 Intent intent = (args == null) ? 204 getIntent() : (Intent) args.getParcelable("intent"); 205 if (mPackageName == null) { 206 if (intent != null && intent.getData() != null) { 207 mPackageName = intent.getData().getSchemeSpecificPart(); 208 } 209 } 210 if (intent != null && intent.hasExtra(Intent.EXTRA_USER_HANDLE)) { 211 mUserId = ((UserHandle) intent.getParcelableExtra( 212 Intent.EXTRA_USER_HANDLE)).getIdentifier(); 213 } else { 214 mUserId = UserHandle.myUserId(); 215 } 216 217 try { 218 mPackageInfo = mPm.getPackageInfoAsUser(mPackageName, 219 PackageManager.MATCH_DISABLED_COMPONENTS | 220 PackageManager.GET_SIGNING_CERTIFICATES | 221 PackageManager.GET_PERMISSIONS, mUserId); 222 } catch (PackageManager.NameNotFoundException e) { 223 // oh well 224 } 225 } 226 227 // Dialogs only have access to the parent fragment, not the controller, so pass the information 228 // along to keep business logic out of this file disable(final ComponentName cn)229 public void disable(final ComponentName cn) { 230 final PreferenceScreen screen = getPreferenceScreen(); 231 ApprovalPreferenceController apc = use(ApprovalPreferenceController.class); 232 apc.disable(cn); 233 apc.updateState(screen.findPreference(apc.getPreferenceKey())); 234 getPreferenceControllers().forEach(controllers -> { 235 controllers.forEach(controller -> { 236 if (controller instanceof TypeFilterPreferenceController) { 237 TypeFilterPreferenceController tfpc = 238 (TypeFilterPreferenceController) controller; 239 tfpc.updateState(screen.findPreference(tfpc.getPreferenceKey())); 240 } 241 }); 242 }); 243 } 244 enable(ComponentName cn)245 protected void enable(ComponentName cn) { 246 final PreferenceScreen screen = getPreferenceScreen(); 247 ApprovalPreferenceController apc = use(ApprovalPreferenceController.class); 248 apc.enable(cn); 249 apc.updateState(screen.findPreference(apc.getPreferenceKey())); 250 getPreferenceControllers().forEach(controllers -> { 251 controllers.forEach(controller -> { 252 if (controller instanceof TypeFilterPreferenceController) { 253 TypeFilterPreferenceController tfpc = 254 (TypeFilterPreferenceController) controller; 255 tfpc.updateState(screen.findPreference(tfpc.getPreferenceKey())); 256 } 257 }); 258 }); 259 } 260 261 // To save binder calls, load this in the fragment rather than each preference controller loadNotificationListenerService()262 protected void loadNotificationListenerService() { 263 mIsNls = false; 264 265 if (mComponentName == null) { 266 return; 267 } 268 Intent intent = new Intent(NotificationListenerService.SERVICE_INTERFACE) 269 .setComponent(mComponentName); 270 List<ResolveInfo> installedServices = mPm.queryIntentServicesAsUser( 271 intent, PackageManager.GET_SERVICES | PackageManager.GET_META_DATA, mUserId); 272 for (ResolveInfo resolveInfo : installedServices) { 273 ServiceInfo info = resolveInfo.serviceInfo; 274 if (android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE.equals( 275 info.permission)) { 276 if (Objects.equals(mComponentName, info.getComponentName())) { 277 mIsNls = true; 278 mServiceName = info.loadLabel(mPm); 279 mServiceInfo = info; 280 break; 281 } 282 } 283 } 284 } 285 286 @Override getPreferenceScreenResId()287 protected int getPreferenceScreenResId() { 288 return R.xml.notification_access_permission_details; 289 } 290 291 @Override getLogTag()292 protected String getLogTag() { 293 return TAG; 294 } 295 }