1 /* 2 * Copyright (C) 2020 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.permissioncontroller.permission.data 18 19 import android.app.Application 20 import android.content.pm.PackageManager.NameNotFoundException 21 import android.content.res.Resources.ID_NULL 22 import android.os.UserHandle 23 import android.util.Log 24 import com.android.permissioncontroller.PermissionControllerApplication 25 import java.io.FileNotFoundException 26 import kotlinx.coroutines.Job 27 import org.xmlpull.v1.XmlPullParser 28 import org.xmlpull.v1.XmlPullParser.END_DOCUMENT 29 import org.xmlpull.v1.XmlPullParser.END_TAG 30 import org.xmlpull.v1.XmlPullParser.START_TAG 31 32 private const val MANIFEST_FILE_NAME = "AndroidManifest.xml" 33 private const val MANIFEST_TAG = "manifest" 34 private const val PKG_ATTR = "package" 35 private const val ATTRIBUTION_TAG = "attribution" 36 private const val ANDROID_NS = "http://schemas.android.com/apk/res/android" 37 private const val TAG_ATTR = "tag" 38 private const val LABEL_ATTR = "label" 39 40 /** 41 * Label-resource-id of an attribution of a package/user. 42 * 43 * <p>Obviously the resource is found in the package, hence needs to be loaded via a Resources 44 * object created for this package. 45 */ 46 class AttributionLabelLiveData 47 private constructor( 48 private val app: Application, 49 private val attributionTag: String?, 50 private val packageName: String, 51 private val user: UserHandle 52 ) : SmartAsyncMediatorLiveData<Int>(), PackageBroadcastReceiver.PackageBroadcastListener { 53 private val LOG_TAG = AttributionLabelLiveData::class.java.simpleName 54 loadDataAndPostValuenull55 override suspend fun loadDataAndPostValue(job: Job) { 56 if (job.isCancelled) { 57 return 58 } 59 60 if (attributionTag == null) { 61 postValue(ID_NULL) 62 return 63 } 64 65 val pkgContext = 66 try { 67 app.createPackageContextAsUser(packageName, 0, user) 68 } catch (e: NameNotFoundException) { 69 Log.e(LOG_TAG, "Cannot find $packageName for $user") 70 71 postValue(null) 72 return 73 } 74 75 // TODO (moltmann): Read this from PackageInfo once available 76 var cookie = 0 77 while (true) { 78 // Some resources have multiple "AndroidManifest.xml" loaded and hence we need 79 // to find the right one 80 cookie++ 81 val parser = 82 try { 83 pkgContext.assets.openXmlResourceParser(cookie, MANIFEST_FILE_NAME) 84 } catch (e: FileNotFoundException) { 85 break 86 } 87 88 try { 89 do { 90 if (parser.eventType != START_TAG) { 91 continue 92 } 93 94 if (parser.name != MANIFEST_TAG) { 95 parser.skipTag() 96 continue 97 } 98 99 // Ensure this is the right manifest 100 if (parser.getAttributeValue(null, PKG_ATTR) != packageName) { 101 break 102 } 103 104 while (parser.next() != END_TAG) { 105 if (parser.eventType != START_TAG) { 106 continue 107 } 108 109 if (parser.name != ATTRIBUTION_TAG) { 110 parser.skipTag() 111 continue 112 } 113 114 if (parser.getAttributeValue(ANDROID_NS, TAG_ATTR) == attributionTag) { 115 postValue( 116 parser.getAttributeResourceValue(ANDROID_NS, LABEL_ATTR, ID_NULL) 117 ) 118 return 119 } else { 120 parser.skipTag() 121 } 122 } 123 } while (parser.next() != END_DOCUMENT) 124 } finally { 125 parser.close() 126 } 127 } 128 129 postValue(null) 130 } 131 132 /** Skip tag parser is currently pointing to (including all tags nested in it) */ skipTagnull133 private fun XmlPullParser.skipTag() { 134 var depth = 1 135 while (depth != 0) { 136 when (next()) { 137 END_TAG -> depth-- 138 START_TAG -> depth++ 139 } 140 } 141 } 142 onActivenull143 override fun onActive() { 144 super.onActive() 145 146 // Listen for changes to the attributions 147 PackageBroadcastReceiver.addChangeCallback(packageName, this) 148 update() 149 } 150 onInactivenull151 override fun onInactive() { 152 super.onInactive() 153 154 PackageBroadcastReceiver.removeChangeCallback(packageName, this) 155 } 156 onPackageUpdatenull157 override fun onPackageUpdate(packageName: String) { 158 update() 159 } 160 161 /** 162 * Repository for AttributionLiveData. 163 * 164 * <p> Key value is a pair of string attribution tag, string package name, user handle, value is 165 * its corresponding LiveData. 166 */ 167 companion object : 168 DataRepository<Triple<String?, String, UserHandle>, AttributionLabelLiveData>() { newValuenull169 override fun newValue(key: Triple<String?, String, UserHandle>): AttributionLabelLiveData { 170 return AttributionLabelLiveData( 171 PermissionControllerApplication.get(), 172 key.first, 173 key.second, 174 key.third 175 ) 176 } 177 getnull178 operator fun get( 179 attributionTag: String?, 180 packageName: String, 181 user: UserHandle 182 ): AttributionLabelLiveData = get(Triple(attributionTag, packageName, user)) 183 } 184 } 185