1 /* 2 * 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.ComponentName 20 import android.content.ContentProvider 21 import android.content.ContentValues 22 import android.content.Context 23 import android.content.pm.PackageManager 24 import android.database.Cursor 25 import android.database.MatrixCursor 26 import android.net.Uri 27 28 /** 29 * A content provider that serves update information for system components. 30 * 31 * This class retrieves [UpdateInfo] stored in JSON format and serves it via a content URI. It only 32 * supports the [query] operation; [insert], [delete], and [update] operations are not permitted. 33 * 34 * Typically, OTA or other update clients utilize this provider to expose update information to 35 * other applications or components within the system that need access to the latest security 36 * updates data. The client calls [UpdateInfoManager.registerUpdate] and 37 * [UpdateInfoManager.unregisterUpdate] to add or remove update information to a local store, from 38 * which the content provider serves the data to the applications. 39 * 40 * To setup the content provider add the following snippet to the client's manifest: 41 * ``` 42 * <provider 43 * android:name="androidx.security.state.provider.UpdateInfoProvider" 44 * android:authorities="${applicationId}.updateinfoprovider" 45 * android:exported="true" /> 46 * ``` 47 */ 48 public class UpdateInfoProvider : ContentProvider() { 49 50 private var context: Context? = null 51 private lateinit var authority: String 52 private lateinit var contentUri: Uri 53 54 /** 55 * Initializes the content provider by constructing [contentUri] using the authority listed in 56 * the manifest. 57 * 58 * @return true if the provider was successfully created, false otherwise. 59 */ onCreatenull60 override fun onCreate(): Boolean { 61 context = 62 getContext() ?: throw IllegalStateException("Cannot find context from the provider.") 63 authority = getAuthority(context!!) 64 contentUri = Uri.parse("content://$authority/updateinfo") 65 return true 66 } 67 68 /** 69 * Handles queries for the update information. 70 * 71 * This method only responds to queries directed at the specific content URI corresponding to 72 * update data. It returns a [Cursor] containing [UpdateInfo] represented in JSON format. 73 * 74 * @param uri The URI to query. This must match the expected content URI for update data. 75 * @param projection The list of columns to put into the cursor. If null, all columns are 76 * included. 77 * @param selection The selection criteria to apply. 78 * @param selectionArgs Arguments for the selection criteria. 79 * @param sortOrder The order in which rows are sorted in the returned Cursor. 80 * @return A [Cursor] object containing the update data. 81 * @throws IllegalArgumentException if the provided URI does not match the expected URI for 82 * update data. 83 */ querynull84 override fun query( 85 uri: Uri, 86 projection: Array<out String>?, 87 selection: String?, 88 selectionArgs: Array<out String>?, 89 sortOrder: String? 90 ): Cursor { 91 // Verify that the caller has requested a correct URI for this provider 92 if (uri == contentUri) { 93 val updateInfoManager = UpdateInfoManager(context!!) 94 val jsonUpdates = updateInfoManager.getAllUpdatesAsJson() 95 val cursor = MatrixCursor(arrayOf("json")) 96 jsonUpdates.forEach { cursor.addRow(arrayOf(it)) } 97 return cursor 98 } else { 99 throw IllegalArgumentException("Unknown URI: $uri") 100 } 101 } 102 103 /** 104 * Returns the MIME type of the data at the given URI. This method only handles the content URI 105 * for update data. 106 * 107 * @param uri The URI to query for its MIME type. 108 * @return The MIME type of the data at the specified URI, or null if the URI is not handled by 109 * this provider. 110 */ getTypenull111 override fun getType(uri: Uri): String? { 112 return "vnd.android.cursor.dir/vnd.$authority.updateinfo" 113 } 114 115 /** 116 * Unsupported operation. This method will throw an exception if called. 117 * 118 * @param uri The URI to query. 119 * @param values The new values to insert. 120 * @return nothing as this operation is not supported. 121 * @throws UnsupportedOperationException always as this operation is not supported. 122 */ insertnull123 override fun insert(uri: Uri, values: ContentValues?): Uri? { 124 throw UnsupportedOperationException("Insert operation is not supported.") 125 } 126 127 /** 128 * Unsupported operation. This method will throw an exception if called. 129 * 130 * @param uri The URI to delete from. 131 * @param selection The selection criteria to apply. 132 * @param selectionArgs Arguments for the selection criteria. 133 * @return nothing as this operation is not supported. 134 * @throws UnsupportedOperationException always as this operation is not supported. 135 */ deletenull136 override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?): Int { 137 throw UnsupportedOperationException("Delete operation is not supported.") 138 } 139 140 /** 141 * Unsupported operation. This method will throw an exception if called. 142 * 143 * @param uri The URI to update. 144 * @param values The new values to apply. 145 * @param selection The selection criteria to apply. 146 * @param selectionArgs Arguments for the selection criteria. 147 * @return nothing as this operation is not supported. 148 * @throws UnsupportedOperationException always as this operation is not supported. 149 */ updatenull150 override fun update( 151 uri: Uri, 152 values: ContentValues?, 153 selection: String?, 154 selectionArgs: Array<out String>? 155 ): Int { 156 throw UnsupportedOperationException("Update operation is not supported.") 157 } 158 159 /** 160 * Returns [android.content.pm.ProviderInfo.authority], the authority of the provider defined in 161 * the manifest. 162 * 163 * For example, "com.example.updateinfoprovider" would be returned for the following provider: 164 * ``` 165 * <provider 166 * android:name="androidx.security.state.provider.UpdateInfoProvider" 167 * android:authorities="com.example.updateinfoprovider" /> 168 * ``` 169 */ getAuthoritynull170 private fun getAuthority(context: Context): String { 171 return context.packageManager 172 .getProviderInfo( 173 ComponentName(context, UpdateInfoProvider::class.java), 174 PackageManager.GET_META_DATA 175 ) 176 .authority 177 } 178 } 179