1 /*
2  * Copyright (C) 2023 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.notification.app
18 
19 import android.Manifest.permission.USE_FULL_SCREEN_INTENT
20 import android.app.AppOpsManager
21 import android.app.AppOpsManager.OP_USE_FULL_SCREEN_INTENT
22 import android.content.AttributionSource
23 import android.content.Context
24 import android.content.pm.PackageManager.NameNotFoundException
25 import android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET
26 import android.content.pm.PackageManager.GET_PERMISSIONS
27 import android.os.UserHandle
28 import android.permission.PermissionManager
29 import android.util.Log
30 import androidx.preference.Preference
31 import androidx.preference.Preference.OnPreferenceChangeListener
32 import com.android.settings.notification.NotificationBackend
33 import com.android.settingslib.RestrictedSwitchPreference
34 
35 class FullScreenIntentPermissionPreferenceController(
36     context: Context,
37     backend: NotificationBackend
38 ) : NotificationPreferenceController(context, backend), OnPreferenceChangeListener {
39     private val packageManager = mPm!!
40     private val permissionManager = context.getSystemService(PermissionManager::class.java)!!
41     private val appOpsManager = context.getSystemService(AppOpsManager::class.java)!!
42 
43     private val packageName get() = mAppRow.pkg
44     private val uid get() = mAppRow.uid
45     private val userHandle get() = UserHandle.getUserHandleForUid(uid)
46 
getPreferenceKeynull47     override fun getPreferenceKey() = KEY_FSI_PERMISSION
48 
49     override fun isAvailable(): Boolean {
50         val inAppWidePreferences = mChannelGroup == null && mChannel == null
51 
52         if (!inAppWidePreferences) {
53             Log.wtf(TAG, "Belongs only in app-wide notification preferences!")
54         }
55 
56         return super.isAvailable() && inAppWidePreferences && isPermissionRequested()
57     }
58 
isIncludedInFilternull59     override fun isIncludedInFilter() = false
60 
61     override fun updateState(preference: Preference) {
62         check(KEY_FSI_PERMISSION.equals(preference.key))
63         check(preference is RestrictedSwitchPreference)
64 
65         preference.setDisabledByAdmin(mAdmin)
66         preference.isEnabled = !preference.isDisabledByAdmin
67         preference.isChecked = isPermissionGranted()
68     }
69 
onPreferenceChangenull70     override fun onPreferenceChange(preference: Preference, value: Any): Boolean {
71         check(KEY_FSI_PERMISSION.equals(preference.key))
72         check(preference is RestrictedSwitchPreference)
73         check(value is Boolean)
74 
75         if (isPermissionGranted() != value) {
76             setPermissionGranted(value)
77         }
78 
79         return true
80     }
81 
isPermissionRequestednull82     private fun isPermissionRequested(): Boolean {
83         try {
84             val packageInfo = packageManager.getPackageInfo(packageName, GET_PERMISSIONS)
85 
86             for (requestedPermission in packageInfo.requestedPermissions.orEmpty()) {
87                 if (USE_FULL_SCREEN_INTENT.equals(requestedPermission)) {
88                     return true
89                 }
90             }
91         } catch (exception: NameNotFoundException) {
92             Log.e(TAG, "isPermissionRequested failed: $exception")
93         }
94 
95         return false
96     }
97 
isPermissionGrantednull98     private fun isPermissionGranted(): Boolean {
99         val attributionSource = AttributionSource.Builder(uid).setPackageName(packageName).build()
100 
101         val permissionResult =
102             permissionManager.checkPermissionForPreflight(USE_FULL_SCREEN_INTENT, attributionSource)
103 
104         return (permissionResult == PermissionManager.PERMISSION_GRANTED)
105     }
106 
setPermissionGrantednull107     private fun setPermissionGranted(allowed: Boolean) {
108         val mode = if (allowed) AppOpsManager.MODE_ALLOWED else AppOpsManager.MODE_ERRORED
109         appOpsManager.setUidMode(OP_USE_FULL_SCREEN_INTENT, uid, mode)
110         packageManager.updatePermissionFlags(
111             USE_FULL_SCREEN_INTENT,
112             packageName,
113             FLAG_PERMISSION_USER_SET,
114             FLAG_PERMISSION_USER_SET,
115             userHandle
116         )
117     }
118 
119     companion object {
120         const val KEY_FSI_PERMISSION = "fsi_permission"
121         const val TAG = "FsiPermPrefController"
122     }
123 }
124