Loading src/com/android/settings/spa/app/catalyst/AppsNotificationAccessScreen.kt +90 −11 Original line number Diff line number Diff line Loading @@ -17,17 +17,20 @@ package com.android.settings.spa.app.catalyst import android.Manifest import android.app.ActivityManager import android.content.ComponentName import android.content.Context import android.content.Intent import android.content.pm.PackageManager import android.content.pm.ServiceInfo import android.os.Bundle import android.provider.Settings import android.provider.Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS import android.service.notification.NotificationListenerService import android.util.Log import com.android.settings.R import com.android.settings.contract.TAG_DEVICE_STATE_SCREEN import com.android.settings.flags.Flags import com.android.settingslib.applications.ServiceListing import com.android.settingslib.metadata.PreferenceMetadata import com.android.settingslib.metadata.ProvidePreferenceScreen import com.android.settingslib.metadata.preferenceHierarchy Loading Loading @@ -73,18 +76,94 @@ class AppsNotificationAccessScreen : PreferenceScreenCreator { companion object { const val KEY = "device_state_apps_notification_access" // TODO: b/416239475 - unify this code with ServiceListing.java @JvmStatic fun loadNotificationListenerServices(context: Context): List<ServiceInfo> { val serviceListing = ServiceListing.Builder(context) .setIntentAction(NotificationListenerService.SERVICE_INTERFACE) .setPermission(Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE) .setNoun("notification listener") .setSetting(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS) .setTag(TAG_DEVICE_STATE_SCREEN) .build() val services = serviceListing.load() private fun loadEnabledServices(context: Context, setting: String): List<ComponentName> { val enabledServices = mutableListOf<ComponentName>() enabledServices.clear() val contentResolver = context.getContentResolver() val flat = Settings.Secure.getString(contentResolver, setting) if (!flat.isNullOrEmpty()) { val names = flat.split(":") for (name in names) { ComponentName.unflattenFromString(name)?.let { enabledServices.add(it) } } } return enabledServices } /** * Loads services matching the given intent and permission, filtered to the apps that * request the service via a setting. * * This is based on com.android.settingslib.applications.ServiceListing#load(). * * @param context The context to use. * @param intentAction The intent action to match. * @param permission The permission to require. * @param setting The setting to use to store enabled services. * @return A list of services that match the given intent and permission. */ // TODO: b/416239475 - unify this code with ServiceListing.java @JvmStatic private fun loadServices( context: Context, intentAction: String, permission: String, setting: String, ): List<ServiceInfo> { val enabledServices = loadEnabledServices(context, setting) val services = mutableListOf<ServiceInfo>() val user = ActivityManager.getCurrentUser() var flags = PackageManager.GET_SERVICES or PackageManager.GET_META_DATA // Add requesting apps, with full validation val installedServices = context.packageManager.queryIntentServicesAsUser(Intent(intentAction), flags, user) for (resolveInfo in installedServices) { val info = resolveInfo.serviceInfo if (info.componentName !in enabledServices) { if (permission != info.permission) { Log.w( TAG_DEVICE_STATE_SCREEN, "Skipping service ${info.packageName}/${info.name}: " + "it does not require the permission $permission", ) continue } services.add(info) } } // Add all apps with access, in case prior approval was granted without full validation for (componentName in enabledServices) { val enabledServicesResolveInfo = context.packageManager.queryIntentServicesAsUser( Intent().setComponent(componentName), flags, user, ) for (resolveInfo in enabledServicesResolveInfo) { val info = resolveInfo.serviceInfo services.add(info) } } return services } @JvmStatic fun loadNotificationListenerServices(context: Context): List<ServiceInfo> { return loadServices( context, NotificationListenerService.SERVICE_INTERFACE, Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE, Settings.Secure.ENABLED_NOTIFICATION_LISTENERS, ) } } } Loading
src/com/android/settings/spa/app/catalyst/AppsNotificationAccessScreen.kt +90 −11 Original line number Diff line number Diff line Loading @@ -17,17 +17,20 @@ package com.android.settings.spa.app.catalyst import android.Manifest import android.app.ActivityManager import android.content.ComponentName import android.content.Context import android.content.Intent import android.content.pm.PackageManager import android.content.pm.ServiceInfo import android.os.Bundle import android.provider.Settings import android.provider.Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS import android.service.notification.NotificationListenerService import android.util.Log import com.android.settings.R import com.android.settings.contract.TAG_DEVICE_STATE_SCREEN import com.android.settings.flags.Flags import com.android.settingslib.applications.ServiceListing import com.android.settingslib.metadata.PreferenceMetadata import com.android.settingslib.metadata.ProvidePreferenceScreen import com.android.settingslib.metadata.preferenceHierarchy Loading Loading @@ -73,18 +76,94 @@ class AppsNotificationAccessScreen : PreferenceScreenCreator { companion object { const val KEY = "device_state_apps_notification_access" // TODO: b/416239475 - unify this code with ServiceListing.java @JvmStatic fun loadNotificationListenerServices(context: Context): List<ServiceInfo> { val serviceListing = ServiceListing.Builder(context) .setIntentAction(NotificationListenerService.SERVICE_INTERFACE) .setPermission(Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE) .setNoun("notification listener") .setSetting(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS) .setTag(TAG_DEVICE_STATE_SCREEN) .build() val services = serviceListing.load() private fun loadEnabledServices(context: Context, setting: String): List<ComponentName> { val enabledServices = mutableListOf<ComponentName>() enabledServices.clear() val contentResolver = context.getContentResolver() val flat = Settings.Secure.getString(contentResolver, setting) if (!flat.isNullOrEmpty()) { val names = flat.split(":") for (name in names) { ComponentName.unflattenFromString(name)?.let { enabledServices.add(it) } } } return enabledServices } /** * Loads services matching the given intent and permission, filtered to the apps that * request the service via a setting. * * This is based on com.android.settingslib.applications.ServiceListing#load(). * * @param context The context to use. * @param intentAction The intent action to match. * @param permission The permission to require. * @param setting The setting to use to store enabled services. * @return A list of services that match the given intent and permission. */ // TODO: b/416239475 - unify this code with ServiceListing.java @JvmStatic private fun loadServices( context: Context, intentAction: String, permission: String, setting: String, ): List<ServiceInfo> { val enabledServices = loadEnabledServices(context, setting) val services = mutableListOf<ServiceInfo>() val user = ActivityManager.getCurrentUser() var flags = PackageManager.GET_SERVICES or PackageManager.GET_META_DATA // Add requesting apps, with full validation val installedServices = context.packageManager.queryIntentServicesAsUser(Intent(intentAction), flags, user) for (resolveInfo in installedServices) { val info = resolveInfo.serviceInfo if (info.componentName !in enabledServices) { if (permission != info.permission) { Log.w( TAG_DEVICE_STATE_SCREEN, "Skipping service ${info.packageName}/${info.name}: " + "it does not require the permission $permission", ) continue } services.add(info) } } // Add all apps with access, in case prior approval was granted without full validation for (componentName in enabledServices) { val enabledServicesResolveInfo = context.packageManager.queryIntentServicesAsUser( Intent().setComponent(componentName), flags, user, ) for (resolveInfo in enabledServicesResolveInfo) { val info = resolveInfo.serviceInfo services.add(info) } } return services } @JvmStatic fun loadNotificationListenerServices(context: Context): List<ServiceInfo> { return loadServices( context, NotificationListenerService.SERVICE_INTERFACE, Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE, Settings.Secure.ENABLED_NOTIFICATION_LISTENERS, ) } } }