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.server.pm; 18 19 import static android.content.pm.PackageManager.TYPE_ACTIVITY; 20 import static android.content.pm.PackageManager.TYPE_APPLICATION; 21 import static android.content.pm.PackageManager.TYPE_PROVIDER; 22 import static android.content.pm.PackageManager.TYPE_RECEIVER; 23 import static android.content.pm.PackageManager.TYPE_SERVICE; 24 25 import android.annotation.NonNull; 26 import android.annotation.Nullable; 27 import android.content.pm.PackageManager; 28 import android.content.pm.PackageManager.Property; 29 import android.content.pm.PackageManager.PropertyLocation; 30 import android.content.pm.parsing.component.ParsedComponent; 31 import android.os.Binder; 32 import android.os.UserHandle; 33 import android.util.ArrayMap; 34 35 import com.android.server.pm.parsing.pkg.AndroidPackage; 36 37 import java.util.ArrayList; 38 import java.util.Iterator; 39 import java.util.List; 40 import java.util.Map; 41 import java.util.Objects; 42 import java.util.function.Predicate; 43 44 /** 45 * Manages properties defined within a package using the <property> tag. 46 */ 47 public class PackageProperty { 48 /** 49 * Mapping of property name to all defined defined properties. 50 * <p>This is a mapping of property name --> package map. The package 51 * map is a mapping of package name -> list of properties. 52 */ 53 private ArrayMap<String, ArrayMap<String, ArrayList<Property>>> mApplicationProperties; 54 private ArrayMap<String, ArrayMap<String, ArrayList<Property>>> mActivityProperties; 55 private ArrayMap<String, ArrayMap<String, ArrayList<Property>>> mProviderProperties; 56 private ArrayMap<String, ArrayMap<String, ArrayList<Property>>> mReceiverProperties; 57 private ArrayMap<String, ArrayMap<String, ArrayList<Property>>> mServiceProperties; 58 59 /** 60 * If the provided component is {@code null}, returns the property defined on the 61 * application. Otherwise, returns the property defined on the component. 62 */ getProperty(@onNull String propertyName, @NonNull String packageName, @Nullable String className)63 public Property getProperty(@NonNull String propertyName, @NonNull String packageName, 64 @Nullable String className) { 65 if (className == null) { 66 return getApplicationProperty(propertyName, packageName); 67 } 68 return getComponentProperty(propertyName, packageName, className); 69 } 70 71 /** 72 * Returns all properties defined at the given location. 73 * <p>Valid locations are {@link PackageManager#TYPE_APPLICATION}, 74 * {@link PackageManager#TYPE_ACTIVITY}, {@link PackageManager#TYPE_PROVIDER}, 75 * {@link PackageManager#TYPE_RECEIVER}, or {@link PackageManager#TYPE_SERVICE}. 76 */ queryProperty(@onNull String propertyName, @PropertyLocation int componentType, Predicate<String> filter)77 public List<Property> queryProperty(@NonNull String propertyName, 78 @PropertyLocation int componentType, Predicate<String> filter) { 79 final ArrayMap<String, ArrayMap<String, ArrayList<Property>>> propertyMap; 80 if (componentType == TYPE_APPLICATION) { 81 propertyMap = mApplicationProperties; 82 } else if (componentType == TYPE_ACTIVITY) { 83 propertyMap = mActivityProperties; 84 } else if (componentType == TYPE_PROVIDER) { 85 propertyMap = mProviderProperties; 86 } else if (componentType == TYPE_RECEIVER) { 87 propertyMap = mReceiverProperties; 88 } else if (componentType == TYPE_SERVICE) { 89 propertyMap = mServiceProperties; 90 } else { 91 propertyMap = null; 92 } 93 if (propertyMap == null) { 94 return null; 95 } 96 final ArrayMap<String, ArrayList<Property>> packagePropertyMap = 97 propertyMap.get(propertyName); 98 if (packagePropertyMap == null) { 99 return null; 100 } 101 final int callingUid = Binder.getCallingUid(); 102 final int callingUserId = UserHandle.getCallingUserId(); 103 final int mapSize = packagePropertyMap.size(); 104 final List<Property> result = new ArrayList<>(mapSize); 105 for (int i = 0; i < mapSize; i++) { 106 final String packageName = packagePropertyMap.keyAt(i); 107 if (filter.test(packageName)) { 108 continue; 109 } 110 result.addAll(packagePropertyMap.valueAt(i)); 111 } 112 return result; 113 } 114 115 /** Adds all properties defined for the given package */ addAllProperties(AndroidPackage pkg)116 void addAllProperties(AndroidPackage pkg) { 117 mApplicationProperties = addProperties(pkg.getProperties(), mApplicationProperties); 118 mActivityProperties = addComponentProperties(pkg.getActivities(), mActivityProperties); 119 mProviderProperties = addComponentProperties(pkg.getProviders(), mProviderProperties); 120 mReceiverProperties = addComponentProperties(pkg.getReceivers(), mReceiverProperties); 121 mServiceProperties = addComponentProperties(pkg.getServices(), mServiceProperties); 122 } 123 124 /** Adds all properties defined for the given package */ removeAllProperties(AndroidPackage pkg)125 void removeAllProperties(AndroidPackage pkg) { 126 mApplicationProperties = removeProperties(pkg.getProperties(), mApplicationProperties); 127 mActivityProperties = removeComponentProperties(pkg.getActivities(), mActivityProperties); 128 mProviderProperties = removeComponentProperties(pkg.getProviders(), mProviderProperties); 129 mReceiverProperties = removeComponentProperties(pkg.getReceivers(), mReceiverProperties); 130 mServiceProperties = removeComponentProperties(pkg.getServices(), mServiceProperties); 131 } 132 133 /** Add the properties defined on the given components to the property collection */ 134 private static <T extends ParsedComponent> addComponentProperties( @onNull List<T> components, @Nullable ArrayMap<String, ArrayMap<String, ArrayList<Property>>> propertyCollection)135 ArrayMap<String, ArrayMap<String, ArrayList<Property>>> addComponentProperties( 136 @NonNull List<T> components, 137 @Nullable ArrayMap<String, ArrayMap<String, ArrayList<Property>>> propertyCollection) { 138 ArrayMap<String, ArrayMap<String, ArrayList<Property>>> returnCollection = 139 propertyCollection; 140 final int componentsSize = components.size(); 141 for (int i = 0; i < componentsSize; i++) { 142 final Map<String, Property> properties = components.get(i).getProperties(); 143 if (properties.size() == 0) { 144 continue; 145 } 146 returnCollection = addProperties(properties, returnCollection); 147 } 148 return returnCollection; 149 } 150 151 /** Add the given properties to the property collection */ addProperties( @onNull Map<String, Property> properties, @Nullable ArrayMap<String, ArrayMap<String, ArrayList<Property>>> propertyCollection)152 private static ArrayMap<String, ArrayMap<String, ArrayList<Property>>> addProperties( 153 @NonNull Map<String, Property> properties, 154 @Nullable ArrayMap<String, ArrayMap<String, ArrayList<Property>>> propertyCollection) { 155 if (properties.size() == 0) { 156 return propertyCollection; 157 } 158 final ArrayMap<String, ArrayMap<String, ArrayList<Property>>> returnCollection = 159 propertyCollection == null ? new ArrayMap<>(10) : propertyCollection; 160 final Iterator<Property> iter = properties.values().iterator(); 161 while (iter.hasNext()) { 162 final Property property = iter.next(); 163 final String propertyName = property.getName(); 164 final String packageName = property.getPackageName(); 165 ArrayMap<String, ArrayList<Property>> propertyMap = returnCollection.get(propertyName); 166 if (propertyMap == null) { 167 propertyMap = new ArrayMap<>(); 168 returnCollection.put(propertyName, propertyMap); 169 } 170 ArrayList<Property> packageProperties = propertyMap.get(packageName); 171 if (packageProperties == null) { 172 packageProperties = new ArrayList<>(properties.size()); 173 propertyMap.put(packageName, packageProperties); 174 } 175 packageProperties.add(property); 176 } 177 return returnCollection; 178 } 179 180 /** Removes the properties defined on the given components from the property collection */ 181 private static <T extends ParsedComponent> removeComponentProperties( @onNull List<T> components, @Nullable ArrayMap<String, ArrayMap<String, ArrayList<Property>>> propertyCollection)182 ArrayMap<String, ArrayMap<String, ArrayList<Property>>> removeComponentProperties( 183 @NonNull List<T> components, 184 @Nullable ArrayMap<String, ArrayMap<String, ArrayList<Property>>> propertyCollection) { 185 ArrayMap<String, ArrayMap<String, ArrayList<Property>>> returnCollection = 186 propertyCollection; 187 final int componentsSize = components.size(); 188 for (int i = 0; returnCollection != null && i < componentsSize; i++) { 189 final Map<String, Property> properties = components.get(i).getProperties(); 190 if (properties.size() == 0) { 191 continue; 192 } 193 returnCollection = removeProperties(properties, returnCollection); 194 } 195 return returnCollection; 196 } 197 198 /** Removes the given properties from the property collection */ removeProperties( @onNull Map<String, Property> properties, @Nullable ArrayMap<String, ArrayMap<String, ArrayList<Property>>> propertyCollection)199 private static ArrayMap<String, ArrayMap<String, ArrayList<Property>>> removeProperties( 200 @NonNull Map<String, Property> properties, 201 @Nullable ArrayMap<String, ArrayMap<String, ArrayList<Property>>> propertyCollection) { 202 if (propertyCollection == null) { 203 return null; 204 } 205 final Iterator<Property> iter = properties.values().iterator(); 206 while (iter.hasNext()) { 207 final Property property = iter.next(); 208 final String propertyName = property.getName(); 209 final String packageName = property.getPackageName(); 210 ArrayMap<String, ArrayList<Property>> propertyMap = 211 propertyCollection.get(propertyName); 212 if (propertyMap == null) { 213 // error 214 continue; 215 } 216 ArrayList<Property> packageProperties = propertyMap.get(packageName); 217 if (packageProperties == null) { 218 //error 219 continue; 220 } 221 packageProperties.remove(property); 222 223 // clean up empty structures 224 if (packageProperties.size() == 0) { 225 propertyMap.remove(packageName); 226 } 227 if (propertyMap.size() == 0) { 228 propertyCollection.remove(propertyName); 229 } 230 } 231 if (propertyCollection.size() == 0) { 232 return null; 233 } 234 return propertyCollection; 235 } 236 getProperty(String propertyName, String packageName, String className, ArrayMap<String, ArrayMap<String, ArrayList<Property>>> propertyMap)237 private static Property getProperty(String propertyName, String packageName, String className, 238 ArrayMap<String, ArrayMap<String, ArrayList<Property>>> propertyMap) { 239 final ArrayMap<String, ArrayList<Property>> packagePropertyMap = 240 propertyMap.get(propertyName); 241 if (packagePropertyMap == null) { 242 return null; 243 } 244 final List<Property> propertyList = packagePropertyMap.get(packageName); 245 if (propertyList == null) { 246 return null; 247 } 248 for (int i = propertyList.size() - 1; i >= 0; i--) { 249 final Property property = propertyList.get(i); 250 if (Objects.equals(className, property.getClassName())) { 251 return property; 252 } 253 } 254 return null; 255 } 256 getComponentProperty( String propertyName, String packageName, String className)257 private Property getComponentProperty( 258 String propertyName, String packageName, String className) { 259 Property property = null; 260 if (property == null && mActivityProperties != null) { 261 property = getProperty(propertyName, packageName, className, mActivityProperties); 262 } 263 if (property == null && mProviderProperties != null) { 264 property = getProperty(propertyName, packageName, className, mProviderProperties); 265 } 266 if (property == null && mReceiverProperties != null) { 267 property = getProperty(propertyName, packageName, className, mReceiverProperties); 268 } 269 if (property == null && mServiceProperties != null) { 270 property = getProperty(propertyName, packageName, className, mServiceProperties); 271 } 272 return property; 273 } 274 getApplicationProperty(String propertyName, String packageName)275 private Property getApplicationProperty(String propertyName, String packageName) { 276 final ArrayMap<String, ArrayList<Property>> packagePropertyMap = 277 mApplicationProperties != null ? mApplicationProperties.get(propertyName) : null; 278 if (packagePropertyMap == null) { 279 return null; 280 } 281 final List<Property> propertyList = packagePropertyMap.get(packageName); 282 if (propertyList == null) { 283 return null; 284 } 285 return propertyList.get(0); 286 } 287 } 288