• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package org.robolectric.shadows;
2 
3 import static android.content.pm.ApplicationInfo.FLAG_ALLOW_BACKUP;
4 import static android.content.pm.ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA;
5 import static android.content.pm.ApplicationInfo.FLAG_ALLOW_TASK_REPARENTING;
6 import static android.content.pm.ApplicationInfo.FLAG_DEBUGGABLE;
7 import static android.content.pm.ApplicationInfo.FLAG_HAS_CODE;
8 import static android.content.pm.ApplicationInfo.FLAG_KILL_AFTER_RESTORE;
9 import static android.content.pm.ApplicationInfo.FLAG_PERSISTENT;
10 import static android.content.pm.ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS;
11 import static android.content.pm.ApplicationInfo.FLAG_RESTORE_ANY_VERSION;
12 import static android.content.pm.ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS;
13 import static android.content.pm.ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS;
14 import static android.content.pm.ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES;
15 import static android.content.pm.ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS;
16 import static android.content.pm.ApplicationInfo.FLAG_TEST_ONLY;
17 import static android.content.pm.ApplicationInfo.FLAG_VM_SAFE_MODE;
18 import static android.os.PatternMatcher.PATTERN_LITERAL;
19 import static android.os.PatternMatcher.PATTERN_PREFIX;
20 import static android.os.PatternMatcher.PATTERN_SIMPLE_GLOB;
21 import static java.util.Arrays.asList;
22 
23 import android.content.IntentFilter.MalformedMimeTypeException;
24 import android.content.pm.ActivityInfo;
25 import android.content.pm.ComponentInfo;
26 import android.content.pm.PackageInfo;
27 import android.content.pm.PackageItemInfo;
28 import android.content.pm.PackageParser;
29 import android.content.pm.PackageParser.Activity;
30 import android.content.pm.PackageParser.ActivityIntentInfo;
31 import android.content.pm.PackageParser.IntentInfo;
32 import android.content.pm.PackageParser.Package;
33 import android.content.pm.PackageParser.Permission;
34 import android.content.pm.PackageParser.PermissionGroup;
35 import android.content.pm.PackageParser.Service;
36 import android.content.pm.PackageParser.ServiceIntentInfo;
37 import android.content.pm.PathPermission;
38 import android.content.pm.PermissionGroupInfo;
39 import android.content.pm.PermissionInfo;
40 import android.content.pm.ProviderInfo;
41 import android.content.pm.ServiceInfo;
42 import android.os.Build;
43 import android.os.Build.VERSION_CODES;
44 import android.os.Bundle;
45 import android.os.Process;
46 import android.util.Pair;
47 import com.google.common.base.Strings;
48 import java.util.ArrayList;
49 import java.util.Arrays;
50 import java.util.List;
51 import java.util.Map;
52 import org.robolectric.RuntimeEnvironment;
53 import org.robolectric.manifest.ActivityData;
54 import org.robolectric.manifest.AndroidManifest;
55 import org.robolectric.manifest.BroadcastReceiverData;
56 import org.robolectric.manifest.ContentProviderData;
57 import org.robolectric.manifest.IntentFilterData;
58 import org.robolectric.manifest.IntentFilterData.DataAuthority;
59 import org.robolectric.manifest.PackageItemData;
60 import org.robolectric.manifest.PathPermissionData;
61 import org.robolectric.manifest.PermissionGroupItemData;
62 import org.robolectric.manifest.PermissionItemData;
63 import org.robolectric.manifest.ServiceData;
64 import org.robolectric.res.AttributeResource;
65 import org.robolectric.res.ResName;
66 import org.robolectric.util.ReflectionHelpers;
67 
68 /** Creates a {@link PackageInfo} from a {@link AndroidManifest} */
69 @SuppressWarnings("NewApi")
70 public class LegacyManifestParser {
71 
72   private static final List<Pair<String, Integer>> APPLICATION_FLAGS =
73       asList(
74           Pair.create("android:allowBackup", FLAG_ALLOW_BACKUP),
75           Pair.create("android:allowClearUserData", FLAG_ALLOW_CLEAR_USER_DATA),
76           Pair.create("android:allowTaskReparenting", FLAG_ALLOW_TASK_REPARENTING),
77           Pair.create("android:debuggable", FLAG_DEBUGGABLE),
78           Pair.create("android:hasCode", FLAG_HAS_CODE),
79           Pair.create("android:killAfterRestore", FLAG_KILL_AFTER_RESTORE),
80           Pair.create("android:persistent", FLAG_PERSISTENT),
81           Pair.create("android:resizeable", FLAG_RESIZEABLE_FOR_SCREENS),
82           Pair.create("android:restoreAnyVersion", FLAG_RESTORE_ANY_VERSION),
83           Pair.create("android:largeScreens", FLAG_SUPPORTS_LARGE_SCREENS),
84           Pair.create("android:normalScreens", FLAG_SUPPORTS_NORMAL_SCREENS),
85           Pair.create("android:anyDensity", FLAG_SUPPORTS_SCREEN_DENSITIES),
86           Pair.create("android:smallScreens", FLAG_SUPPORTS_SMALL_SCREENS),
87           Pair.create("android:testOnly", FLAG_TEST_ONLY),
88           Pair.create("android:vmSafeMode", FLAG_VM_SAFE_MODE));
89   private static final List<Pair<String, Integer>> CONFIG_OPTIONS =
90       asList(
91           Pair.create("mcc", ActivityInfo.CONFIG_MCC),
92           Pair.create("mnc", ActivityInfo.CONFIG_MNC),
93           Pair.create("locale", ActivityInfo.CONFIG_LOCALE),
94           Pair.create("touchscreen", ActivityInfo.CONFIG_TOUCHSCREEN),
95           Pair.create("keyboard", ActivityInfo.CONFIG_KEYBOARD),
96           Pair.create("keyboardHidden", ActivityInfo.CONFIG_KEYBOARD_HIDDEN),
97           Pair.create("navigation", ActivityInfo.CONFIG_NAVIGATION),
98           Pair.create("screenLayout", ActivityInfo.CONFIG_SCREEN_LAYOUT),
99           Pair.create("fontScale", ActivityInfo.CONFIG_FONT_SCALE),
100           Pair.create("uiMode", ActivityInfo.CONFIG_UI_MODE),
101           Pair.create("orientation", ActivityInfo.CONFIG_ORIENTATION),
102           Pair.create("screenSize", ActivityInfo.CONFIG_SCREEN_SIZE),
103           Pair.create("smallestScreenSize", ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE));
104 
createPackage(AndroidManifest androidManifest)105   public static Package createPackage(AndroidManifest androidManifest) {
106 
107     Package pkg = new Package(androidManifest.getPackageName());
108 
109     pkg.mVersionName = androidManifest.getVersionName();
110     pkg.mVersionCode = androidManifest.getVersionCode();
111 
112     Map<String, PermissionItemData> permissionItemData = androidManifest.getPermissions();
113     for (PermissionItemData itemData : permissionItemData.values()) {
114       Permission permission = new Permission(pkg, createPermissionInfo(pkg, itemData));
115       permission.metaData = permission.info.metaData;
116       pkg.permissions.add(permission);
117     }
118 
119     Map<String, PermissionGroupItemData> permissionGroupItemData =
120         androidManifest.getPermissionGroups();
121     for (PermissionGroupItemData itemData : permissionGroupItemData.values()) {
122       PermissionGroup permissionGroup =
123           new PermissionGroup(pkg, createPermissionGroupInfo(pkg, itemData));
124       permissionGroup.metaData = permissionGroup.info.metaData;
125       pkg.permissionGroups.add(permissionGroup);
126     }
127 
128     pkg.requestedPermissions.addAll(androidManifest.getUsedPermissions());
129     if (RuntimeEnvironment.getApiLevel() < VERSION_CODES.M) {
130       List<Boolean> permissionsRequired =
131           ReflectionHelpers.getField(pkg, "requestedPermissionsRequired");
132       permissionsRequired.addAll(buildBooleanList(pkg.requestedPermissions.size(), true));
133     }
134 
135     pkg.applicationInfo.flags = decodeFlags(androidManifest.getApplicationAttributes());
136     pkg.applicationInfo.targetSdkVersion = androidManifest.getTargetSdkVersion();
137     pkg.applicationInfo.packageName = androidManifest.getPackageName();
138     pkg.applicationInfo.processName = androidManifest.getProcessName();
139     if (!Strings.isNullOrEmpty(androidManifest.getApplicationName())) {
140       pkg.applicationInfo.className =
141           buildClassName(pkg.applicationInfo.packageName, androidManifest.getApplicationName());
142       if (RuntimeEnvironment.getApiLevel() >= VERSION_CODES.N_MR1) {
143         pkg.applicationInfo.name = pkg.applicationInfo.className;
144       }
145     }
146     pkg.applicationInfo.metaData = metaDataToBundle(androidManifest.getApplicationMetaData());
147     pkg.applicationInfo.uid = Process.myUid();
148     if (androidManifest.getThemeRef() != null) {
149       pkg.applicationInfo.theme =
150           RuntimeEnvironment.getAppResourceTable()
151               .getResourceId(
152                   ResName.qualifyResName(
153                       androidManifest.getThemeRef().replace("@", ""), pkg.packageName, "style"));
154     }
155 
156     int labelRes = 0;
157     if (androidManifest.getLabelRef() != null) {
158       String fullyQualifiedName =
159           ResName.qualifyResName(androidManifest.getLabelRef(), androidManifest.getPackageName());
160       Integer id =
161           fullyQualifiedName == null
162               ? null
163               : RuntimeEnvironment.getAppResourceTable()
164                   .getResourceId(new ResName(fullyQualifiedName));
165       labelRes = id != null ? id : 0;
166     }
167 
168     pkg.applicationInfo.labelRes = labelRes;
169     String labelRef = androidManifest.getLabelRef();
170     if (labelRef != null && !labelRef.startsWith("@")) {
171       pkg.applicationInfo.nonLocalizedLabel = labelRef;
172     }
173 
174     Map<String, ActivityData> activityDatas = androidManifest.getActivityDatas();
175     for (ActivityData data : activityDatas.values()) {
176       ActivityInfo activityInfo = new ActivityInfo();
177       activityInfo.name = buildClassName(pkg.packageName, data.getName());
178       activityInfo.packageName = pkg.packageName;
179       activityInfo.configChanges = getConfigChanges(data);
180       activityInfo.parentActivityName = data.getParentActivityName();
181       activityInfo.metaData = metaDataToBundle(data.getMetaData().getValueMap());
182       activityInfo.applicationInfo = pkg.applicationInfo;
183       activityInfo.targetActivity = data.getTargetActivityName();
184       activityInfo.exported = data.isExported();
185       activityInfo.permission = data.getPermission();
186       activityInfo.enabled = data.isEnabled();
187       String themeRef;
188 
189       // Based on ShadowActivity
190       if (data.getThemeRef() != null) {
191         themeRef = data.getThemeRef();
192       } else {
193         themeRef = androidManifest.getThemeRef();
194       }
195       if (themeRef != null) {
196         activityInfo.theme =
197             RuntimeEnvironment.getAppResourceTable()
198                 .getResourceId(
199                     ResName.qualifyResName(themeRef.replace("@", ""), pkg.packageName, "style"));
200       }
201 
202       if (data.getLabel() != null) {
203         activityInfo.labelRes =
204             RuntimeEnvironment.getAppResourceTable()
205                 .getResourceId(
206                     ResName.qualifyResName(
207                         data.getLabel().replace("@", ""), pkg.packageName, "string"));
208         if (activityInfo.labelRes == 0) {
209           activityInfo.nonLocalizedLabel = data.getLabel();
210         }
211       }
212 
213       Activity activity = createActivity(pkg, activityInfo);
214       for (IntentFilterData intentFilterData : data.getIntentFilters()) {
215         ActivityIntentInfo outInfo = new ActivityIntentInfo(activity);
216         populateIntentInfo(intentFilterData, outInfo);
217         activity.intents.add(outInfo);
218       }
219       pkg.activities.add(activity);
220     }
221 
222     for (ContentProviderData data : androidManifest.getContentProviders()) {
223       ProviderInfo info = new ProviderInfo();
224       populateComponentInfo(info, pkg, data);
225       info.authority = data.getAuthorities();
226 
227       List<PathPermission> permissions = new ArrayList<>();
228       for (PathPermissionData permissionData : data.getPathPermissionDatas()) {
229         permissions.add(createPathPermission(permissionData));
230       }
231       info.pathPermissions = permissions.toArray(new PathPermission[permissions.size()]);
232       info.readPermission = data.getReadPermission();
233       info.writePermission = data.getWritePermission();
234       info.grantUriPermissions = data.getGrantUriPermissions();
235       info.enabled = data.isEnabled();
236       pkg.providers.add(createProvider(pkg, info));
237     }
238 
239     for (BroadcastReceiverData data : androidManifest.getBroadcastReceivers()) {
240       ActivityInfo info = new ActivityInfo();
241       populateComponentInfo(info, pkg, data);
242       info.permission = data.getPermission();
243       info.exported = data.isExported();
244       info.enabled = data.isEnabled();
245       Activity receiver = createActivity(pkg, info);
246       for (IntentFilterData intentFilterData : data.getIntentFilters()) {
247         ActivityIntentInfo outInfo = new ActivityIntentInfo(receiver);
248         populateIntentInfo(intentFilterData, outInfo);
249         receiver.intents.add(outInfo);
250       }
251       pkg.receivers.add(receiver);
252     }
253 
254     for (ServiceData data : androidManifest.getServices()) {
255       ServiceInfo info = new ServiceInfo();
256       populateComponentInfo(info, pkg, data);
257       info.permission = data.getPermission();
258       info.exported = data.isExported();
259       info.enabled = data.isEnabled();
260 
261       Service service = createService(pkg, info);
262       for (IntentFilterData intentFilterData : data.getIntentFilters()) {
263         ServiceIntentInfo outInfo = new ServiceIntentInfo(service);
264         populateIntentInfo(intentFilterData, outInfo);
265         service.intents.add(outInfo);
266       }
267       pkg.services.add(service);
268     }
269 
270     String codePath = RuntimeEnvironment.getTempDirectory()
271         .createIfNotExists(pkg.packageName + "-codePath")
272         .toAbsolutePath()
273         .toString();
274     pkg.codePath = codePath;
275     return pkg;
276   }
277 
createPathPermission(PathPermissionData data)278   private static PathPermission createPathPermission(PathPermissionData data) {
279     if (!Strings.isNullOrEmpty(data.pathPattern)) {
280       return new PathPermission(
281           data.pathPattern, PATTERN_SIMPLE_GLOB, data.readPermission, data.writePermission);
282     } else if (!Strings.isNullOrEmpty(data.path)) {
283       return new PathPermission(
284           data.path, PATTERN_LITERAL, data.readPermission, data.writePermission);
285     } else if (!Strings.isNullOrEmpty(data.pathPrefix)) {
286       return new PathPermission(
287           data.pathPrefix, PATTERN_PREFIX, data.readPermission, data.writePermission);
288     } else {
289       throw new IllegalStateException("Permission without type");
290     }
291   }
292 
populateComponentInfo( ComponentInfo outInfo, Package owner, PackageItemData itemData)293   private static void populateComponentInfo(
294       ComponentInfo outInfo, Package owner, PackageItemData itemData) {
295     populatePackageItemInfo(outInfo, owner, itemData);
296     outInfo.applicationInfo = owner.applicationInfo;
297   }
298 
populatePackageItemInfo( PackageItemInfo outInfo, Package owner, PackageItemData itemData)299   private static void populatePackageItemInfo(
300       PackageItemInfo outInfo, Package owner, PackageItemData itemData) {
301     outInfo.name = buildClassName(owner.packageName, itemData.getName());
302     outInfo.packageName = owner.packageName;
303     outInfo.metaData = metaDataToBundle(itemData.getMetaData().getValueMap());
304   }
305 
buildBooleanList(int size, boolean defaultVal)306   private static List<Boolean> buildBooleanList(int size, boolean defaultVal) {
307     Boolean[] barray = new Boolean[size];
308     Arrays.fill(barray, defaultVal);
309     return Arrays.asList(barray);
310   }
311 
createProvider(Package pkg, ProviderInfo info)312   private static PackageParser.Provider createProvider(Package pkg, ProviderInfo info) {
313     PackageParser.Provider provider =
314         ReflectionHelpers.callConstructor(PackageParser.Provider.class);
315     populateComponent(pkg, info, provider);
316     return provider;
317   }
318 
createActivity(Package pkg, ActivityInfo activityInfo)319   private static Activity createActivity(Package pkg, ActivityInfo activityInfo) {
320     Activity activity = ReflectionHelpers.callConstructor(Activity.class);
321     populateComponent(pkg, activityInfo, activity);
322     return activity;
323   }
324 
createService(Package pkg, ServiceInfo info)325   private static Service createService(Package pkg, ServiceInfo info) {
326     PackageParser.Service service = ReflectionHelpers.callConstructor(PackageParser.Service.class);
327     populateComponent(pkg, info, service);
328     return service;
329   }
330 
populateComponent( Package pkg, ComponentInfo info, PackageParser.Component component)331   private static void populateComponent(
332       Package pkg, ComponentInfo info, PackageParser.Component component) {
333     ReflectionHelpers.setField(component, "info", info);
334     ReflectionHelpers.setField(component, "intents", new ArrayList<>());
335     ReflectionHelpers.setField(component, "owner", pkg);
336     ReflectionHelpers.setField(component, "className", info.name);
337   }
338 
populateIntentInfo(IntentFilterData intentFilterData, IntentInfo outInfo)339   private static void populateIntentInfo(IntentFilterData intentFilterData, IntentInfo outInfo) {
340     for (String action : intentFilterData.getActions()) {
341       outInfo.addAction(action);
342     }
343     for (String category : intentFilterData.getCategories()) {
344       outInfo.addCategory(category);
345     }
346     for (DataAuthority dataAuthority : intentFilterData.getAuthorities()) {
347       outInfo.addDataAuthority(dataAuthority.getHost(), dataAuthority.getPort());
348     }
349     for (String mimeType : intentFilterData.getMimeTypes()) {
350       try {
351         outInfo.addDataType(mimeType);
352       } catch (MalformedMimeTypeException e) {
353         throw new RuntimeException(e);
354       }
355     }
356     for (String scheme : intentFilterData.getSchemes()) {
357       outInfo.addDataScheme(scheme);
358     }
359     for (String pathPattern : intentFilterData.getPathPatterns()) {
360       outInfo.addDataPath(pathPattern, PATTERN_SIMPLE_GLOB);
361     }
362     for (String pathPattern : intentFilterData.getPathPrefixes()) {
363       outInfo.addDataPath(pathPattern, PATTERN_PREFIX);
364     }
365     for (String pathPattern : intentFilterData.getPaths()) {
366       outInfo.addDataPath(pathPattern, PATTERN_LITERAL);
367     }
368   }
369 
getConfigChanges(ActivityData activityData)370   private static int getConfigChanges(ActivityData activityData) {
371     String s = activityData.getConfigChanges();
372 
373     int res = 0;
374 
375     // quick sanity check.
376     if (s == null || "".equals(s)) {
377       return res;
378     }
379 
380     String[] pieces = s.split("\\|", 0);
381 
382     for (String s1 : pieces) {
383       s1 = s1.trim();
384 
385       for (Pair<String, Integer> pair : CONFIG_OPTIONS) {
386         if (s1.equals(pair.first)) {
387           res |= pair.second;
388           break;
389         }
390       }
391     }
392 
393     // Matches platform behavior
394     if (RuntimeEnvironment.getApiLevel() >= Build.VERSION_CODES.O) {
395       res |= ActivityInfo.CONFIG_MNC;
396       res |= ActivityInfo.CONFIG_MCC;
397     }
398 
399     return res;
400   }
401 
decodeFlags(Map<String, String> applicationAttributes)402   private static int decodeFlags(Map<String, String> applicationAttributes) {
403     int applicationFlags = 0;
404     for (Pair<String, Integer> pair : APPLICATION_FLAGS) {
405       if ("true".equals(applicationAttributes.get(pair.first))) {
406         applicationFlags |= pair.second;
407       }
408     }
409     return applicationFlags;
410   }
411 
createPermissionInfo(Package owner, PermissionItemData itemData)412   private static PermissionInfo createPermissionInfo(Package owner, PermissionItemData itemData) {
413     PermissionInfo permissionInfo = new PermissionInfo();
414     populatePackageItemInfo(permissionInfo, owner, itemData);
415 
416     permissionInfo.group = itemData.getPermissionGroup();
417     permissionInfo.protectionLevel = decodeProtectionLevel(itemData.getProtectionLevel());
418     permissionInfo.metaData = metaDataToBundle(itemData.getMetaData().getValueMap());
419 
420     String descriptionRef = itemData.getDescription();
421     if (descriptionRef != null) {
422       ResName descResName =
423           AttributeResource.getResourceReference(descriptionRef, owner.packageName, "string");
424       permissionInfo.descriptionRes =
425           RuntimeEnvironment.getAppResourceTable().getResourceId(descResName);
426     }
427 
428     String labelRefOrString = itemData.getLabel();
429     if (labelRefOrString != null) {
430       if (AttributeResource.isResourceReference(labelRefOrString)) {
431         ResName labelResName =
432             AttributeResource.getResourceReference(labelRefOrString, owner.packageName, "string");
433         permissionInfo.labelRes =
434             RuntimeEnvironment.getAppResourceTable().getResourceId(labelResName);
435       } else {
436         permissionInfo.nonLocalizedLabel = labelRefOrString;
437       }
438     }
439 
440     return permissionInfo;
441   }
442 
createPermissionGroupInfo(Package owner, PermissionGroupItemData itemData)443   private static PermissionGroupInfo createPermissionGroupInfo(Package owner,
444       PermissionGroupItemData itemData) {
445     PermissionGroupInfo permissionGroupInfo = new PermissionGroupInfo();
446     populatePackageItemInfo(permissionGroupInfo, owner, itemData);
447 
448     permissionGroupInfo.metaData = metaDataToBundle(itemData.getMetaData().getValueMap());
449 
450     String descriptionRef = itemData.getDescription();
451     if (descriptionRef != null) {
452       ResName descResName =
453           AttributeResource.getResourceReference(descriptionRef, owner.packageName, "string");
454       permissionGroupInfo.descriptionRes =
455           RuntimeEnvironment.getAppResourceTable().getResourceId(descResName);
456     }
457 
458     String labelRefOrString = itemData.getLabel();
459     if (labelRefOrString != null) {
460       if (AttributeResource.isResourceReference(labelRefOrString)) {
461         ResName labelResName =
462             AttributeResource.getResourceReference(labelRefOrString, owner.packageName, "string");
463         permissionGroupInfo.labelRes =
464             RuntimeEnvironment.getAppResourceTable().getResourceId(labelResName);
465       } else {
466         permissionGroupInfo.nonLocalizedLabel = labelRefOrString;
467       }
468     }
469 
470     return permissionGroupInfo;
471   }
472 
decodeProtectionLevel(String protectionLevel)473   private static int decodeProtectionLevel(String protectionLevel) {
474     if (protectionLevel == null) {
475       return PermissionInfo.PROTECTION_NORMAL;
476     }
477 
478     int permissions = PermissionInfo.PROTECTION_NORMAL;
479     String[] levels = protectionLevel.split("\\|", 0);
480 
481     for (String level : levels) {
482       switch (level) {
483         case "normal":
484           permissions |= PermissionInfo.PROTECTION_NORMAL;
485           break;
486         case "dangerous":
487           permissions |= PermissionInfo.PROTECTION_DANGEROUS;
488           break;
489         case "signature":
490           permissions |= PermissionInfo.PROTECTION_SIGNATURE;
491           break;
492         case "signatureOrSystem":
493           permissions |= PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM;
494           break;
495         case "privileged":
496           permissions |= PermissionInfo.PROTECTION_FLAG_PRIVILEGED;
497           break;
498         case "system":
499           permissions |= PermissionInfo.PROTECTION_FLAG_SYSTEM;
500           break;
501         case "development":
502           permissions |= PermissionInfo.PROTECTION_FLAG_DEVELOPMENT;
503           break;
504         case "appop":
505           permissions |= PermissionInfo.PROTECTION_FLAG_APPOP;
506           break;
507         case "pre23":
508           permissions |= PermissionInfo.PROTECTION_FLAG_PRE23;
509           break;
510         case "installer":
511           permissions |= PermissionInfo.PROTECTION_FLAG_INSTALLER;
512           break;
513         case "verifier":
514           permissions |= PermissionInfo.PROTECTION_FLAG_VERIFIER;
515           break;
516         case "preinstalled":
517           permissions |= PermissionInfo.PROTECTION_FLAG_PREINSTALLED;
518           break;
519         case "setup":
520           permissions |= PermissionInfo.PROTECTION_FLAG_SETUP;
521           break;
522         case "instant":
523           permissions |= PermissionInfo.PROTECTION_FLAG_INSTANT;
524           break;
525         case "runtime":
526           permissions |= PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY;
527           break;
528         case "oem":
529           permissions |= PermissionInfo.PROTECTION_FLAG_OEM;
530           break;
531         case "vendorPrivileged":
532           permissions |= PermissionInfo.PROTECTION_FLAG_VENDOR_PRIVILEGED;
533           break;
534         case "textClassifier":
535           permissions |= PermissionInfo.PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER;
536           break;
537         default:
538           throw new IllegalArgumentException("unknown protection level " + protectionLevel);
539       }
540     }
541     return permissions;
542   }
543 
544   /**
545    * Goes through the meta data and puts each value in to a bundle as the correct type.
546    *
547    * <p>Note that this will convert resource identifiers specified via the value attribute as well.
548    *
549    * @param meta Meta data to put in to a bundle
550    * @return bundle containing the meta data
551    */
metaDataToBundle(Map<String, Object> meta)552   private static Bundle metaDataToBundle(Map<String, Object> meta) {
553     if (meta.size() == 0) {
554       return null;
555     }
556 
557     Bundle bundle = new Bundle();
558 
559     for (Map.Entry<String, Object> entry : meta.entrySet()) {
560       String key = entry.getKey();
561       Object value = entry.getValue();
562       if (Boolean.class.isInstance(value)) {
563         bundle.putBoolean(key, (Boolean) value);
564       } else if (Float.class.isInstance(value)) {
565         bundle.putFloat(key, (Float) value);
566       } else if (Integer.class.isInstance(value)) {
567         bundle.putInt(key, (Integer) value);
568       } else {
569         bundle.putString(key, value == null ? null : value.toString());
570       }
571     }
572     return bundle;
573   }
574 
buildClassName(String pkg, String cls)575   private static String buildClassName(String pkg, String cls) {
576     if (Strings.isNullOrEmpty(cls)) {
577       throw new IllegalArgumentException("Empty class name in package " + pkg);
578     }
579     char c = cls.charAt(0);
580     if (c == '.') {
581       return (pkg + cls).intern();
582     }
583     if (cls.indexOf('.') < 0) {
584       StringBuilder b = new StringBuilder(pkg);
585       b.append('.');
586       b.append(cls);
587       return b.toString();
588     }
589     return cls;
590     // TODO: consider reenabling this for stricter platform-complaint checking
591     // if (c >= 'a' && c <= 'z') {
592     // return cls;
593     // }
594     // throw new IllegalArgumentException("Bad class name " + cls + " in package " + pkg);
595   }
596 }
597