diff options
Diffstat (limited to 'tools')
18 files changed, 553 insertions, 220 deletions
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp index d12ab3b725c8..5e2e82601b47 100644 --- a/tools/aapt/Command.cpp +++ b/tools/aapt/Command.cpp @@ -383,6 +383,16 @@ static void printUsesPermission(const String8& name, bool optional=false, int ma } } +static void printUsesPermissionSdk23(const String8& name, int maxSdkVersion=-1) { + printf("uses-permission-sdk-23: "); + + printf("name='%s'", ResTable::normalizeForOutput(name.string()).string()); + if (maxSdkVersion != -1) { + printf(" maxSdkVersion='%d'", maxSdkVersion); + } + printf("\n"); +} + static void printUsesImpliedPermission(const String8& name, const String8& reason) { printf("uses-implied-permission: name='%s' reason='%s'\n", ResTable::normalizeForOutput(name.string()).string(), @@ -463,12 +473,20 @@ static void printComponentPresence(const char* componentName) { * a pre-requisite or some other reason. */ struct ImpliedFeature { + ImpliedFeature() : impliedBySdk23(false) {} + ImpliedFeature(const String8& n, bool sdk23) : name(n), impliedBySdk23(sdk23) {} + /** * Name of the implied feature. */ String8 name; /** + * Was this implied by a permission from SDK 23 (<uses-permission-sdk-23 />)? + */ + bool impliedBySdk23; + + /** * List of human-readable reasons for why this feature was implied. */ SortedVector<String8> reasons; @@ -497,18 +515,24 @@ struct FeatureGroup { }; static void addImpliedFeature(KeyedVector<String8, ImpliedFeature>* impliedFeatures, - const char* name, const char* reason) { + const char* name, const char* reason, bool sdk23) { String8 name8(name); ssize_t idx = impliedFeatures->indexOfKey(name8); if (idx < 0) { - idx = impliedFeatures->add(name8, ImpliedFeature()); - impliedFeatures->editValueAt(idx).name = name8; + idx = impliedFeatures->add(name8, ImpliedFeature(name8, sdk23)); } - impliedFeatures->editValueAt(idx).reasons.add(String8(reason)); + + ImpliedFeature* feature = &impliedFeatures->editValueAt(idx); + + // A non-sdk 23 implied feature takes precedence. + if (feature->impliedBySdk23 && !sdk23) { + feature->impliedBySdk23 = false; + } + feature->reasons.add(String8(reason)); } -static void printFeatureGroup(const FeatureGroup& grp, - const KeyedVector<String8, ImpliedFeature>* impliedFeatures = NULL) { +static void printFeatureGroupImpl(const FeatureGroup& grp, + const KeyedVector<String8, ImpliedFeature>* impliedFeatures) { printf("feature-group: label='%s'\n", grp.label.string()); if (grp.openGLESVersion > 0) { @@ -536,9 +560,11 @@ static void printFeatureGroup(const FeatureGroup& grp, String8 printableFeatureName(ResTable::normalizeForOutput( impliedFeature.name.string())); - printf(" uses-feature: name='%s'\n", printableFeatureName.string()); - printf(" uses-implied-feature: name='%s' reason='", - printableFeatureName.string()); + const char* sdk23Suffix = impliedFeature.impliedBySdk23 ? "-sdk-23" : ""; + + printf(" uses-feature%s: name='%s'\n", sdk23Suffix, printableFeatureName.string()); + printf(" uses-implied-feature%s: name='%s' reason='", sdk23Suffix, + printableFeatureName.string()); const size_t numReasons = impliedFeature.reasons.size(); for (size_t j = 0; j < numReasons; j++) { printf("%s", impliedFeature.reasons[j].string()); @@ -552,6 +578,15 @@ static void printFeatureGroup(const FeatureGroup& grp, } } +static void printFeatureGroup(const FeatureGroup& grp) { + printFeatureGroupImpl(grp, NULL); +} + +static void printDefaultFeatureGroup(const FeatureGroup& grp, + const KeyedVector<String8, ImpliedFeature>& impliedFeatures) { + printFeatureGroupImpl(grp, &impliedFeatures); +} + static void addParentFeatures(FeatureGroup* grp, const String8& name) { if (name == "android.hardware.camera.autofocus" || name == "android.hardware.camera.flash") { @@ -572,6 +607,72 @@ static void addParentFeatures(FeatureGroup* grp, const String8& name) { } } +static void addImpliedFeaturesForPermission(const int targetSdk, const String8& name, + KeyedVector<String8, ImpliedFeature>* impliedFeatures, + bool impliedBySdk23Permission) { + if (name == "android.permission.CAMERA") { + addImpliedFeature(impliedFeatures, "android.hardware.camera", + String8::format("requested %s permission", name.string()) + .string(), impliedBySdk23Permission); + } else if (name == "android.permission.ACCESS_FINE_LOCATION") { + addImpliedFeature(impliedFeatures, "android.hardware.location.gps", + String8::format("requested %s permission", name.string()) + .string(), impliedBySdk23Permission); + addImpliedFeature(impliedFeatures, "android.hardware.location", + String8::format("requested %s permission", name.string()) + .string(), impliedBySdk23Permission); + } else if (name == "android.permission.ACCESS_MOCK_LOCATION") { + addImpliedFeature(impliedFeatures, "android.hardware.location", + String8::format("requested %s permission", name.string()) + .string(), impliedBySdk23Permission); + } else if (name == "android.permission.ACCESS_COARSE_LOCATION") { + addImpliedFeature(impliedFeatures, "android.hardware.location.network", + String8::format("requested %s permission", name.string()) + .string(), impliedBySdk23Permission); + addImpliedFeature(impliedFeatures, "android.hardware.location", + String8::format("requested %s permission", name.string()) + .string(), impliedBySdk23Permission); + } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" || + name == "android.permission.INSTALL_LOCATION_PROVIDER") { + addImpliedFeature(impliedFeatures, "android.hardware.location", + String8::format("requested %s permission", name.string()) + .string(), impliedBySdk23Permission); + } else if (name == "android.permission.BLUETOOTH" || + name == "android.permission.BLUETOOTH_ADMIN") { + if (targetSdk > 4) { + addImpliedFeature(impliedFeatures, "android.hardware.bluetooth", + String8::format("requested %s permission", name.string()) + .string(), impliedBySdk23Permission); + addImpliedFeature(impliedFeatures, "android.hardware.bluetooth", + "targetSdkVersion > 4", impliedBySdk23Permission); + } + } else if (name == "android.permission.RECORD_AUDIO") { + addImpliedFeature(impliedFeatures, "android.hardware.microphone", + String8::format("requested %s permission", name.string()) + .string(), impliedBySdk23Permission); + } else if (name == "android.permission.ACCESS_WIFI_STATE" || + name == "android.permission.CHANGE_WIFI_STATE" || + name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") { + addImpliedFeature(impliedFeatures, "android.hardware.wifi", + String8::format("requested %s permission", name.string()) + .string(), impliedBySdk23Permission); + } else if (name == "android.permission.CALL_PHONE" || + name == "android.permission.CALL_PRIVILEGED" || + name == "android.permission.MODIFY_PHONE_STATE" || + name == "android.permission.PROCESS_OUTGOING_CALLS" || + name == "android.permission.READ_SMS" || + name == "android.permission.RECEIVE_SMS" || + name == "android.permission.RECEIVE_MMS" || + name == "android.permission.RECEIVE_WAP_PUSH" || + name == "android.permission.SEND_SMS" || + name == "android.permission.WRITE_APN_SETTINGS" || + name == "android.permission.WRITE_SMS") { + addImpliedFeature(impliedFeatures, "android.hardware.telephony", + String8("requested a telephony permission").string(), + impliedBySdk23Permission); + } +} + /* * Handle the "dump" command, to extract select data from an archive. */ @@ -712,7 +813,8 @@ int doDump(Bundle* bundle) size_t len; ResXMLTree::event_code_t code; int depth = 0; - while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { + while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && + code != ResXMLTree::BAD_DOCUMENT) { if (code == ResXMLTree::END_TAG) { depth--; continue; @@ -735,25 +837,53 @@ int doDump(Bundle* bundle) } String8 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL); printf("package: %s\n", ResTable::normalizeForOutput(pkg.string()).string()); - } else if (depth == 2 && tag == "permission") { - String8 error; - String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error); - if (error != "") { - fprintf(stderr, "ERROR: %s\n", error.string()); - goto bail; - } - printf("permission: %s\n", - ResTable::normalizeForOutput(name.string()).string()); - } else if (depth == 2 && tag == "uses-permission") { - String8 error; - String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error); - if (error != "") { - fprintf(stderr, "ERROR: %s\n", error.string()); - goto bail; + } else if (depth == 2) { + if (tag == "permission") { + String8 error; + String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error); + if (error != "") { + fprintf(stderr, "ERROR: %s\n", error.string()); + goto bail; + } + + if (name == "") { + fprintf(stderr, "ERROR: missing 'android:name' for permission\n"); + goto bail; + } + printf("permission: %s\n", + ResTable::normalizeForOutput(name.string()).string()); + } else if (tag == "uses-permission") { + String8 error; + String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error); + if (error != "") { + fprintf(stderr, "ERROR: %s\n", error.string()); + goto bail; + } + + if (name == "") { + fprintf(stderr, "ERROR: missing 'android:name' for uses-permission\n"); + goto bail; + } + printUsesPermission(name, + AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0, + AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR)); + } else if (tag == "uses-permission-sdk-23" || tag == "uses-permission-sdk-m") { + String8 error; + String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error); + if (error != "") { + fprintf(stderr, "ERROR: %s\n", error.string()); + goto bail; + } + + if (name == "") { + fprintf(stderr, "ERROR: missing 'android:name' for " + "uses-permission-sdk-23\n"); + goto bail; + } + printUsesPermissionSdk23( + name, + AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR)); } - printUsesPermission(name, - AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0, - AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR)); } } } else if (strcmp("badging", option) == 0) { @@ -893,7 +1023,8 @@ int doDump(Bundle* bundle) Vector<FeatureGroup> featureGroups; KeyedVector<String8, ImpliedFeature> impliedFeatures; - while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { + while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && + code != ResXMLTree::BAD_DOCUMENT) { if (code == ResXMLTree::END_TAG) { depth--; if (depth < 2) { @@ -924,8 +1055,10 @@ int doDump(Bundle* bundle) ResTable::normalizeForOutput(aName.string()).string()); } printf(" label='%s' icon='%s'\n", - ResTable::normalizeForOutput(activityLabel.string()).string(), - ResTable::normalizeForOutput(activityIcon.string()).string()); + ResTable::normalizeForOutput(activityLabel.string()) + .string(), + ResTable::normalizeForOutput(activityIcon.string()) + .string()); } if (isLeanbackLauncherActivity) { printf("leanback-launchable-activity:"); @@ -934,9 +1067,12 @@ int doDump(Bundle* bundle) ResTable::normalizeForOutput(aName.string()).string()); } printf(" label='%s' icon='%s' banner='%s'\n", - ResTable::normalizeForOutput(activityLabel.string()).string(), - ResTable::normalizeForOutput(activityIcon.string()).string(), - ResTable::normalizeForOutput(activityBanner.string()).string()); + ResTable::normalizeForOutput(activityLabel.string()) + .string(), + ResTable::normalizeForOutput(activityIcon.string()) + .string(), + ResTable::normalizeForOutput(activityBanner.string()) + .string()); } } if (!hasIntentFilter) { @@ -964,18 +1100,21 @@ int doDump(Bundle* bundle) hasLauncher |= catLauncher; hasCameraActivity |= actCamera; hasCameraSecureActivity |= actCameraSecure; - hasOtherActivities |= !actMainActivity && !actCamera && !actCameraSecure; + hasOtherActivities |= + !actMainActivity && !actCamera && !actCameraSecure; } else if (withinReceiver) { hasWidgetReceivers |= actWidgetReceivers; hasDeviceAdminReceiver |= (actDeviceAdminEnabled && hasBindDeviceAdminPermission); - hasOtherReceivers |= (!actWidgetReceivers && !actDeviceAdminEnabled); + hasOtherReceivers |= + (!actWidgetReceivers && !actDeviceAdminEnabled); } else if (withinService) { hasImeService |= actImeService; hasWallpaperService |= actWallpaperService; hasAccessibilityService |= (actAccessibilityService && hasBindAccessibilityServicePermission); - hasPrintService |= (actPrintService && hasBindPrintServicePermission); + hasPrintService |= + (actPrintService && hasBindPrintServicePermission); hasNotificationListenerService |= actNotificationListenerService && hasBindNotificationListenerServicePermission; hasDreamService |= actDreamService && hasBindDreamServicePermission; @@ -984,7 +1123,8 @@ int doDump(Bundle* bundle) !actHostApduService && !actOffHostApduService && !actNotificationListenerService); } else if (withinProvider) { - hasDocumentsProvider |= actDocumentsProvider && hasRequiredSafAttributes; + hasDocumentsProvider |= + actDocumentsProvider && hasRequiredSafAttributes; } } withinIntentFilter = false; @@ -1125,7 +1265,8 @@ int doDump(Bundle* bundle) goto bail; } - String8 banner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR, &error); + String8 banner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR, + &error); if (error != "") { fprintf(stderr, "ERROR getting 'android:banner' attribute: %s\n", error.string()); @@ -1135,7 +1276,8 @@ int doDump(Bundle* bundle) ResTable::normalizeForOutput(label.string()).string()); printf("icon='%s'", ResTable::normalizeForOutput(icon.string()).string()); if (banner != "") { - printf(" banner='%s'", ResTable::normalizeForOutput(banner.string()).string()); + printf(" banner='%s'", + ResTable::normalizeForOutput(banner.string()).string()); } printf("\n"); if (testOnly != 0) { @@ -1178,13 +1320,15 @@ int doDump(Bundle* bundle) } } } else if (tag == "uses-sdk") { - int32_t code = AaptXml::getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error); + int32_t code = AaptXml::getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, + &error); if (error != "") { error = ""; String8 name = AaptXml::getResolvedAttribute(res, tree, MIN_SDK_VERSION_ATTR, &error); if (error != "") { - fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n", + fprintf(stderr, + "ERROR getting 'android:minSdkVersion' attribute: %s\n", error.string()); goto bail; } @@ -1205,7 +1349,8 @@ int doDump(Bundle* bundle) String8 name = AaptXml::getResolvedAttribute(res, tree, TARGET_SDK_VERSION_ATTR, &error); if (error != "") { - fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n", + fprintf(stderr, + "ERROR getting 'android:targetSdkVersion' attribute: %s\n", error.string()); goto bail; } @@ -1297,90 +1442,58 @@ int doDump(Bundle* bundle) } } else if (tag == "uses-permission") { String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error); - if (name != "" && error == "") { - if (name == "android.permission.CAMERA") { - addImpliedFeature(&impliedFeatures, "android.hardware.camera", - String8::format("requested %s permission", name.string()) - .string()); - } else if (name == "android.permission.ACCESS_FINE_LOCATION") { - addImpliedFeature(&impliedFeatures, "android.hardware.location.gps", - String8::format("requested %s permission", name.string()) - .string()); - addImpliedFeature(&impliedFeatures, "android.hardware.location", - String8::format("requested %s permission", name.string()) - .string()); - } else if (name == "android.permission.ACCESS_MOCK_LOCATION") { - addImpliedFeature(&impliedFeatures, "android.hardware.location", - String8::format("requested %s permission", name.string()) - .string()); - } else if (name == "android.permission.ACCESS_COARSE_LOCATION") { - addImpliedFeature(&impliedFeatures, "android.hardware.location.network", - String8::format("requested %s permission", name.string()) - .string()); - addImpliedFeature(&impliedFeatures, "android.hardware.location", - String8::format("requested %s permission", name.string()) - .string()); - } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" || - name == "android.permission.INSTALL_LOCATION_PROVIDER") { - addImpliedFeature(&impliedFeatures, "android.hardware.location", - String8::format("requested %s permission", name.string()) - .string()); - } else if (name == "android.permission.BLUETOOTH" || - name == "android.permission.BLUETOOTH_ADMIN") { - if (targetSdk > 4) { - addImpliedFeature(&impliedFeatures, "android.hardware.bluetooth", - String8::format("requested %s permission", name.string()) - .string()); - addImpliedFeature(&impliedFeatures, "android.hardware.bluetooth", - "targetSdkVersion > 4"); - } - } else if (name == "android.permission.RECORD_AUDIO") { - addImpliedFeature(&impliedFeatures, "android.hardware.microphone", - String8::format("requested %s permission", name.string()) - .string()); - } else if (name == "android.permission.ACCESS_WIFI_STATE" || - name == "android.permission.CHANGE_WIFI_STATE" || - name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") { - addImpliedFeature(&impliedFeatures, "android.hardware.wifi", - String8::format("requested %s permission", name.string()) - .string()); - } else if (name == "android.permission.CALL_PHONE" || - name == "android.permission.CALL_PRIVILEGED" || - name == "android.permission.MODIFY_PHONE_STATE" || - name == "android.permission.PROCESS_OUTGOING_CALLS" || - name == "android.permission.READ_SMS" || - name == "android.permission.RECEIVE_SMS" || - name == "android.permission.RECEIVE_MMS" || - name == "android.permission.RECEIVE_WAP_PUSH" || - name == "android.permission.SEND_SMS" || - name == "android.permission.WRITE_APN_SETTINGS" || - name == "android.permission.WRITE_SMS") { - addImpliedFeature(&impliedFeatures, "android.hardware.telephony", - String8("requested a telephony permission").string()); - } else if (name == "android.permission.WRITE_EXTERNAL_STORAGE") { - hasWriteExternalStoragePermission = true; - } else if (name == "android.permission.READ_EXTERNAL_STORAGE") { - hasReadExternalStoragePermission = true; - } else if (name == "android.permission.READ_PHONE_STATE") { - hasReadPhoneStatePermission = true; - } else if (name == "android.permission.READ_CONTACTS") { - hasReadContactsPermission = true; - } else if (name == "android.permission.WRITE_CONTACTS") { - hasWriteContactsPermission = true; - } else if (name == "android.permission.READ_CALL_LOG") { - hasReadCallLogPermission = true; - } else if (name == "android.permission.WRITE_CALL_LOG") { - hasWriteCallLogPermission = true; - } + if (error != "") { + fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", + error.string()); + goto bail; + } - printUsesPermission(name, - AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0, - AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR)); - } else { + if (name == "") { + fprintf(stderr, "ERROR: missing 'android:name' for uses-permission\n"); + goto bail; + } + + addImpliedFeaturesForPermission(targetSdk, name, &impliedFeatures, false); + + if (name == "android.permission.WRITE_EXTERNAL_STORAGE") { + hasWriteExternalStoragePermission = true; + } else if (name == "android.permission.READ_EXTERNAL_STORAGE") { + hasReadExternalStoragePermission = true; + } else if (name == "android.permission.READ_PHONE_STATE") { + hasReadPhoneStatePermission = true; + } else if (name == "android.permission.READ_CONTACTS") { + hasReadContactsPermission = true; + } else if (name == "android.permission.WRITE_CONTACTS") { + hasWriteContactsPermission = true; + } else if (name == "android.permission.READ_CALL_LOG") { + hasReadCallLogPermission = true; + } else if (name == "android.permission.WRITE_CALL_LOG") { + hasWriteCallLogPermission = true; + } + + printUsesPermission(name, + AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0, + AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR)); + + } else if (tag == "uses-permission-sdk-23" || tag == "uses-permission-sdk-m") { + String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error); + if (error != "") { fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string()); goto bail; } + + if (name == "") { + fprintf(stderr, "ERROR: missing 'android:name' for " + "uses-permission-sdk-23\n"); + goto bail; + } + + addImpliedFeaturesForPermission(targetSdk, name, &impliedFeatures, true); + + printUsesPermissionSdk23( + name, AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR)); + } else if (tag == "uses-package") { String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error); if (name != "" && error == "") { @@ -1422,7 +1535,8 @@ int doDump(Bundle* bundle) } else if (tag == "package-verifier") { String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error); if (name != "" && error == "") { - String8 publicKey = AaptXml::getAttribute(tree, PUBLIC_KEY_ATTR, &error); + String8 publicKey = AaptXml::getAttribute(tree, PUBLIC_KEY_ATTR, + &error); if (publicKey != "" && error == "") { printf("package-verifier: name='%s' publicKey='%s'\n", ResTable::normalizeForOutput(name.string()).string(), @@ -1485,12 +1599,18 @@ int doDump(Bundle* bundle) if (error == "") { if (orien == 0 || orien == 6 || orien == 8) { // Requests landscape, sensorLandscape, or reverseLandscape. - addImpliedFeature(&impliedFeatures, "android.hardware.screen.landscape", - "one or more activities have specified a landscape orientation"); + addImpliedFeature(&impliedFeatures, + "android.hardware.screen.landscape", + "one or more activities have specified a " + "landscape orientation", + false); } else if (orien == 1 || orien == 7 || orien == 9) { // Requests portrait, sensorPortrait, or reversePortrait. - addImpliedFeature(&impliedFeatures, "android.hardware.screen.portrait", - "one or more activities have specified a portrait orientation"); + addImpliedFeature(&impliedFeatures, + "android.hardware.screen.portrait", + "one or more activities have specified a " + "portrait orientation", + false); } } } else if (tag == "uses-library") { @@ -1524,8 +1644,10 @@ int doDump(Bundle* bundle) hasBindDeviceAdminPermission = true; } } else { - fprintf(stderr, "ERROR getting 'android:permission' attribute for" - " receiver '%s': %s\n", receiverName.string(), error.string()); + fprintf(stderr, + "ERROR getting 'android:permission' attribute for" + " receiver '%s': %s\n", + receiverName.string(), error.string()); } } else if (tag == "service") { withinService = true; @@ -1542,20 +1664,24 @@ int doDump(Bundle* bundle) if (error == "") { if (permission == "android.permission.BIND_INPUT_METHOD") { hasBindInputMethodPermission = true; - } else if (permission == "android.permission.BIND_ACCESSIBILITY_SERVICE") { + } else if (permission == + "android.permission.BIND_ACCESSIBILITY_SERVICE") { hasBindAccessibilityServicePermission = true; - } else if (permission == "android.permission.BIND_PRINT_SERVICE") { + } else if (permission == + "android.permission.BIND_PRINT_SERVICE") { hasBindPrintServicePermission = true; - } else if (permission == "android.permission.BIND_NFC_SERVICE") { + } else if (permission == + "android.permission.BIND_NFC_SERVICE") { hasBindNfcServicePermission = true; - } else if (permission == "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE") { + } else if (permission == + "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE") { hasBindNotificationListenerServicePermission = true; } else if (permission == "android.permission.BIND_DREAM_SERVICE") { hasBindDreamServicePermission = true; } } else { - fprintf(stderr, "ERROR getting 'android:permission' attribute for" - " service '%s': %s\n", serviceName.string(), error.string()); + fprintf(stderr, "ERROR getting 'android:permission' attribute for " + "service '%s': %s\n", serviceName.string(), error.string()); } } else if (tag == "provider") { withinProvider = true; @@ -1563,7 +1689,8 @@ int doDump(Bundle* bundle) bool exported = AaptXml::getResolvedIntegerAttribute(res, tree, EXPORTED_ATTR, &error); if (error != "") { - fprintf(stderr, "ERROR getting 'android:exported' attribute for provider:" + fprintf(stderr, + "ERROR getting 'android:exported' attribute for provider:" " %s\n", error.string()); goto bail; } @@ -1571,16 +1698,17 @@ int doDump(Bundle* bundle) bool grantUriPermissions = AaptXml::getResolvedIntegerAttribute( res, tree, GRANT_URI_PERMISSIONS_ATTR, &error); if (error != "") { - fprintf(stderr, "ERROR getting 'android:grantUriPermissions' attribute for provider:" - " %s\n", error.string()); + fprintf(stderr, + "ERROR getting 'android:grantUriPermissions' attribute for " + "provider: %s\n", error.string()); goto bail; } String8 permission = AaptXml::getResolvedAttribute(res, tree, PERMISSION_ATTR, &error); if (error != "") { - fprintf(stderr, "ERROR getting 'android:permission' attribute for provider:" - " %s\n", error.string()); + fprintf(stderr, "ERROR getting 'android:permission' attribute for " + "provider: %s\n", error.string()); goto bail; } @@ -1661,8 +1789,9 @@ int doDump(Bundle* bundle) } else if (withinService && tag == "meta-data") { String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error); if (error != "") { - fprintf(stderr, "ERROR getting 'android:name' attribute for" - " meta-data tag in service '%s': %s\n", serviceName.string(), error.string()); + fprintf(stderr, "ERROR getting 'android:name' attribute for " + "meta-data tag in service '%s': %s\n", serviceName.string(), + error.string()); goto bail; } @@ -1676,8 +1805,9 @@ int doDump(Bundle* bundle) String8 xmlPath = AaptXml::getResolvedAttribute(res, tree, RESOURCE_ATTR, &error); if (error != "") { - fprintf(stderr, "ERROR getting 'android:resource' attribute for" - " meta-data tag in service '%s': %s\n", serviceName.string(), error.string()); + fprintf(stderr, "ERROR getting 'android:resource' attribute for " + "meta-data tag in service '%s': %s\n", + serviceName.string(), error.string()); goto bail; } @@ -1731,15 +1861,19 @@ int doDump(Bundle* bundle) actImeService = true; } else if (action == "android.service.wallpaper.WallpaperService") { actWallpaperService = true; - } else if (action == "android.accessibilityservice.AccessibilityService") { + } else if (action == + "android.accessibilityservice.AccessibilityService") { actAccessibilityService = true; - } else if (action == "android.printservice.PrintService") { + } else if (action =="android.printservice.PrintService") { actPrintService = true; - } else if (action == "android.nfc.cardemulation.action.HOST_APDU_SERVICE") { + } else if (action == + "android.nfc.cardemulation.action.HOST_APDU_SERVICE") { actHostApduService = true; - } else if (action == "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") { + } else if (action == + "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") { actOffHostApduService = true; - } else if (action == "android.service.notification.NotificationListenerService") { + } else if (action == + "android.service.notification.NotificationListenerService") { actNotificationListenerService = true; } else if (action == "android.service.dreams.DreamService") { actDreamService = true; @@ -1814,12 +1948,12 @@ int doDump(Bundle* bundle) } addImpliedFeature(&impliedFeatures, "android.hardware.touchscreen", - "default feature for all apps"); + "default feature for all apps", false); const size_t numFeatureGroups = featureGroups.size(); if (numFeatureGroups == 0) { // If no <feature-group> tags were defined, apply auto-implied features. - printFeatureGroup(commonFeatures, &impliedFeatures); + printDefaultFeatureGroup(commonFeatures, impliedFeatures); } else { // <feature-group> tags are defined, so we ignore implied features and diff --git a/tools/layoutlib/.idea/encodings.xml b/tools/layoutlib/.idea/encodings.xml index e206d70d8595..f758959656c7 100644 --- a/tools/layoutlib/.idea/encodings.xml +++ b/tools/layoutlib/.idea/encodings.xml @@ -1,5 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <project version="4"> - <component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false" /> -</project> - + <component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false"> + <file url="PROJECT" charset="UTF-8" /> + </component> +</project>
\ No newline at end of file diff --git a/tools/layoutlib/bridge/src/android/text/Hyphenator_Delegate.java b/tools/layoutlib/bridge/src/android/text/Hyphenator_Delegate.java index 5a595970e195..44ce7311a95c 100644 --- a/tools/layoutlib/bridge/src/android/text/Hyphenator_Delegate.java +++ b/tools/layoutlib/bridge/src/android/text/Hyphenator_Delegate.java @@ -20,9 +20,10 @@ import com.android.layoutlib.bridge.impl.DelegateManager; import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import java.io.File; +import java.nio.ByteBuffer; /** - * Delegate that overrides implementation for certain methods in {@link android.text.StaticLayout} + * Delegate that overrides implementation for certain methods in {@link android.text.Hyphenator} * <p/> * Through the layoutlib_create tool, selected methods of StaticLayout have been replaced * by calls to methods of the same name in this delegate class. @@ -38,7 +39,7 @@ public class Hyphenator_Delegate { return null; } - /*package*/ static long loadHyphenator(String patternData) { + /*package*/ static long loadHyphenator(ByteBuffer buf, int offset) { return sDelegateManager.addNewDelegate(new Hyphenator_Delegate()); } } diff --git a/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java b/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java index 1b0ba5156acd..65c0a07bbac4 100644 --- a/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java +++ b/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java @@ -13,6 +13,7 @@ import android.icu.util.ULocale; import android.text.Primitive.PrimitiveType; import android.text.StaticLayout.LineBreaks; +import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -52,8 +53,8 @@ public class StaticLayout_Delegate { } @LayoutlibDelegate - /*package*/ static long nLoadHyphenator(String patternData) { - return Hyphenator_Delegate.loadHyphenator(patternData); + /*package*/ static long nLoadHyphenator(ByteBuffer buf, int offset) { + return Hyphenator_Delegate.loadHyphenator(buf, offset); } @LayoutlibDelegate diff --git a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java index 5db1bde5f3f0..723e8278bcc0 100644 --- a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java +++ b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java @@ -23,6 +23,7 @@ import com.android.ide.common.rendering.api.ResourceReference; import com.android.ide.common.rendering.api.ResourceValue; import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.BridgeConstants; +import com.android.layoutlib.bridge.MockView; import com.android.layoutlib.bridge.android.BridgeContext; import com.android.layoutlib.bridge.android.BridgeXmlBlockParser; import com.android.layoutlib.bridge.android.support.DrawerLayoutUtil; @@ -126,6 +127,9 @@ public final class BridgeInflater extends LayoutInflater { if (view == null) { view = loadCustomView(name, attrs); } + } catch (InflateException e) { + // Don't catch the InflateException below as that results in hiding the real cause. + throw e; } catch (Exception e) { // Wrap the real exception in a ClassNotFoundException, so that the calling method // can deal with it. @@ -154,23 +158,30 @@ public final class BridgeInflater extends LayoutInflater { } ta.recycle(); } - final Object lastContext = mConstructorArgs[0]; - mConstructorArgs[0] = context; - // try to load the class from using the custom view loader - try { - view = loadCustomView(name, attrs); - } catch (Exception e2) { - // Wrap the real exception in an InflateException so that the calling - // method can deal with it. - InflateException exception = new InflateException(); - if (!e2.getClass().equals(ClassNotFoundException.class)) { - exception.initCause(e2); - } else { - exception.initCause(e); + if (!(e.getCause() instanceof ClassNotFoundException)) { + // There is some unknown inflation exception in inflating a View that was found. + view = new MockView(context, attrs); + ((MockView) view).setText(name); + Bridge.getLog().error(LayoutLog.TAG_BROKEN, e.getMessage(), e, null); + } else { + final Object lastContext = mConstructorArgs[0]; + mConstructorArgs[0] = context; + // try to load the class from using the custom view loader + try { + view = loadCustomView(name, attrs); + } catch (Exception e2) { + // Wrap the real exception in an InflateException so that the calling + // method can deal with it. + InflateException exception = new InflateException(); + if (!e2.getClass().equals(ClassNotFoundException.class)) { + exception.initCause(e2); + } else { + exception.initCause(e); + } + throw exception; + } finally { + mConstructorArgs[0] = lastContext; } - throw exception; - } finally { - mConstructorArgs[0] = lastContext; } } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java index 44a9aad55daa..d392f213e5c8 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java @@ -17,39 +17,90 @@ package com.android.layoutlib.bridge; import android.content.Context; -import android.graphics.Canvas; import android.util.AttributeSet; import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; import android.widget.TextView; /** * Base class for mocked views. - * - * TODO: implement onDraw and draw a rectangle in a random color with the name of the class - * (or better the id of the view). + * <p/> + * FrameLayout with a single TextView. Doesn't allow adding any other views to itself. */ -public class MockView extends TextView { +public class MockView extends FrameLayout { + + private final TextView mView; + + public MockView(Context context) { + this(context, null); + } public MockView(Context context, AttributeSet attrs) { this(context, attrs, 0); } - public MockView(Context context, AttributeSet attrs, int defStyle) { - this(context, attrs, defStyle, 0); + public MockView(Context context, AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, 0); } public MockView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); - - setText(this.getClass().getSimpleName()); - setTextColor(0xFF000000); + mView = new TextView(context, attrs); + mView.setTextColor(0xFF000000); setGravity(Gravity.CENTER); + setText(getClass().getSimpleName()); + addView(mView); + setBackgroundColor(0xFF7F7F7F); + } + + // Only allow adding one TextView. + @Override + public void addView(View child) { + if (child == mView) { + super.addView(child); + } + } + + @Override + public void addView(View child, int index) { + if (child == mView) { + super.addView(child, index); + } } @Override - public void onDraw(Canvas canvas) { - canvas.drawARGB(0xFF, 0x7F, 0x7F, 0x7F); + public void addView(View child, int width, int height) { + if (child == mView) { + super.addView(child, width, height); + } + } + + @Override + public void addView(View child, ViewGroup.LayoutParams params) { + if (child == mView) { + super.addView(child, params); + } + } + + @Override + public void addView(View child, int index, ViewGroup.LayoutParams params) { + if (child == mView) { + super.addView(child, index, params); + } + } + + // The following methods are called by the IDE via reflection, and should be considered part + // of the API. + // Historically, MockView used to be a textView and had these methods. Now, we simply delegate + // them to the contained textView. + + public void setText(CharSequence text) { + mView.setText(text); + } - super.onDraw(canvas); + public void setGravity(int gravity) { + mView.setGravity(gravity); } } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java index b2dc29a90fb1..f2d214c43135 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java @@ -409,7 +409,7 @@ public final class BridgeContext extends Context { pushParser(blockParser); return Pair.of( mBridgeInflater.inflate(blockParser, parent, attachToRoot), - true); + Boolean.TRUE); } finally { popParser(); } @@ -447,7 +447,7 @@ public final class BridgeContext extends Context { pushParser(blockParser); return Pair.of( mBridgeInflater.inflate(blockParser, parent, attachToRoot), - false); + Boolean.FALSE); } finally { popParser(); } @@ -470,7 +470,7 @@ public final class BridgeContext extends Context { resource.getName()), null); } - return Pair.of(null, false); + return Pair.of(null, Boolean.FALSE); } @SuppressWarnings("deprecation") diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java index 868c6d328e8e..cdcf0ea1ca76 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java @@ -16,10 +16,13 @@ package com.android.layoutlib.bridge.bars; +import com.android.ide.common.rendering.api.LayoutLog; +import com.android.ide.common.rendering.api.LayoutlibCallback; import com.android.ide.common.rendering.api.RenderResources; import com.android.ide.common.rendering.api.ResourceValue; import com.android.ide.common.rendering.api.SessionParams; import com.android.ide.common.rendering.api.StyleResourceValue; +import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.android.BridgeContext; import com.android.layoutlib.bridge.impl.ResourceHelper; import com.android.resources.ResourceType; @@ -45,6 +48,8 @@ public class AppCompatActionBar extends BridgeActionBar { private Object mWindowDecorActionBar; private static final String WINDOW_ACTION_BAR_CLASS = "android.support.v7.internal.app.WindowDecorActionBar"; + // This is used on v23.1.1 and later. + private static final String WINDOW_ACTION_BAR_CLASS_NEW = "android.support.v7.app.WindowDecorActionBar"; private Class<?> mWindowActionBarClass; /** @@ -70,14 +75,25 @@ public class AppCompatActionBar extends BridgeActionBar { try { Class[] constructorParams = {View.class}; Object[] constructorArgs = {getDecorContent()}; - mWindowDecorActionBar = params.getLayoutlibCallback().loadView(WINDOW_ACTION_BAR_CLASS, - constructorParams, constructorArgs); + LayoutlibCallback callback = params.getLayoutlibCallback(); + + // Check if the old action bar class is present. + String actionBarClass = WINDOW_ACTION_BAR_CLASS; + try { + callback.findClass(actionBarClass); + } catch (ClassNotFoundException expected) { + // Failed to find the old class, use the newer one. + actionBarClass = WINDOW_ACTION_BAR_CLASS_NEW; + } + mWindowDecorActionBar = callback.loadView(actionBarClass, + constructorParams, constructorArgs); mWindowActionBarClass = mWindowDecorActionBar == null ? null : mWindowDecorActionBar.getClass(); setupActionBar(); } catch (Exception e) { - e.printStackTrace(); + Bridge.getLog().warning(LayoutLog.TAG_BROKEN, + "Failed to load AppCompat ActionBar with unknown error.", e); } } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java index 567002e564b7..a6e5fb841bff 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java @@ -33,7 +33,6 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import android.annotation.NonNull; -import android.annotation.Nullable; import android.content.res.ColorStateList; import android.graphics.Bitmap; import android.graphics.Bitmap_Delegate; @@ -117,11 +116,11 @@ abstract class CustomBar extends LinearLayout { density = iconLoader.getDensity(); String path = iconLoader.getPath(); // look for a cached bitmap - Bitmap bitmap = Bridge.getCachedBitmap(path, true /*isFramework*/); + Bitmap bitmap = Bridge.getCachedBitmap(path, Boolean.TRUE /*isFramework*/); if (bitmap == null) { try { bitmap = Bitmap_Delegate.createBitmap(stream, false /*isMutable*/, density); - Bridge.setCachedBitmap(path, bitmap, true /*isFramework*/); + Bridge.setCachedBitmap(path, bitmap, Boolean.TRUE /*isFramework*/); } catch (IOException e) { return; } @@ -228,18 +227,16 @@ abstract class CustomBar extends LinearLayout { * Find the background color for this bar from the theme attributes. Only relevant to StatusBar * and NavigationBar. * <p/> - * Returns null if not found. + * Returns 0 if not found. * * @param colorAttrName the attribute name for the background color * @param translucentAttrName the attribute name for the translucency property of the bar. * * @throws NumberFormatException if color resolved to an invalid string. */ - @Nullable - protected Integer getBarColor(@NonNull String colorAttrName, - @NonNull String translucentAttrName) { + protected int getBarColor(@NonNull String colorAttrName, @NonNull String translucentAttrName) { if (!Config.isGreaterOrEqual(mSimulatedPlatformVersion, LOLLIPOP)) { - return null; + return 0; } RenderResources renderResources = getContext().getRenderResources(); // First check if the bar is translucent. @@ -254,11 +251,10 @@ abstract class CustomBar extends LinearLayout { if (transparent) { return getColor(renderResources, colorAttrName); } - return null; + return 0; } - @Nullable - private static Integer getColor(RenderResources renderResources, String attr) { + private static int getColor(RenderResources renderResources, String attr) { // From ?attr/foo to @color/bar. This is most likely an ItemResourceValue. ResourceValue resource = renderResources.findItemInTheme(attr, true); // Form @color/bar to the #AARRGGBB @@ -279,7 +275,7 @@ abstract class CustomBar extends LinearLayout { } } } - return null; + return 0; } private ResourceValue getResourceValue(String reference) { diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java index d50ce23a0902..9c89bfe2a28f 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java @@ -65,8 +65,8 @@ public class NavigationBar extends CustomBar { super(context, orientation, getShortestWidth(context)>= 600 ? LAYOUT_600DP_XML : LAYOUT_XML, "navigation_bar.xml", simulatedPlatformVersion); - Integer color = getBarColor(ATTR_COLOR, ATTR_TRANSLUCENT); - setBackgroundColor(color == null ? 0xFF000000 : color); + int color = getBarColor(ATTR_COLOR, ATTR_TRANSLUCENT); + setBackgroundColor(color == 0 ? 0xFF000000 : color); // Cannot access the inside items through id because no R.id values have been // created for them. diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java index 95a5a58c535f..2dc7c65e2085 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java @@ -71,9 +71,8 @@ public class StatusBar extends CustomBar { // FIXME: use FILL_H? setGravity(Gravity.START | Gravity.TOP | Gravity.RIGHT); - Integer color = getBarColor(ATTR_COLOR, ATTR_TRANSLUCENT); - setBackgroundColor( - color == null ? Config.getStatusBarColor(simulatedPlatformVersion) : color); + int color = getBarColor(ATTR_COLOR, ATTR_TRANSLUCENT); + setBackgroundColor(color == 0 ? Config.getStatusBarColor(simulatedPlatformVersion) : color); // Cannot access the inside items through id because no R.id values have been // created for them. diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/PorterDuffUtility.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/PorterDuffUtility.java index 95880355ada9..80d7c68bcf06 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/PorterDuffUtility.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/PorterDuffUtility.java @@ -21,6 +21,7 @@ import com.android.layoutlib.bridge.Bridge; import android.graphics.BlendComposite; import android.graphics.BlendComposite.BlendingMode; +import android.graphics.PorterDuff; import android.graphics.PorterDuff.Mode; import android.graphics.PorterDuffColorFilter_Delegate; import android.graphics.PorterDuffXfermode_Delegate; @@ -34,6 +35,8 @@ import java.awt.Composite; */ public final class PorterDuffUtility { + private static final int MODES_COUNT = Mode.values().length; + // Make the class non-instantiable. private PorterDuffUtility() { } @@ -43,12 +46,11 @@ public final class PorterDuffUtility { * {@link Mode#SRC_OVER} for invalid modes. */ public static Mode getPorterDuffMode(int porterDuffMode) { - Mode[] values = Mode.values(); - if (porterDuffMode >= 0 && porterDuffMode < values.length) { - return values[porterDuffMode]; + if (porterDuffMode >= 0 && porterDuffMode < MODES_COUNT) { + return PorterDuff.intToMode(porterDuffMode); } Bridge.getLog().error(LayoutLog.TAG_BROKEN, - String.format("Unknown PorterDuff.Mode: %1$d", porterDuffMode), null /*data*/); + String.format("Unknown PorterDuff.Mode: %1$d", porterDuffMode), null); assert false; return Mode.SRC_OVER; } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java index 2a4f58381aee..0ffa35733180 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java @@ -421,7 +421,8 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { gc.setComposite(AlphaComposite.Src); gc.setColor(new Color(0x00000000, true)); - gc.fillRect(0, 0, mMeasuredScreenWidth, mMeasuredScreenHeight); + gc.fillRect(0, 0, + mMeasuredScreenWidth, mMeasuredScreenHeight); // done gc.dispose(); diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java index ae4a57d8eea4..7ef75662aad4 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java @@ -17,6 +17,7 @@ package com.android.tools.layoutlib.create; import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; @@ -40,6 +41,7 @@ public class DelegateClassAdapter extends ClassVisitor { private final String mClassName; private final Set<String> mDelegateMethods; private final Log mLog; + private boolean mIsStaticInnerClass; /** * Creates a new {@link DelegateClassAdapter} that can transform some methods @@ -62,16 +64,30 @@ public class DelegateClassAdapter extends ClassVisitor { mLog = log; mClassName = className; mDelegateMethods = delegateMethods; + // If this is an inner class, by default, we assume it's static. If it's not we will detect + // by looking at the fields (see visitField) + mIsStaticInnerClass = className.contains("$"); } //---------------------------------- // Methods from the ClassAdapter @Override + public FieldVisitor visitField(int access, String name, String desc, String signature, + Object value) { + if (mIsStaticInnerClass && "this$0".equals(name)) { + // Having a "this$0" field, proves that this class is not a static inner class. + mIsStaticInnerClass = false; + } + + return super.visitField(access, name, desc, signature, value); + } + + @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { - boolean isStatic = (access & Opcodes.ACC_STATIC) != 0; + boolean isStaticMethod = (access & Opcodes.ACC_STATIC) != 0; boolean isNative = (access & Opcodes.ACC_NATIVE) != 0; boolean useDelegate = (isNative && mDelegateMethods.contains(ALL_NATIVES)) || @@ -96,7 +112,8 @@ public class DelegateClassAdapter extends ClassVisitor { MethodVisitor mwDelegate = super.visitMethod(access, name, desc, signature, exceptions); DelegateMethodAdapter a = new DelegateMethodAdapter( - mLog, null, mwDelegate, mClassName, name, desc, isStatic); + mLog, null, mwDelegate, mClassName, name, desc, isStaticMethod, + mIsStaticInnerClass); // A native has no code to visit, so we need to generate it directly. a.generateDelegateCode(); @@ -120,6 +137,7 @@ public class DelegateClassAdapter extends ClassVisitor { desc, signature, exceptions); return new DelegateMethodAdapter( - mLog, mwOriginal, mwDelegate, mClassName, name, desc, isStatic); + mLog, mwOriginal, mwDelegate, mClassName, name, desc, isStaticMethod, + mIsStaticInnerClass); } } diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java index 12690db547a9..cca9e574b7ea 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java @@ -85,6 +85,8 @@ class DelegateMethodAdapter extends MethodVisitor { private String mDesc; /** True if the original method is static. */ private final boolean mIsStatic; + /** True if the method is contained in a static inner class */ + private final boolean mIsStaticInnerClass; /** The internal class name (e.g. <code>com/android/SomeClass$InnerClass</code>.) */ private final String mClassName; /** The method name. */ @@ -120,7 +122,8 @@ class DelegateMethodAdapter extends MethodVisitor { String className, String methodName, String desc, - boolean isStatic) { + boolean isStatic, + boolean isStaticClass) { super(Opcodes.ASM4); mLog = log; mOrgWriter = mvOriginal; @@ -129,6 +132,7 @@ class DelegateMethodAdapter extends MethodVisitor { mMethodName = methodName; mDesc = desc; mIsStatic = isStatic; + mIsStaticInnerClass = isStaticClass; } /** @@ -206,7 +210,7 @@ class DelegateMethodAdapter extends MethodVisitor { // by the 'this' of any outer class, if any. if (!mIsStatic) { - if (outerType != null) { + if (outerType != null && !mIsStaticInnerClass) { // The first-level inner class has a package-protected member called 'this$0' // that points to the outer class. diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java index 648cea430de2..e37a09b348b8 100644 --- a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java +++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java @@ -27,6 +27,7 @@ import static org.junit.Assert.fail; import com.android.tools.layoutlib.create.dataclass.ClassWithNative; import com.android.tools.layoutlib.create.dataclass.OuterClass; import com.android.tools.layoutlib.create.dataclass.OuterClass.InnerClass; +import com.android.tools.layoutlib.create.dataclass.OuterClass.StaticInnerClass; import org.junit.Before; import org.junit.Test; @@ -56,6 +57,8 @@ public class DelegateClassAdapterTest { private static final String OUTER_CLASS_NAME = OuterClass.class.getCanonicalName(); private static final String INNER_CLASS_NAME = OuterClass.class.getCanonicalName() + "$" + InnerClass.class.getSimpleName(); + private static final String STATIC_INNER_CLASS_NAME = + OuterClass.class.getCanonicalName() + "$" + StaticInnerClass.class.getSimpleName(); @Before public void setUp() throws Exception { @@ -294,6 +297,61 @@ public class DelegateClassAdapterTest { } } + @Test + public void testDelegateStaticInner() throws Throwable { + // We'll delegate the "get" method of both the inner and outer class. + HashSet<String> delegateMethods = new HashSet<String>(); + delegateMethods.add("get"); + + // Generate the delegate for the outer class. + ClassWriter cwOuter = new ClassWriter(0 /*flags*/); + String outerClassName = OUTER_CLASS_NAME.replace('.', '/'); + DelegateClassAdapter cvOuter = new DelegateClassAdapter( + mLog, cwOuter, outerClassName, delegateMethods); + ClassReader cr = new ClassReader(OUTER_CLASS_NAME); + cr.accept(cvOuter, 0 /* flags */); + + // Generate the delegate for the static inner class. + ClassWriter cwInner = new ClassWriter(0 /*flags*/); + String innerClassName = STATIC_INNER_CLASS_NAME.replace('.', '/'); + DelegateClassAdapter cvInner = new DelegateClassAdapter( + mLog, cwInner, innerClassName, delegateMethods); + cr = new ClassReader(STATIC_INNER_CLASS_NAME); + cr.accept(cvInner, 0 /* flags */); + + // Load the generated classes in a different class loader and try them + ClassLoader2 cl2 = null; + try { + cl2 = new ClassLoader2() { + @Override + public void testModifiedInstance() throws Exception { + + // Check the outer class + Class<?> outerClazz2 = loadClass(OUTER_CLASS_NAME); + Object o2 = outerClazz2.newInstance(); + assertNotNull(o2); + + // Check the inner class. Since it's not a static inner class, we need + // to use the hidden constructor that takes the outer class as first parameter. + Class<?> innerClazz2 = loadClass(STATIC_INNER_CLASS_NAME); + Constructor<?> innerCons = innerClazz2.getConstructor(); + Object i2 = innerCons.newInstance(); + assertNotNull(i2); + + // The original StaticInner.get returns 100+10+20, + // but the delegate makes it return 6+10+20 + assertEquals(6+10+20, callGet(i2, 10, 20)); + assertEquals(100+10+20, callGet_Original(i2, 10, 20)); + } + }; + cl2.add(OUTER_CLASS_NAME, cwOuter.toByteArray()); + cl2.add(STATIC_INNER_CLASS_NAME, cwInner.toByteArray()); + cl2.testModifiedInstance(); + } catch (Throwable t) { + throw dumpGeneratedClass(t, cl2); + } + } + //------- /** diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass.java index f083e76d995c..6dfb81662e40 100644 --- a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass.java +++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass.java @@ -45,6 +45,16 @@ public class OuterClass { } } + public static class StaticInnerClass { + public StaticInnerClass() { + } + + // StaticInnerClass.get returns 100 + a + b + public int get(int a, long b) { + return 100 + a + (int) b; + } + } + @SuppressWarnings("unused") private String privateMethod() { return "outerPrivateMethod"; diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass_StaticInnerClass_Delegate.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass_StaticInnerClass_Delegate.java new file mode 100644 index 000000000000..a29439ee3fee --- /dev/null +++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass_StaticInnerClass_Delegate.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.tools.layoutlib.create.dataclass; + +import com.android.tools.layoutlib.create.DelegateClassAdapterTest; +import com.android.tools.layoutlib.create.dataclass.OuterClass.StaticInnerClass; + +/** + * Used by {@link DelegateClassAdapterTest}. + */ +public class OuterClass_StaticInnerClass_Delegate { + // The delegate override of Inner.get return 6 + a + b + public static int get(StaticInnerClass inner, int a, long b) { + return 6 + a + (int) b; + } +} |