+ /* The following blocks handle printing "inferred" uses-features, based
+ * on whether related features or permissions are used by the app.
+ * Note that the various spec*Feature variables denote whether the
+ * relevant tag was *present* in the AndroidManfest, not that it was
+ * present and set to true.
+ */
+ // Camera-related back-compatibility logic
+ if (!specCameraFeature) {
+ if (reqCameraFlashFeature || reqCameraAutofocusFeature) {
+ // if app requested a sub-feature (autofocus or flash) and didn't
+ // request the base camera feature, we infer that it meant to
+ printf("uses-feature:'android.hardware.camera'\n");
+ } else if (hasCameraPermission) {
+ // if app wants to use camera but didn't request the feature, we infer
+ // that it meant to, and further that it wants autofocus
+ // (which was the 1.0 - 1.5 behavior)
+ printf("uses-feature:'android.hardware.camera'\n");
+ if (!specCameraAutofocusFeature) {
+ printf("uses-feature:'android.hardware.camera.autofocus'\n");
+ }
+ }
+ }
+
+ // Location-related back-compatibility logic
+ if (!specLocationFeature &&
+ (hasMockLocPermission || hasCoarseLocPermission || hasGpsPermission ||
+ hasGeneralLocPermission || reqNetworkLocFeature || reqGpsFeature)) {
+ // if app either takes a location-related permission or requests one of the
+ // sub-features, we infer that it also meant to request the base location feature
+ printf("uses-feature:'android.hardware.location'\n");
+ }
+ if (!specGpsFeature && hasGpsPermission) {
+ // if app takes GPS (FINE location) perm but does not request the GPS
+ // feature, we infer that it meant to
+ printf("uses-feature:'android.hardware.location.gps'\n");
+ }
+ if (!specNetworkLocFeature && hasCoarseLocPermission) {
+ // if app takes Network location (COARSE location) perm but does not request the
+ // network location feature, we infer that it meant to
+ printf("uses-feature:'android.hardware.location.network'\n");
+ }
+
+ // Bluetooth-related compatibility logic
+ if (!specBluetoothFeature && hasBluetoothPermission && (targetSdk > 4)) {
+ // if app takes a Bluetooth permission but does not request the Bluetooth
+ // feature, we infer that it meant to
+ printf("uses-feature:'android.hardware.bluetooth'\n");
+ }
+
+ // Microphone-related compatibility logic
+ if (!specMicrophoneFeature && hasRecordAudioPermission) {
+ // if app takes the record-audio permission but does not request the microphone
+ // feature, we infer that it meant to
+ printf("uses-feature:'android.hardware.microphone'\n");
+ }
+
+ // WiFi-related compatibility logic
+ if (!specWiFiFeature && hasWiFiPermission) {
+ // if app takes one of the WiFi permissions but does not request the WiFi
+ // feature, we infer that it meant to
+ printf("uses-feature:'android.hardware.wifi'\n");
+ }
+
+ // Telephony-related compatibility logic
+ if (!specTelephonyFeature && (hasTelephonyPermission || reqTelephonySubFeature)) {
+ // if app takes one of the telephony permissions or requests a sub-feature but
+ // does not request the base telephony feature, we infer that it meant to
+ printf("uses-feature:'android.hardware.telephony'\n");
+ }
+
+ // Touchscreen-related back-compatibility logic
+ if (!specTouchscreenFeature) { // not a typo!
+ // all apps are presumed to require a touchscreen, unless they explicitly say
+ // <uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
+ // Note that specTouchscreenFeature is true if the tag is present, regardless
+ // of whether its value is true or false, so this is safe
+ printf("uses-feature:'android.hardware.touchscreen'\n");
+ }
+ if (!specMultitouchFeature && reqDistinctMultitouchFeature) {
+ // if app takes one of the telephony permissions or requests a sub-feature but
+ // does not request the base telephony feature, we infer that it meant to
+ printf("uses-feature:'android.hardware.touchscreen.multitouch'\n");