• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 kotlinx.coroutines.Job
26 import org.xmlpull.v1.XmlPullParser
27 import org.xmlpull.v1.XmlPullParser.END_DOCUMENT
28 import org.xmlpull.v1.XmlPullParser.END_TAG
29 import org.xmlpull.v1.XmlPullParser.START_TAG
30 import java.io.FileNotFoundException
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 private constructor(
47     private val app: Application,
48     private val attributionTag: String?,
49     private val packageName: String,
50     private val user: UserHandle
51 ) : SmartAsyncMediatorLiveData<Int>(), PackageBroadcastReceiver.PackageBroadcastListener {
52     private val LOG_TAG = AttributionLabelLiveData::class.java.simpleName
53 
loadDataAndPostValuenull54     override suspend fun loadDataAndPostValue(job: Job) {
55         if (job.isCancelled) {
56             return
57         }
58 
59         if (attributionTag == null) {
60             postValue(ID_NULL)
61             return
62         }
63 
64         val pkgContext = try {
65             app.createPackageContextAsUser(packageName, 0, user)
66         } catch (e: NameNotFoundException) {
67             Log.e(LOG_TAG, "Cannot find $packageName for $user")
68 
69             postValue(null)
70             return
71         }
72 
73         // TODO (moltmann): Read this from PackageInfo once available
74         var cookie = 0
75         while (true) {
76             // Some resources have multiple "AndroidManifest.xml" loaded and hence we need
77             // to find the right one
78             cookie++
79             val parser = try {
80                 pkgContext.assets.openXmlResourceParser(cookie, MANIFEST_FILE_NAME)
81             } catch (e: FileNotFoundException) {
82                 break
83             }
84 
85             try {
86                 do {
87                     if (parser.eventType != START_TAG) {
88                         continue
89                     }
90 
91                     if (parser.name != MANIFEST_TAG) {
92                         parser.skipTag()
93                         continue
94                     }
95 
96                     // Ensure this is the right manifest
97                     if (parser.getAttributeValue(null, PKG_ATTR) != packageName) {
98                         break
99                     }
100 
101                     while (parser.next() != END_TAG) {
102                         if (parser.eventType != START_TAG) {
103                             continue
104                         }
105 
106                         if (parser.name != ATTRIBUTION_TAG) {
107                             parser.skipTag()
108                             continue
109                         }
110 
111                         if (parser.getAttributeValue(ANDROID_NS, TAG_ATTR) == attributionTag) {
112                             postValue(parser.getAttributeResourceValue(ANDROID_NS, LABEL_ATTR,
113                                     ID_NULL))
114                             return
115                         } else {
116                             parser.skipTag()
117                         }
118                     }
119                 } while (parser.next() != END_DOCUMENT)
120             } finally {
121                 parser.close()
122             }
123         }
124 
125         postValue(null)
126     }
127 
128     /**
129      * Skip tag parser is currently pointing to (including all tags nested in it)
130      */
skipTagnull131     private fun XmlPullParser.skipTag() {
132         var depth = 1
133         while (depth != 0) {
134             when (next()) {
135                 END_TAG -> depth--
136                 START_TAG -> depth++
137             }
138         }
139     }
140 
onActivenull141     override fun onActive() {
142         super.onActive()
143 
144         // Listen for changes to the attributions
145         PackageBroadcastReceiver.addChangeCallback(packageName, this)
146         update()
147     }
148 
onInactivenull149     override fun onInactive() {
150         super.onInactive()
151 
152         PackageBroadcastReceiver.removeChangeCallback(packageName, this)
153     }
154 
onPackageUpdatenull155     override fun onPackageUpdate(packageName: String) {
156         update()
157     }
158 
159     /**
160      * Repository for AttributionLiveData.
161      * <p> Key value is a pair of string attribution tag, string package name, user handle, value is
162      * its corresponding LiveData.
163      */
164     companion object : DataRepository<Triple<String?, String, UserHandle>,
165             AttributionLabelLiveData>() {
newValuenull166         override fun newValue(key: Triple<String?, String, UserHandle>): AttributionLabelLiveData {
167             return AttributionLabelLiveData(PermissionControllerApplication.get(),
168                     key.first, key.second, key.third)
169         }
170 
getnull171         operator fun get(attributionTag: String?, packageName: String, user: UserHandle):
172                 AttributionLabelLiveData =
173             get(Triple(attributionTag, packageName, user))
174     }
175 }
176