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.Build.VERSION_CODES.LOLLIPOP;
19 import static android.os.PatternMatcher.PATTERN_LITERAL;
20 import static android.os.PatternMatcher.PATTERN_PREFIX;
21 import static android.os.PatternMatcher.PATTERN_SIMPLE_GLOB;
22 import static java.util.Arrays.asList;
23 
24 import android.content.IntentFilter.MalformedMimeTypeException;
25 import android.content.pm.ActivityInfo;
26 import android.content.pm.ComponentInfo;
27 import android.content.pm.PackageInfo;
28 import android.content.pm.PackageItemInfo;
29 import android.content.pm.PackageParser;
30 import android.content.pm.PackageParser.Activity;
31 import android.content.pm.PackageParser.ActivityIntentInfo;
32 import android.content.pm.PackageParser.IntentInfo;
33 import android.content.pm.PackageParser.Package;
34 import android.content.pm.PackageParser.Permission;
35 import android.content.pm.PackageParser.PermissionGroup;
36 import android.content.pm.PackageParser.Service;
37 import android.content.pm.PackageParser.ServiceIntentInfo;
38 import android.content.pm.PathPermission;
39 import android.content.pm.PermissionGroupInfo;
40 import android.content.pm.PermissionInfo;
41 import android.content.pm.ProviderInfo;
42 import android.content.pm.ServiceInfo;
43 import android.os.Build;
44 import android.os.Build.VERSION_CODES;
45 import android.os.Bundle;
46 import android.os.Process;
47 import android.util.Pair;
48 import com.google.common.base.Strings;
49 import java.util.ArrayList;
50 import java.util.Arrays;
51 import java.util.List;
52 import java.util.Map;
53 import org.robolectric.RuntimeEnvironment;
54 import org.robolectric.manifest.ActivityData;
55 import org.robolectric.manifest.AndroidManifest;
56 import org.robolectric.manifest.BroadcastReceiverData;
57 import org.robolectric.manifest.ContentProviderData;
58 import org.robolectric.manifest.IntentFilterData;
59 import org.robolectric.manifest.IntentFilterData.DataAuthority;
60 import org.robolectric.manifest.PackageItemData;
61 import org.robolectric.manifest.PathPermissionData;
62 import org.robolectric.manifest.PermissionGroupItemData;
63 import org.robolectric.manifest.PermissionItemData;
64 import org.robolectric.manifest.ServiceData;
65 import org.robolectric.res.AttributeResource;
66 import org.robolectric.res.ResName;
67 import org.robolectric.util.ReflectionHelpers;
68 
69 /** Creates a {@link PackageInfo} from a {@link AndroidManifest} */
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     if (RuntimeEnvironment.getApiLevel() >= LOLLIPOP) {
275       pkg.codePath = codePath;
276     } else {
277       ReflectionHelpers.setField(Package.class, pkg, "mPath", codePath);
278     }
279 
280     return pkg;
281   }
282 
createPathPermission(PathPermissionData data)283   private static PathPermission createPathPermission(PathPermissionData data) {
284     if (!Strings.isNullOrEmpty(data.pathPattern)) {
285       return new PathPermission(
286           data.pathPattern, PATTERN_SIMPLE_GLOB, data.readPermission, data.writePermission);
287     } else if (!Strings.isNullOrEmpty(data.path)) {
288       return new PathPermission(
289           data.path, PATTERN_LITERAL, data.readPermission, data.writePermission);
290     } else if (!Strings.isNullOrEmpty(data.pathPrefix)) {
291       return new PathPermission(
292           data.pathPrefix, PATTERN_PREFIX, data.readPermission, data.writePermission);
293     } else {
294       throw new IllegalStateException("Permission without type");
295     }
296   }
297 
populateComponentInfo( ComponentInfo outInfo, Package owner, PackageItemData itemData)298   private static void populateComponentInfo(
299       ComponentInfo outInfo, Package owner, PackageItemData itemData) {
300     populatePackageItemInfo(outInfo, owner, itemData);
301     outInfo.applicationInfo = owner.applicationInfo;
302   }
303 
populatePackageItemInfo( PackageItemInfo outInfo, Package owner, PackageItemData itemData)304   private static void populatePackageItemInfo(
305       PackageItemInfo outInfo, Package owner, PackageItemData itemData) {
306     outInfo.name = buildClassName(owner.packageName, itemData.getName());
307     outInfo.packageName = owner.packageName;
308     outInfo.metaData = metaDataToBundle(itemData.getMetaData().getValueMap());
309   }
310 
buildBooleanList(int size, boolean defaultVal)311   private static List<Boolean> buildBooleanList(int size, boolean defaultVal) {
312     Boolean[] barray = new Boolean[size];
313     Arrays.fill(barray, defaultVal);
314     return Arrays.asList(barray);
315   }
316 
createProvider(Package pkg, ProviderInfo info)317   private static PackageParser.Provider createProvider(Package pkg, ProviderInfo info) {
318     PackageParser.Provider provider =
319         ReflectionHelpers.callConstructor(PackageParser.Provider.class);
320     populateComponent(pkg, info, provider);
321     return provider;
322   }
323 
createActivity(Package pkg, ActivityInfo activityInfo)324   private static Activity createActivity(Package pkg, ActivityInfo activityInfo) {
325     Activity activity = ReflectionHelpers.callConstructor(Activity.class);
326     populateComponent(pkg, activityInfo, activity);
327     return activity;
328   }
329 
createService(Package pkg, ServiceInfo info)330   private static Service createService(Package pkg, ServiceInfo info) {
331     PackageParser.Service service = ReflectionHelpers.callConstructor(PackageParser.Service.class);
332     populateComponent(pkg, info, service);
333     return service;
334   }
335 
populateComponent( Package pkg, ComponentInfo info, PackageParser.Component component)336   private static void populateComponent(
337       Package pkg, ComponentInfo info, PackageParser.Component component) {
338     ReflectionHelpers.setField(component, "info", info);
339     ReflectionHelpers.setField(component, "intents", new ArrayList<>());
340     ReflectionHelpers.setField(component, "owner", pkg);
341     ReflectionHelpers.setField(component, "className", info.name);
342   }
343 
populateIntentInfo(IntentFilterData intentFilterData, IntentInfo outInfo)344   private static void populateIntentInfo(IntentFilterData intentFilterData, IntentInfo outInfo) {
345     for (String action : intentFilterData.getActions()) {
346       outInfo.addAction(action);
347     }
348     for (String category : intentFilterData.getCategories()) {
349       outInfo.addCategory(category);
350     }
351     for (DataAuthority dataAuthority : intentFilterData.getAuthorities()) {
352       outInfo.addDataAuthority(dataAuthority.getHost(), dataAuthority.getPort());
353     }
354     for (String mimeType : intentFilterData.getMimeTypes()) {
355       try {
356         outInfo.addDataType(mimeType);
357       } catch (MalformedMimeTypeException e) {
358         throw new RuntimeException(e);
359       }
360     }
361     for (String scheme : intentFilterData.getSchemes()) {
362       outInfo.addDataScheme(scheme);
363     }
364     for (String pathPattern : intentFilterData.getPathPatterns()) {
365       outInfo.addDataPath(pathPattern, PATTERN_SIMPLE_GLOB);
366     }
367     for (String pathPattern : intentFilterData.getPathPrefixes()) {
368       outInfo.addDataPath(pathPattern, PATTERN_PREFIX);
369     }
370     for (String pathPattern : intentFilterData.getPaths()) {
371       outInfo.addDataPath(pathPattern, PATTERN_LITERAL);
372     }
373   }
374 
getConfigChanges(ActivityData activityData)375   private static int getConfigChanges(ActivityData activityData) {
376     String s = activityData.getConfigChanges();
377 
378     int res = 0;
379 
380     // quick sanity check.
381     if (s == null || "".equals(s)) {
382       return res;
383     }
384 
385     String[] pieces = s.split("\\|", 0);
386 
387     for (String s1 : pieces) {
388       s1 = s1.trim();
389 
390       for (Pair<String, Integer> pair : CONFIG_OPTIONS) {
391         if (s1.equals(pair.first)) {
392           res |= pair.second;
393           break;
394         }
395       }
396     }
397 
398     // Matches platform behavior
399     if (RuntimeEnvironment.getApiLevel() >= Build.VERSION_CODES.O) {
400       res |= ActivityInfo.CONFIG_MNC;
401       res |= ActivityInfo.CONFIG_MCC;
402     }
403 
404     return res;
405   }
406 
decodeFlags(Map<String, String> applicationAttributes)407   private static int decodeFlags(Map<String, String> applicationAttributes) {
408     int applicationFlags = 0;
409     for (Pair<String, Integer> pair : APPLICATION_FLAGS) {
410       if ("true".equals(applicationAttributes.get(pair.first))) {
411         applicationFlags |= pair.second;
412       }
413     }
414     return applicationFlags;
415   }
416 
createPermissionInfo(Package owner, PermissionItemData itemData)417   private static PermissionInfo createPermissionInfo(Package owner, PermissionItemData itemData) {
418     PermissionInfo permissionInfo = new PermissionInfo();
419     populatePackageItemInfo(permissionInfo, owner, itemData);
420 
421     permissionInfo.group = itemData.getPermissionGroup();
422     permissionInfo.protectionLevel = decodeProtectionLevel(itemData.getProtectionLevel());
423     permissionInfo.metaData = metaDataToBundle(itemData.getMetaData().getValueMap());
424 
425     String descriptionRef = itemData.getDescription();
426     if (descriptionRef != null) {
427       ResName descResName =
428           AttributeResource.getResourceReference(descriptionRef, owner.packageName, "string");
429       permissionInfo.descriptionRes =
430           RuntimeEnvironment.getAppResourceTable().getResourceId(descResName);
431     }
432 
433     String labelRefOrString = itemData.getLabel();
434     if (labelRefOrString != null) {
435       if (AttributeResource.isResourceReference(labelRefOrString)) {
436         ResName labelResName =
437             AttributeResource.getResourceReference(labelRefOrString, owner.packageName, "string");
438         permissionInfo.labelRes =
439             RuntimeEnvironment.getAppResourceTable().getResourceId(labelResName);
440       } else {
441         permissionInfo.nonLocalizedLabel = labelRefOrString;
442       }
443     }
444 
445     return permissionInfo;
446   }
447 
createPermissionGroupInfo(Package owner, PermissionGroupItemData itemData)448   private static PermissionGroupInfo createPermissionGroupInfo(Package owner,
449       PermissionGroupItemData itemData) {
450     PermissionGroupInfo permissionGroupInfo = new PermissionGroupInfo();
451     populatePackageItemInfo(permissionGroupInfo, owner, itemData);
452 
453     permissionGroupInfo.metaData = metaDataToBundle(itemData.getMetaData().getValueMap());
454 
455     String descriptionRef = itemData.getDescription();
456     if (descriptionRef != null) {
457       ResName descResName =
458           AttributeResource.getResourceReference(descriptionRef, owner.packageName, "string");
459       permissionGroupInfo.descriptionRes =
460           RuntimeEnvironment.getAppResourceTable().getResourceId(descResName);
461     }
462 
463     String labelRefOrString = itemData.getLabel();
464     if (labelRefOrString != null) {
465       if (AttributeResource.isResourceReference(labelRefOrString)) {
466         ResName labelResName =
467             AttributeResource.getResourceReference(labelRefOrString, owner.packageName, "string");
468         permissionGroupInfo.labelRes =
469             RuntimeEnvironment.getAppResourceTable().getResourceId(labelResName);
470       } else {
471         permissionGroupInfo.nonLocalizedLabel = labelRefOrString;
472       }
473     }
474 
475     return permissionGroupInfo;
476   }
477 
decodeProtectionLevel(String protectionLevel)478   private static int decodeProtectionLevel(String protectionLevel) {
479     if (protectionLevel == null) {
480       return PermissionInfo.PROTECTION_NORMAL;
481     }
482 
483     int permissions = PermissionInfo.PROTECTION_NORMAL;
484     String[] levels = protectionLevel.split("\\|", 0);
485 
486     for (String level : levels) {
487       switch (level) {
488         case "normal":
489           permissions |= PermissionInfo.PROTECTION_NORMAL;
490           break;
491         case "dangerous":
492           permissions |= PermissionInfo.PROTECTION_DANGEROUS;
493           break;
494         case "signature":
495           permissions |= PermissionInfo.PROTECTION_SIGNATURE;
496           break;
497         case "signatureOrSystem":
498           permissions |= PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM;
499           break;
500         case "privileged":
501           permissions |= PermissionInfo.PROTECTION_FLAG_PRIVILEGED;
502           break;
503         case "system":
504           permissions |= PermissionInfo.PROTECTION_FLAG_SYSTEM;
505           break;
506         case "development":
507           permissions |= PermissionInfo.PROTECTION_FLAG_DEVELOPMENT;
508           break;
509         case "appop":
510           permissions |= PermissionInfo.PROTECTION_FLAG_APPOP;
511           break;
512         case "pre23":
513           permissions |= PermissionInfo.PROTECTION_FLAG_PRE23;
514           break;
515         case "installer":
516           permissions |= PermissionInfo.PROTECTION_FLAG_INSTALLER;
517           break;
518         case "verifier":
519           permissions |= PermissionInfo.PROTECTION_FLAG_VERIFIER;
520           break;
521         case "preinstalled":
522           permissions |= PermissionInfo.PROTECTION_FLAG_PREINSTALLED;
523           break;
524         case "setup":
525           permissions |= PermissionInfo.PROTECTION_FLAG_SETUP;
526           break;
527         case "instant":
528           permissions |= PermissionInfo.PROTECTION_FLAG_INSTANT;
529           break;
530         case "runtime":
531           permissions |= PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY;
532           break;
533         case "oem":
534           permissions |= PermissionInfo.PROTECTION_FLAG_OEM;
535           break;
536         case "vendorPrivileged":
537           permissions |= PermissionInfo.PROTECTION_FLAG_VENDOR_PRIVILEGED;
538           break;
539         case "textClassifier":
540           permissions |= PermissionInfo.PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER;
541           break;
542         default:
543           throw new IllegalArgumentException("unknown protection level " + protectionLevel);
544       }
545     }
546     return permissions;
547   }
548 
549   /**
550    * Goes through the meta data and puts each value in to a bundle as the correct type.
551    *
552    * <p>Note that this will convert resource identifiers specified via the value attribute as well.
553    *
554    * @param meta Meta data to put in to a bundle
555    * @return bundle containing the meta data
556    */
metaDataToBundle(Map<String, Object> meta)557   private static Bundle metaDataToBundle(Map<String, Object> meta) {
558     if (meta.size() == 0) {
559       return null;
560     }
561 
562     Bundle bundle = new Bundle();
563 
564     for (Map.Entry<String, Object> entry : meta.entrySet()) {
565       String key = entry.getKey();
566       Object value = entry.getValue();
567       if (Boolean.class.isInstance(value)) {
568         bundle.putBoolean(key, (Boolean) value);
569       } else if (Float.class.isInstance(value)) {
570         bundle.putFloat(key, (Float) value);
571       } else if (Integer.class.isInstance(value)) {
572         bundle.putInt(key, (Integer) value);
573       } else {
574         bundle.putString(key, value == null ? null : value.toString());
575       }
576     }
577     return bundle;
578   }
579 
buildClassName(String pkg, String cls)580   private static String buildClassName(String pkg, String cls) {
581     if (Strings.isNullOrEmpty(cls)) {
582       throw new IllegalArgumentException("Empty class name in package " + pkg);
583     }
584     char c = cls.charAt(0);
585     if (c == '.') {
586       return (pkg + cls).intern();
587     }
588     if (cls.indexOf('.') < 0) {
589       StringBuilder b = new StringBuilder(pkg);
590       b.append('.');
591       b.append(cls);
592       return b.toString();
593     }
594     return cls;
595     // TODO: consider reenabling this for stricter platform-complaint checking
596     // if (c >= 'a' && c <= 'z') {
597     // return cls;
598     // }
599     // throw new IllegalArgumentException("Bad class name " + cls + " in package " + pkg);
600   }
601 }
602