1 /* <lambda>null2 * Copyright 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 androidx.security.state.provider 18 19 import android.content.Context 20 import androidx.security.state.SecurityPatchState 21 import androidx.security.state.SecurityPatchState.Companion.getComponentSecurityPatchLevel 22 import kotlinx.serialization.json.Json 23 24 /** 25 * This class interfaces with a [SecurityPatchState] to manage update information for system 26 * components. 27 * 28 * Typically, OTA or other update clients utilize this class to expose update information to other 29 * applications or components within the system that need access to the latest security updates 30 * data. The client calls [registerUpdate] to add [UpdateInfo] to a local store and 31 * [unregisterUpdate] to remove it. [UpdateInfoProvider] then serves this data to the applications. 32 */ 33 public class UpdateInfoManager( 34 private val context: Context, 35 customSecurityState: SecurityPatchState? = null 36 ) { 37 38 private val updateInfoPrefs: String = "UPDATE_INFO_PREFS" 39 private var securityState: SecurityPatchState = 40 customSecurityState ?: SecurityPatchState(context) 41 42 /** 43 * Registers information about an available update for the specified component. 44 * 45 * @param updateInfo Update information structure. 46 */ 47 public fun registerUpdate(updateInfo: UpdateInfo) { 48 cleanupUpdateInfo() 49 50 val sharedPreferences = context.getSharedPreferences(updateInfoPrefs, Context.MODE_PRIVATE) 51 val editor = sharedPreferences?.edit() 52 val key = getKeyForUpdateInfo(updateInfo) 53 val json = 54 Json.encodeToString( 55 SerializableUpdateInfo.serializer(), 56 updateInfo.toSerializableUpdateInfo() 57 ) 58 editor?.putString(key, json) 59 editor?.apply() 60 } 61 62 /** 63 * Unregisters information about an available update for the specified component. 64 * 65 * @param updateInfo Update information structure. 66 */ 67 public fun unregisterUpdate(updateInfo: UpdateInfo) { 68 cleanupUpdateInfo() 69 70 val sharedPreferences = context.getSharedPreferences(updateInfoPrefs, Context.MODE_PRIVATE) 71 val editor = sharedPreferences?.edit() 72 val key = getKeyForUpdateInfo(updateInfo) 73 editor?.remove(key) 74 editor?.apply() 75 } 76 77 /** 78 * Cleans up outdated or applied updates from the shared preferences. This method checks each 79 * registered update against the current device security patch levels and removes any updates 80 * that are no longer relevant (i.e., the update's patch level is less than or equal to the 81 * current device patch level). 82 */ 83 private fun cleanupUpdateInfo() { 84 val allUpdates = getAllUpdates() 85 val sharedPreferences = context.getSharedPreferences(updateInfoPrefs, Context.MODE_PRIVATE) 86 val editor = sharedPreferences?.edit() ?: return 87 88 allUpdates.forEach { updateInfo -> 89 val component = updateInfo.component 90 val currentSpl: SecurityPatchState.SecurityPatchLevel 91 try { 92 currentSpl = securityState.getDeviceSecurityPatchLevel(component) 93 } catch (e: IllegalArgumentException) { 94 // Ignore unknown components. 95 return@forEach 96 } 97 val updateSpl = getComponentSecurityPatchLevel(component, updateInfo.securityPatchLevel) 98 99 if (updateSpl <= currentSpl) { 100 val key = getKeyForUpdateInfo(updateInfo) 101 editor.remove(key) 102 } 103 } 104 105 editor.apply() 106 } 107 108 private fun getKeyForUpdateInfo(updateInfo: UpdateInfo): String { 109 // Create a unique key for each update info. 110 return "${updateInfo.component}-${updateInfo.uri}" 111 } 112 113 /** 114 * Retrieves a list of all updates currently registered in the system's shared preferences. This 115 * method is primarily used for managing and tracking updates that have been registered but not 116 * yet applied or acknowledged by the system. 117 * 118 * @return A list of [UpdateInfo] objects, each representing a registered update. 119 */ 120 private fun getAllUpdates(): List<UpdateInfo> { 121 val allUpdates = mutableListOf<UpdateInfo>() 122 for (json in getAllUpdatesAsJson()) { 123 val serializableUpdateInfo: SerializableUpdateInfo = Json.decodeFromString(json) 124 val updateInfo: UpdateInfo = serializableUpdateInfo.toUpdateInfo() 125 allUpdates.add(updateInfo) 126 } 127 return allUpdates 128 } 129 130 /** 131 * Retrieves all registered updates in JSON format from the system's shared preferences. 132 * 133 * @return A list of strings, each representing an update in JSON format. 134 */ 135 internal fun getAllUpdatesAsJson(): List<String> { 136 val allUpdates = mutableListOf<String>() 137 val sharedPreferences = context.getSharedPreferences(updateInfoPrefs, Context.MODE_PRIVATE) 138 val allEntries = sharedPreferences?.all ?: return emptyList() 139 for ((_, value) in allEntries) { 140 val json = value as? String 141 if (json != null) { 142 allUpdates.add(json) 143 } 144 } 145 return allUpdates 146 } 147 } 148