• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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.tools.metalava.manifest
18 
19 import com.android.SdkConstants
20 import com.android.tools.metalava.reporter.Issues
21 import com.android.tools.metalava.reporter.Reporter
22 import com.android.tools.metalava.xml.parseDocument
23 import com.android.utils.XmlUtils
24 import java.io.File
25 
26 /**
27  * An empty manifest. This is safe to share as while it is not strictly immutable it only mutates
28  * the object when lazily initializing [Manifest.info].
29  */
<lambda>null30 val emptyManifest: Manifest by lazy { Manifest(null, null) }
31 
32 private data class ManifestInfo(
33     val permissions: Map<String, String>,
34     val minSdkVersion: MinSdkVersion
35 )
36 
37 private val defaultInfo = ManifestInfo(emptyMap(), UnsetMinSdkVersion)
38 
39 /** Provides access to information from an `AndroidManifest.xml` file. */
40 class Manifest(private val manifest: File?, private val reporter: Reporter?) {
41 
<lambda>null42     private val info: ManifestInfo by lazy { readManifestInfo() }
43 
readManifestInfonull44     private fun readManifestInfo(): ManifestInfo {
45         if (manifest == null) {
46             return defaultInfo
47         }
48 
49         return try {
50             val doc = parseDocument(manifest.readText(Charsets.UTF_8), true)
51 
52             // Extract permissions.
53             val map = HashMap<String, String>(600)
54             var current =
55                 XmlUtils.getFirstSubTagByName(doc.documentElement, SdkConstants.TAG_PERMISSION)
56             while (current != null) {
57                 val permissionName =
58                     current.getAttributeNS(SdkConstants.ANDROID_URI, SdkConstants.ATTR_NAME)
59                 val protectionLevel =
60                     current.getAttributeNS(SdkConstants.ANDROID_URI, "protectionLevel")
61                 map[permissionName] = protectionLevel
62                 current = XmlUtils.getNextTagByName(current, SdkConstants.TAG_PERMISSION)
63             }
64 
65             // Extract minSdkVersion.
66             val min: MinSdkVersion = run {
67                 val usesSdk =
68                     XmlUtils.getFirstSubTagByName(doc.documentElement, SdkConstants.TAG_USES_SDK)
69                 if (usesSdk == null) {
70                     UnsetMinSdkVersion
71                 } else {
72                     val value =
73                         usesSdk.getAttributeNS(
74                             SdkConstants.ANDROID_URI,
75                             SdkConstants.ATTR_MIN_SDK_VERSION
76                         )
77                     if (value.isEmpty()) UnsetMinSdkVersion else SetMinSdkVersion(value.toInt())
78                 }
79             }
80 
81             ManifestInfo(map, min)
82         } catch (error: Throwable) {
83             reporter?.report(
84                 Issues.PARSE_ERROR,
85                 manifest,
86                 "Failed to parse $manifest: ${error.message}"
87             )
88                 ?: throw error
89             defaultInfo
90         }
91     }
92 
93     /** Check whether the manifest is empty or not. */
isEmptynull94     fun isEmpty(): Boolean {
95         return manifest == null
96     }
97 
98     /**
99      * Returns the permission level of the named permission, if specified in the manifest. This
100      * method should only be called if the codebase has been configured with a manifest
101      */
getPermissionLevelnull102     fun getPermissionLevel(name: String): String? {
103         assert(manifest != null) {
104             "This method should only be called when a manifest has been configured on the codebase"
105         }
106 
107         return info.permissions[name]
108     }
109 
getMinSdkVersionnull110     fun getMinSdkVersion(): MinSdkVersion {
111         return info.minSdkVersion
112     }
113 
toStringnull114     override fun toString(): String {
115         return manifest.toString()
116     }
117 }
118 
119 sealed class MinSdkVersion
120 
121 data class SetMinSdkVersion(val value: Int) : MinSdkVersion()
122 
123 object UnsetMinSdkVersion : MinSdkVersion()
124