1 /* <lambda>null2 * 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.graph 18 19 import android.app.Application 20 import android.os.Bundle 21 import android.os.Parcelable 22 import android.os.SystemClock 23 import com.android.settingslib.graph.proto.PreferenceGraphProto 24 import com.android.settingslib.ipc.ApiHandler 25 import com.android.settingslib.ipc.ApiPermissionChecker 26 import com.android.settingslib.ipc.MessageCodec 27 import com.android.settingslib.metadata.PreferenceRemoteOpMetricsLogger 28 import com.android.settingslib.metadata.PreferenceScreenCoordinate 29 import com.android.settingslib.metadata.PreferenceScreenRegistry 30 import com.android.settingslib.preference.PreferenceScreenProvider 31 import java.util.Locale 32 33 /** API to get preference graph. */ 34 class GetPreferenceGraphApiHandler( 35 override val id: Int, 36 private val permissionChecker: ApiPermissionChecker<GetPreferenceGraphRequest>, 37 private val metricsLogger: PreferenceRemoteOpMetricsLogger? = null, 38 private val preferenceScreenProviders: Set<Class<out PreferenceScreenProvider>> = emptySet(), 39 ) : ApiHandler<GetPreferenceGraphRequest, PreferenceGraphProto> { 40 41 override val requestCodec: MessageCodec<GetPreferenceGraphRequest> 42 get() = GetPreferenceGraphRequestCodec 43 44 override val responseCodec: MessageCodec<PreferenceGraphProto> 45 get() = PreferenceGraphProtoCodec 46 47 override fun hasPermission( 48 application: Application, 49 callingPid: Int, 50 callingUid: Int, 51 request: GetPreferenceGraphRequest, 52 ) = permissionChecker.hasPermission(application, callingPid, callingUid, request) 53 54 override suspend fun invoke( 55 application: Application, 56 callingPid: Int, 57 callingUid: Int, 58 request: GetPreferenceGraphRequest, 59 ): PreferenceGraphProto { 60 val elapsedRealtime = SystemClock.elapsedRealtime() 61 var success = false 62 try { 63 val builder = PreferenceGraphBuilder.of(application, callingPid, callingUid, request) 64 if (request.screens.isEmpty()) { 65 val factories = PreferenceScreenRegistry.preferenceScreenMetadataFactories 66 factories.forEachAsync { _, factory -> builder.addPreferenceScreen(factory) } 67 for (provider in preferenceScreenProviders) { 68 builder.addPreferenceScreenProvider(provider) 69 } 70 } 71 val result = builder.build() 72 success = true 73 return result 74 } finally { 75 metricsLogger?.logGraphApi( 76 application, 77 callingUid, 78 success, 79 SystemClock.elapsedRealtime() - elapsedRealtime, 80 ) 81 } 82 } 83 } 84 85 /** 86 * Request of [GetPreferenceGraphApiHandler]. 87 * 88 * @param screens screens of the preference graph 89 * @param visitedScreens visited preference screens 90 * @param locale locale of the preference graph 91 */ 92 data class GetPreferenceGraphRequest 93 @JvmOverloads 94 constructor( 95 val screens: Set<PreferenceScreenCoordinate> = setOf(), 96 val visitedScreens: Set<PreferenceScreenCoordinate> = setOf(), 97 val locale: Locale? = null, 98 val flags: Int = PreferenceGetterFlags.ALL, 99 val includeValueDescriptor: Boolean = true, 100 ) 101 102 object GetPreferenceGraphRequestCodec : MessageCodec<GetPreferenceGraphRequest> { encodenull103 override fun encode(data: GetPreferenceGraphRequest): Bundle = 104 Bundle(4).apply { 105 putParcelableArray(KEY_SCREENS, data.screens.toTypedArray()) 106 putParcelableArray(KEY_VISITED_SCREENS, data.visitedScreens.toTypedArray()) 107 putString(KEY_LOCALE, data.locale?.toLanguageTag()) 108 putInt(KEY_FLAGS, data.flags) 109 } 110 111 @Suppress("DEPRECATION") decodenull112 override fun decode(data: Bundle): GetPreferenceGraphRequest { 113 data.classLoader = PreferenceScreenCoordinate::class.java.classLoader 114 val screens = data.getParcelableArray(KEY_SCREENS) ?: arrayOf() 115 val visitedScreens = data.getParcelableArray(KEY_VISITED_SCREENS) ?: arrayOf() 116 fun String?.toLocale() = if (this != null) Locale.forLanguageTag(this) else null 117 fun Array<Parcelable>.toScreenCoordinates() = 118 buildSet(size) { 119 for (element in this@toScreenCoordinates) add(element as PreferenceScreenCoordinate) 120 } 121 return GetPreferenceGraphRequest( 122 screens.toScreenCoordinates(), 123 visitedScreens.toScreenCoordinates(), 124 data.getString(KEY_LOCALE).toLocale(), 125 data.getInt(KEY_FLAGS), 126 ) 127 } 128 129 private const val KEY_SCREENS = "s" 130 private const val KEY_VISITED_SCREENS = "v" 131 private const val KEY_LOCALE = "l" 132 private const val KEY_FLAGS = "f" 133 } 134 135 object PreferenceGraphProtoCodec : MessageCodec<PreferenceGraphProto> { encodenull136 override fun encode(data: PreferenceGraphProto): Bundle = 137 Bundle(1).apply { putByteArray(KEY_GRAPH, data.toByteArray()) } 138 decodenull139 override fun decode(data: Bundle): PreferenceGraphProto = 140 PreferenceGraphProto.parseFrom(data.getByteArray(KEY_GRAPH)!!) 141 142 private const val KEY_GRAPH = "g" 143 } 144