• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 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.settingslib.metadata
18 
19 import android.content.Context
20 import androidx.annotation.ArrayRes
21 import androidx.annotation.IntDef
22 import com.android.settingslib.datastore.KeyValueStore
23 import com.android.settingslib.datastore.Permissions
24 
25 /** Permit of read and write request. */
26 @IntDef(
27     ReadWritePermit.ALLOW,
28     ReadWritePermit.DISALLOW,
29     ReadWritePermit.REQUIRE_APP_PERMISSION,
30     ReadWritePermit.REQUIRE_USER_AGREEMENT,
31 )
32 @Retention(AnnotationRetention.SOURCE)
33 @Target(AnnotationTarget.TYPE)
34 annotation class ReadWritePermit {
35     companion object {
36         /** Allow to read/write value. */
37         const val ALLOW = 0
38         /** Disallow to read/write value (e.g. uid not allowed). */
39         const val DISALLOW = 1
40         /** Require (runtime/special) app permission from user explicitly. */
41         const val REQUIRE_APP_PERMISSION = 2
42         /** Require explicit user agreement (e.g. terms of service). */
43         const val REQUIRE_USER_AGREEMENT = 3
44 
45         private const val READ_PERMIT_BITS = 15
46         private const val READ_PERMIT_MASK = (1 shl 16) - 1
47 
48         /** Wraps given read and write permit into an integer. */
makenull49         fun make(readPermit: @ReadWritePermit Int, writePermit: @ReadWritePermit Int): Int =
50             (writePermit shl READ_PERMIT_BITS) or readPermit
51 
52         /** Extracts the read permit from given integer generated by [make]. */
53         fun getReadPermit(readWritePermit: Int): Int = readWritePermit and READ_PERMIT_MASK
54 
55         /** Extracts the write permit from given integer generated by [make]. */
56         fun getWritePermit(readWritePermit: Int): Int = readWritePermit shr READ_PERMIT_BITS
57     }
58 }
59 
60 /** The reason of preference change. */
61 @IntDef(
62     PreferenceChangeReason.VALUE,
63     PreferenceChangeReason.STATE,
64     PreferenceChangeReason.DEPENDENT,
65 )
66 @Retention(AnnotationRetention.SOURCE)
67 annotation class PreferenceChangeReason {
68     companion object {
69         /** Preference value is changed. */
70         const val VALUE = 1000
71         /** Preference state (title/summary, enable state, etc.) is changed. */
72         const val STATE = 1001
73         /** Dependent preference state is changed. */
74         const val DEPENDENT = 1002
75     }
76 }
77 
78 /** Indicates how sensitive of the data. */
79 @Retention(AnnotationRetention.SOURCE)
80 @Target(AnnotationTarget.TYPE)
81 annotation class SensitivityLevel {
82     companion object {
83         const val UNKNOWN_SENSITIVITY = 0
84         const val NO_SENSITIVITY = 1
85         const val LOW_SENSITIVITY = 2
86         const val MEDIUM_SENSITIVITY = 3
87         const val HIGH_SENSITIVITY = 4
88     }
89 }
90 
91 /** Preference metadata that has a value persisted in datastore. */
92 interface PersistentPreference<T> : PreferenceMetadata {
93 
94     /** The value type the preference is associated with. */
95     val valueType: Class<T>
96 
97     /** The sensitivity level of the preference. */
98     val sensitivityLevel: @SensitivityLevel Int
99         get() = SensitivityLevel.UNKNOWN_SENSITIVITY
100 
isPersistentnull101     override fun isPersistent(context: Context) = true
102 
103     /**
104      * Returns the key-value storage of the preference.
105      *
106      * The default implementation returns the storage provided by
107      * [PreferenceScreenRegistry.getKeyValueStore].
108      */
109     fun storage(context: Context): KeyValueStore =
110         PreferenceScreenRegistry.getKeyValueStore(context, this)!!
111 
112     /** Returns the required permissions to read preference value. */
113     fun getReadPermissions(context: Context): Permissions? = null
114 
115     /**
116      * Returns if the external application (identified by [callingPid] and [callingUid]) is
117      * permitted to read preference value.
118      *
119      * The underlying implementation does NOT need to check common states like isEnabled,
120      * isRestricted, isAvailable or permissions in [getReadPermissions]. The framework will do it
121      * behind the scene.
122      */
123     fun getReadPermit(context: Context, callingPid: Int, callingUid: Int): @ReadWritePermit Int =
124         PreferenceScreenRegistry.defaultReadPermit
125 
126     /** Returns the required permissions to write preference value. */
127     fun getWritePermissions(context: Context): Permissions? = null
128 
129     /**
130      * Returns if the external application (identified by [callingPid] and [callingUid]) is
131      * permitted to write preference value. If the write permit depends on certain value, implement
132      * the overloading [getWritePermit] instead.
133      *
134      * The underlying implementation does NOT need to check common states like isEnabled,
135      * isRestricted, isAvailable or permissions in [getWritePermissions]. The framework will do it
136      * behind the scene.
137      */
138     fun getWritePermit(context: Context, callingPid: Int, callingUid: Int): @ReadWritePermit Int? =
139         null
140 
141     /**
142      * Returns if the external application (identified by [callingPid] and [callingUid]) is
143      * permitted to write preference with given [value]. Note that if the overloading
144      * [getWritePermit] returns non null value, this method will be ignored!
145      *
146      * The underlying implementation does NOT need to check common states like isEnabled,
147      * isRestricted, isAvailable or permissions in [getWritePermissions]. The framework will do it
148      * behind the scene.
149      */
150     fun getWritePermit(
151         context: Context,
152         value: T?,
153         callingPid: Int,
154         callingUid: Int,
155     ): @ReadWritePermit Int = PreferenceScreenRegistry.defaultWritePermit
156 }
157 
158 /** Descriptor of values. */
159 sealed interface ValueDescriptor {
160 
161     /** Returns if given value (represented by index) is valid. */
162     fun isValidValue(context: Context, index: Int): Boolean
163 }
164 
165 /** Value falls into a given array. */
166 interface DiscreteValue<T> : ValueDescriptor {
167     @get:ArrayRes val values: Int
168 
169     @get:ArrayRes val valuesDescription: Int
170 
getValuenull171     fun getValue(context: Context, index: Int): T
172 }
173 
174 /**
175  * Value falls into a text array, whose element is [CharSequence] type.
176  *
177  * [values] resource is `<string-array>`.
178  */
179 interface DiscreteTextValue : DiscreteValue<CharSequence> {
180     override fun isValidValue(context: Context, index: Int): Boolean {
181         if (index < 0) return false
182         return index < context.resources.getTextArray(values).size
183     }
184 
185     override fun getValue(context: Context, index: Int): CharSequence =
186         context.resources.getTextArray(values)[index]
187 }
188 
189 /**
190  * Value falls into a string array, whose element is [String] type.
191  *
192  * [values] resource is `<string-array>`.
193  */
194 interface DiscreteStringValue : DiscreteValue<String> {
isValidValuenull195     override fun isValidValue(context: Context, index: Int): Boolean {
196         if (index < 0) return false
197         return index < context.resources.getStringArray(values).size
198     }
199 
getValuenull200     override fun getValue(context: Context, index: Int): String =
201         context.resources.getStringArray(values)[index]
202 }
203 
204 /**
205  * Value falls into an integer array.
206  *
207  * [values] resource is `<integer-array>`.
208  */
209 interface DiscreteIntValue : DiscreteValue<Int> {
210     override fun isValidValue(context: Context, index: Int): Boolean {
211         if (index < 0) return false
212         return index < context.resources.getIntArray(values).size
213     }
214 
215     override fun getValue(context: Context, index: Int): Int =
216         context.resources.getIntArray(values)[index]
217 }
218