Add FSI toggle to per-app notification settings

Bug: 277938609
Test: atest FullScreenIntentPermissionPreferenceControllerTest
Test: # manual, verifying against "Special app access" screen
Change-Id: I9cb0d9bc99ce59a7b0ff6bcd2cab7a3c2d63f45f
This commit is contained in:
Julia Tuttle
2023-04-18 14:59:54 -04:00
parent 44a613438b
commit 6630c852d9
5 changed files with 452 additions and 9 deletions

View File

@@ -74,6 +74,7 @@ public class AppNotificationSettings extends NotificationSettings {
mControllers = new ArrayList<>();
mControllers.add(new HeaderPreferenceController(context, this));
mControllers.add(new BlockPreferenceController(context, mDependentFieldListener, mBackend));
mControllers.add(new FullScreenIntentPermissionPreferenceController(context, mBackend));
mControllers.add(new BadgePreferenceController(context, mBackend));
mControllers.add(new AllowSoundPreferenceController(
context, mDependentFieldListener, mBackend));

View File

@@ -0,0 +1,118 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.notification.app
import android.Manifest.permission.USE_FULL_SCREEN_INTENT
import android.app.AppOpsManager
import android.app.AppOpsManager.OP_USE_FULL_SCREEN_INTENT
import android.content.AttributionSource
import android.content.Context
import android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET
import android.content.pm.PackageManager.GET_PERMISSIONS
import android.os.UserHandle
import android.permission.PermissionManager
import android.util.Log
import androidx.preference.Preference
import androidx.preference.Preference.OnPreferenceChangeListener
import com.android.settings.notification.NotificationBackend
import com.android.settingslib.RestrictedSwitchPreference
class FullScreenIntentPermissionPreferenceController(
context: Context,
backend: NotificationBackend
) : NotificationPreferenceController(context, backend), OnPreferenceChangeListener {
private val packageManager = mPm!!
private val permissionManager = context.getSystemService(PermissionManager::class.java)!!
private val appOpsManager = context.getSystemService(AppOpsManager::class.java)!!
private val packageName get() = mAppRow.pkg
private val uid get() = mAppRow.uid
private val userHandle get() = UserHandle.getUserHandleForUid(uid)
override fun getPreferenceKey() = KEY_FSI_PERMISSION
override fun isAvailable(): Boolean {
val inAppWidePreferences = mChannelGroup == null && mChannel == null
if (!inAppWidePreferences) {
Log.wtf(TAG, "Belongs only in app-wide notification preferences!")
}
return super.isAvailable() && inAppWidePreferences && isPermissionRequested()
}
override fun isIncludedInFilter() = false
override fun updateState(preference: Preference) {
check(KEY_FSI_PERMISSION.equals(preference.key))
check(preference is RestrictedSwitchPreference)
preference.setDisabledByAdmin(mAdmin)
preference.isEnabled = !preference.isDisabledByAdmin
preference.isChecked = isPermissionGranted()
}
override fun onPreferenceChange(preference: Preference, value: Any): Boolean {
check(KEY_FSI_PERMISSION.equals(preference.key))
check(preference is RestrictedSwitchPreference)
check(value is Boolean)
if (isPermissionGranted() != value) {
setPermissionGranted(value)
}
return true
}
private fun isPermissionRequested(): Boolean {
val packageInfo = packageManager.getPackageInfo(packageName, GET_PERMISSIONS)
for (requestedPermission in packageInfo.requestedPermissions) {
if (USE_FULL_SCREEN_INTENT.equals(requestedPermission)) {
return true
}
}
return false
}
private fun isPermissionGranted(): Boolean {
val attributionSource = AttributionSource.Builder(uid).setPackageName(packageName).build()
val permissionResult =
permissionManager.checkPermissionForPreflight(USE_FULL_SCREEN_INTENT, attributionSource)
return (permissionResult == PermissionManager.PERMISSION_GRANTED)
}
private fun setPermissionGranted(allowed: Boolean) {
val mode = if (allowed) AppOpsManager.MODE_ALLOWED else AppOpsManager.MODE_ERRORED
appOpsManager.setUidMode(OP_USE_FULL_SCREEN_INTENT, uid, mode)
packageManager.updatePermissionFlags(
USE_FULL_SCREEN_INTENT,
packageName,
FLAG_PERMISSION_USER_SET,
FLAG_PERMISSION_USER_SET,
userHandle
)
}
companion object {
const val KEY_FSI_PERMISSION = "fsi_permission"
const val TAG = "FsiPermPrefController"
}
}