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