devtools/madb: handle density split filters for madb install.

Screen density filters are handled similar to how abi filters work,
and the .apk files that are incompatible with the given device are
excluded when finding the best matching .apk file.

Change-Id: I14b2760327f2c323d94b4b0e3877ff7a08e64ef9
diff --git a/madb/install.go b/madb/install.go
index b9cfc11..0e1e435 100644
--- a/madb/install.go
+++ b/madb/install.go
@@ -6,6 +6,8 @@
 
 import (
 	"fmt"
+	"regexp"
+	"strconv"
 	"strings"
 
 	"v.io/x/lib/cmdline"
@@ -54,13 +56,18 @@
 	sh.ContinueOnError = true
 	if isGradleProject(wd) {
 		// Get the necessary device properties.
-		// TODO(youngseokyoon): get the device density.
 		deviceAbis, err := getSupportedAbisForDevice(d)
 		if err != nil {
 			return err
 		}
 
-		bestOutput := computeBestOutput(properties.VariantOutputs, properties.AbiFilters, 0, deviceAbis)
+		deviceDensity, err := getScreenDensityForDevice(d)
+		if err != nil {
+			return err
+		}
+
+		// Compute the best output based on the device properties and the .apk filters.
+		bestOutput := computeBestOutput(properties.VariantOutputs, properties.AbiFilters, deviceDensity, deviceAbis)
 		if bestOutput == nil {
 			return fmt.Errorf("Could not find the matching .apk for device %q", d.displayName())
 		}
@@ -104,7 +111,8 @@
 	return parseSupportedAbis(output)
 }
 
-// parseSupportedAbis takes the output of "adb shell am get-config" command, and extracts the supported abis.
+// parseSupportedAbis takes the output of "adb shell am get-config" command, and extracts the
+// supported abis.
 func parseSupportedAbis(output string) ([]string, error) {
 	prefix := "abi: "
 	lines := strings.Split(output, "\n")
@@ -118,10 +126,71 @@
 	return nil, fmt.Errorf("Could not extract the abi list from the device configuration output.")
 }
 
-// computeBestOutput returns the pointer of the best matching output among the multiple variant outputs,
-// given the device density and the abis supported by the device.
-// The logic of this function is similar to that of SplitOutputMatcher.java in the Android platform tools.
+// getScreenDensityForDevice returns the numeric screen dpi value of the given device.
+func getScreenDensityForDevice(d device) (int, error) {
+	sh := gosh.NewShell(nil)
+	defer sh.Cleanup()
+
+	sh.ContinueOnError = true
+
+	cmd := sh.Cmd("adb", "-s", d.Serial, "shell", "getprop")
+	output := cmd.Stdout()
+
+	if sh.Err != nil {
+		return 0, sh.Err
+	}
+
+	return parseScreenDensity(output)
+}
+
+// parseScreenDensity takes the output of "adb shell getprop" command, and extracts the screen
+// density value.
+func parseScreenDensity(output string) (int, error) {
+	// Each line in the output has the following format: "[<property_key>]: [<property_value>]"
+	// The property key for the screen density is "ro.sf.lcd_density".
+	// Look for the pattern "[ro.sf.lcd_density]: [(<value>)]" with regexp, with the value part
+	// as a subexpression.
+	key := "ro.sf.lcd_density"
+	pattern := fmt.Sprintf(`\[%v\]: \[(.*)\]`, key)
+	exp := regexp.MustCompile(pattern)
+
+	matches := exp.FindStringSubmatch(output)
+	// matches[1] is the first subexpression, which contains the density value we need.
+	if matches != nil && len(matches) == 2 {
+		return strconv.Atoi(matches[1])
+	}
+
+	return 0, fmt.Errorf("Could not extract the screen density from the device properties output.")
+}
+
+// getDensityResourceName converts the given numeric density value into a resource name such as
+// "ldpi", "mdpi", etc.
+func getDensityResourceName(density int) string {
+	// Predefined density names.
+	densityMap := map[int]string{
+		0:   "anydpi",
+		120: "ldpi",
+		160: "mdpi",
+		213: "tvdpi",
+		240: "hdpi",
+		320: "xhdpi",
+		480: "xxhdpi",
+		640: "xxxhdpi",
+	}
+
+	if name, ok := densityMap[density]; ok {
+		return name
+	}
+
+	// Otherwise, return density + "dpi". (e.g., 280 -> "280dpi")
+	return fmt.Sprintf("%vdpi", density)
+}
+
+// computeBestOutput returns the pointer of the best matching output among the multiple variant
+// outputs, given the device density and the abis supported by the device. The logic of this
+// function is similar to that of SplitOutputMatcher.java in the Android platform tools.
 func computeBestOutput(variantOutputs []variantOutput, variantAbiFilters []string, deviceDensity int, deviceAbis []string) *variantOutput {
+	densityName := getDensityResourceName(deviceDensity)
 	matches := map[*variantOutput]bool{}
 
 VariantOutputLoop:
@@ -130,7 +199,6 @@
 	FilterLoop:
 		for _, filter := range vo.Filters {
 
-			// TODO(youngseokyoon): check the density filter too.
 			switch filter.FilterType {
 			case "ABI":
 				for _, supportedAbi := range deviceAbis {
@@ -144,6 +212,11 @@
 				// this variant output is not compatible with the device.
 				// Check the next variant output.
 				continue VariantOutputLoop
+
+			case "DENSITY":
+				if filter.Identifier != densityName {
+					continue VariantOutputLoop
+				}
 			}
 		}
 
diff --git a/madb/install_test.go b/madb/install_test.go
index 32f11bc..4691d2d 100644
--- a/madb/install_test.go
+++ b/madb/install_test.go
@@ -5,6 +5,8 @@
 package main
 
 import (
+	"io/ioutil"
+	"os"
 	"path/filepath"
 	"reflect"
 	"testing"
@@ -34,7 +36,38 @@
 	}
 }
 
-// TODO(youngseokyoon): add tests for the density splits too.
+func TestParseScreenDensity(t *testing.T) {
+	tests := []struct {
+		outputFile string
+		want       int
+	}{
+		{filepath.Join("testdata", "getprop1.txt"), 420},
+		{filepath.Join("testdata", "getprop2.txt"), 320},
+	}
+
+	for i, test := range tests {
+		f, err := os.Open(test.outputFile)
+		if err != nil {
+			t.Fatal(err)
+		}
+		defer f.Close()
+
+		bytes, err := ioutil.ReadAll(f)
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		got, err := parseScreenDensity(string(bytes))
+		if err != nil {
+			t.Fatal(err)
+		}
+		if got != test.want {
+			t.Fatalf("unmatched results for tests[%v]: got %v, want %v", i, got, test.want)
+		}
+	}
+}
+
+// TODO(youngseokyoon): restructure the Gradle test projects so that they share the binary data.
 func TestComputeBestOutputForAbiSplits(t *testing.T) {
 	// Read the variant properties from the "testAndroidAbiSplit" project.
 	key := variantKey{
@@ -49,15 +82,16 @@
 	}
 
 	// Make sure that it has three outputs for each Abi.
-	if len(props.VariantOutputs) != 3 {
-		t.Fatalf("The number of extracted variant outputs does not match the expected value.")
+	if got, want := len(props.VariantOutputs), 3; got != want {
+		t.Fatalf("unexpected variant output length: got %v, want %v", got, want)
 	}
 
 	outputX86 := &props.VariantOutputs[0]
 	outputArmv7a := &props.VariantOutputs[1]
 	outputMips := &props.VariantOutputs[2]
 
-	// Now create fake device properties and test whether the computeBestOutput returns the correct variantOutput.
+	// Now create fake device properties and test whether the computeBestOutput returns the correct
+	// variantOutput.
 	deviceDensity := 240
 	tests := []struct {
 		deviceAbis []string
@@ -76,3 +110,44 @@
 		}
 	}
 }
+
+func TestComputeBestOutputForDensitySplits(t *testing.T) {
+	// Read the variant properties from the "testAndroidDensitySplit" project.
+	key := variantKey{
+		Dir:     filepath.Join("testdata", "projects", "testAndroidDensitySplit"),
+		Module:  "app",
+		Variant: "Debug",
+	}
+
+	props, err := extractPropertiesFromGradle(key)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	// Make sure that it has three outputs for each density.
+	if got, want := len(props.VariantOutputs), 3; got != want {
+		t.Fatalf("unexpected variant output length: got %v, want %v", got, want)
+	}
+
+	outputUniversal := &props.VariantOutputs[0]
+	outputLdpi := &props.VariantOutputs[1]
+	outputMdpi := &props.VariantOutputs[2]
+
+	// Now create fake device properties and test whether the computeBestOutput returns the correct
+	// variantOutput.
+	deviceAbis := []string{"armeabi-v7a"}
+	tests := []struct {
+		deviceDensity int
+		want          *variantOutput
+	}{
+		{120, outputLdpi},
+		{160, outputMdpi},
+		{420, outputUniversal},
+	}
+
+	for i, test := range tests {
+		if got := computeBestOutput(props.VariantOutputs, props.AbiFilters, test.deviceDensity, deviceAbis); got != test.want {
+			t.Fatalf("unmatched results for tests[%v]: got %v, want %v", i, got, test.want)
+		}
+	}
+}
diff --git a/madb/madb_test.go b/madb/madb_test.go
index 34434b1..9a10723 100644
--- a/madb/madb_test.go
+++ b/madb/madb_test.go
@@ -269,7 +269,7 @@
 	}
 }
 
-func TestExtractIdsFromGradle(t *testing.T) {
+func TestExtractPropertiesFromGradle(t *testing.T) {
 	tests := []struct {
 		key  variantKey
 		want variantProperties
diff --git a/madb/testdata/getprop1.txt b/madb/testdata/getprop1.txt
new file mode 100644
index 0000000..5ff9fd4
--- /dev/null
+++ b/madb/testdata/getprop1.txt
@@ -0,0 +1,313 @@
+[af.fast_track_multiplier]: [1]

+[audio_hal.period_size]: [192]

+[bluetooth.enable_timeout_ms]: [12000]

+[dalvik.vm.boot-dex2oat-threads]: [4]

+[dalvik.vm.dex2oat-Xms]: [64m]

+[dalvik.vm.dex2oat-Xmx]: [512m]

+[dalvik.vm.dex2oat-threads]: [2]

+[dalvik.vm.heapgrowthlimit]: [192m]

+[dalvik.vm.heapmaxfree]: [8m]

+[dalvik.vm.heapminfree]: [512k]

+[dalvik.vm.heapsize]: [512m]

+[dalvik.vm.heapstartsize]: [8m]

+[dalvik.vm.heaptargetutilization]: [0.75]

+[dalvik.vm.image-dex2oat-Xms]: [64m]

+[dalvik.vm.image-dex2oat-Xmx]: [64m]

+[dalvik.vm.image-dex2oat-threads]: [4]

+[dalvik.vm.isa.arm.features]: [default]

+[dalvik.vm.isa.arm.variant]: [cortex-a7]

+[dalvik.vm.isa.arm64.features]: [default]

+[dalvik.vm.isa.arm64.variant]: [cortex-a53]

+[dalvik.vm.stack-trace-file]: [/data/anr/traces.txt]

+[debug.atrace.tags.enableflags]: [0]

+[debug.force_rtl]: [0]

+[dev.bootcomplete]: [1]

+[drm.service.enabled]: [true]

+[gsm.current.phone-type]: [1]

+[gsm.defaultpdpcontext.active]: [false]

+[gsm.network.type]: [LTE]

+[gsm.nitz.time]: [1459268245004]

+[gsm.operator.alpha]: [T-Mobile]

+[gsm.operator.iso-country]: [us]

+[gsm.operator.isroaming]: [false]

+[gsm.operator.numeric]: [310260]

+[gsm.sim.operator.alpha]: [T-Mobile]

+[gsm.sim.operator.iso-country]: [us]

+[gsm.sim.operator.numeric]: [310260]

+[gsm.sim.state]: [READY]

+[gsm.version.baseband]: [M8994F-2.6.31.1.09]

+[gsm.version.ril-impl]: [Qualcomm RIL 1.0]

+[init.svc.adbd]: [running]

+[init.svc.atfwd]: [running]

+[init.svc.bootanim]: [stopped]

+[init.svc.bugreport]: [stopped]

+[init.svc.bullhead-sh]: [stopped]

+[init.svc.cnd]: [running]

+[init.svc.cnss-daemon]: [running]

+[init.svc.debuggerd]: [running]

+[init.svc.debuggerd64]: [running]

+[init.svc.defaultcrypto]: [stopped]

+[init.svc.drm]: [running]

+[init.svc.dumpstate]: [stopped]

+[init.svc.fingerprintd]: [running]

+[init.svc.flash-sh-fw]: [stopped]

+[init.svc.flash_recovery]: [stopped]

+[init.svc.gatekeeperd]: [running]

+[init.svc.healthd]: [running]

+[init.svc.imscmservice]: [running]

+[init.svc.imsdatadaemon]: [running]

+[init.svc.imsqmidaemon]: [running]

+[init.svc.installd]: [running]

+[init.svc.irsc_util]: [stopped]

+[init.svc.keystore]: [running]

+[init.svc.lmkd]: [running]

+[init.svc.loc_launcher]: [running]

+[init.svc.logd]: [running]

+[init.svc.logd-reinit]: [stopped]

+[init.svc.mdnsd]: [stopped]

+[init.svc.media]: [running]

+[init.svc.msm_irqbalance]: [running]

+[init.svc.mtpd]: [stopped]

+[init.svc.netd]: [running]

+[init.svc.netmgrd]: [running]

+[init.svc.p2p_supplicant]: [running]

+[init.svc.per_mgr]: [running]

+[init.svc.per_proxy]: [running]

+[init.svc.perfd]: [running]

+[init.svc.pre-recovery]: [stopped]

+[init.svc.qcamerasvr]: [running]

+[init.svc.qmuxd]: [running]

+[init.svc.qseecomd]: [running]

+[init.svc.qti]: [running]

+[init.svc.racoon]: [stopped]

+[init.svc.ril-daemon]: [running]

+[init.svc.rmt_storage]: [running]

+[init.svc.servicemanager]: [running]

+[init.svc.ss_ramdump]: [stopped]

+[init.svc.start_hci_filter]: [running]

+[init.svc.surfaceflinger]: [running]

+[init.svc.thermal-engine]: [running]

+[init.svc.time_daemon]: [running]

+[init.svc.ueventd]: [running]

+[init.svc.uncrypt]: [stopped]

+[init.svc.vold]: [running]

+[init.svc.wpa_supplicant]: [stopped]

+[init.svc.zygote]: [running]

+[init.svc.zygote_secondary]: [running]

+[keyguard.no_require_sim]: [true]

+[media.aac_51_output_enabled]: [true]

+[net.bt.name]: [Android]

+[net.change]: [net.rmnet_data0.gw]

+[net.dns1]: [8.8.8.8]

+[net.dns2]: [8.8.4.4]

+[net.dns3]: []

+[net.dns4]: []

+[net.hostname]: [android-2ecef02aa8711242]

+[net.qtaguid_enabled]: [1]

+[net.r_rmnet_data0.dns1]: []

+[net.r_rmnet_data0.dns2]: []

+[net.r_rmnet_data0.gw]: []

+[net.rmnet_data0.dns1]: []

+[net.rmnet_data0.dns2]: []

+[net.rmnet_data0.gw]: []

+[net.rmnet_data7.dns1]: [fd00:976a:0:0:0:0:0:9]

+[net.rmnet_data7.dns2]: [fd00:976a:0:0:0:0:0:10]

+[net.rmnet_data7.gw]: [2607:fb90:270a:955c:c84a:2cef:950d:59e8]

+[net.tcp.default_init_rwnd]: [60]

+[partition.system.verified]: [2]

+[partition.vendor.verified]: [2]

+[persist.audio.fluence.speaker]: [true]

+[persist.audio.fluence.voicecall]: [true]

+[persist.audio.fluence.voicecomm]: [true]

+[persist.audio.fluence.voicerec]: [false]

+[persist.camera.tnr.preview]: [0]

+[persist.camera.tnr.video]: [0]

+[persist.data.iwlan.enable]: [true]

+[persist.hwc.mdpcomp.enable]: [true]

+[persist.qcril.disable_retry]: [true]

+[persist.radio.adb_log_on]: [0]

+[persist.radio.always_send_plmn]: [true]

+[persist.radio.apm_sim_not_pwdn]: [1]

+[persist.radio.custom_ecc]: [1]

+[persist.radio.data_con_rprt]: [true]

+[persist.radio.data_no_toggle]: [1]

+[persist.radio.eons.enabled]: [false]

+[persist.radio.eri64_as_home]: [1]

+[persist.radio.mode_pref_nv10]: [1]

+[persist.radio.nitz_lons_0_0]: [olleh]

+[persist.radio.nitz_lons_1_0]: []

+[persist.radio.nitz_lons_2_0]: []

+[persist.radio.nitz_lons_3_0]: []

+[persist.radio.nitz_plmn_0]: [450 08]

+[persist.radio.nitz_sons_0_0]: [olleh]

+[persist.radio.nitz_sons_1_0]: []

+[persist.radio.nitz_sons_2_0]: []

+[persist.radio.nitz_sons_3_0]: []

+[persist.radio.process_sups_ind]: [1]

+[persist.radio.redir_party_num]: [0]

+[persist.radio.ril_payload_on]: [0]

+[persist.radio.snapshot_enabled]: [1]

+[persist.radio.snapshot_timer]: [10]

+[persist.radio.use_cc_names]: [true]

+[persist.speaker.prot.enable]: [true]

+[persist.sys.dalvik.vm.lib.2]: [libart.so]

+[persist.sys.locale]: [en-US]

+[persist.sys.profiler_ms]: [0]

+[persist.sys.timezone]: [America/Los_Angeles]

+[persist.sys.usb.config]: [mtp,adb]

+[persist.sys.webview.vmsize]: [107673088]

+[qcom.bluetooth.soc]: [rome]

+[radio.atfwd.start]: [false]

+[ril.ecclist]: [911,112,*911,#911]

+[ril.nosim.ecc_list_1]: [111,113,117,122,125]

+[ril.nosim.ecc_list_count]: [1]

+[ril.qcril_pre_init_lock_held]: [0]

+[rild.libpath]: [/vendor/lib64/libril-qc-qmi-1.so]

+[ro.adb.secure]: [1]

+[ro.allow.mock.location]: [0]

+[ro.audio.flinger_standbytime_ms]: [300]

+[ro.baseband]: [msm]

+[ro.board.platform]: [msm8992]

+[ro.boot.authorized_kernel]: [true]

+[ro.boot.baseband]: [msm]

+[ro.boot.bootloader]: [BHZ10m]

+[ro.boot.bootreason]: [reboot]

+[ro.boot.dlcomplete]: [0]

+[ro.boot.emmc]: [true]

+[ro.boot.hardware]: [bullhead]

+[ro.boot.hardware.sku]: [LGH790]

+[ro.boot.revision]: [rev_1.0]

+[ro.boot.serialno]: [01023f5e2fd2accf]

+[ro.boot.verifiedbootstate]: [green]

+[ro.boot.veritymode]: [enforcing]

+[ro.boot.wificountrycode]: [US]

+[ro.bootimage.build.date]: [Thu Mar 3 01:08:29 UTC 2016]

+[ro.bootimage.build.date.utc]: [1456967309]

+[ro.bootimage.build.fingerprint]: [google/bullhead/bullhead:6.0.1/MHC19M/2652519:user/release-keys]

+[ro.bootloader]: [BHZ10m]

+[ro.bootmode]: [unknown]

+[ro.bt.bdaddr_path]: [/persist/bdaddr.txt]

+[ro.build.characteristics]: [nosdcard]

+[ro.build.date]: [Thu Mar  3 01:04:00 UTC 2016]

+[ro.build.date.utc]: [1456967040]

+[ro.build.description]: [bullhead-user 6.0.1 MHC19M 2652519 release-keys]

+[ro.build.display.id]: [MHC19M]

+[ro.build.expect.baseband]: [M8994F-2.6.31.1.09]

+[ro.build.expect.bootloader]: [BHZ10m]

+[ro.build.fingerprint]: [google/bullhead/bullhead:6.0.1/MHC19M/2652519:user/release-keys]

+[ro.build.flavor]: [bullhead-user]

+[ro.build.host]: [wpdv4.hot.corp.google.com]

+[ro.build.id]: [MHC19M]

+[ro.build.product]: [bullhead]

+[ro.build.tags]: [release-keys]

+[ro.build.type]: [user]

+[ro.build.user]: [android-build]

+[ro.build.version.all_codenames]: [REL]

+[ro.build.version.base_os]: []

+[ro.build.version.codename]: [REL]

+[ro.build.version.incremental]: [2652519]

+[ro.build.version.preview_sdk]: [0]

+[ro.build.version.release]: [6.0.1]

+[ro.build.version.sdk]: [23]

+[ro.build.version.security_patch]: [2016-04-01]

+[ro.camera.notify_nfc]: [1]

+[ro.carrier]: [unknown]

+[ro.com.android.dataroaming]: [false]

+[ro.com.android.wifi-watchlist]: [GoogleGuest]

+[ro.com.google.clientidbase]: [android-google]

+[ro.config.alarm_alert]: [Oxygen.ogg]

+[ro.config.notification_sound]: [Tethys.ogg]

+[ro.config.ringtone]: [Titania.ogg]

+[ro.config.vc_call_vol_steps]: [7]

+[ro.crypto.fs_crypto_blkdev]: [/dev/block/dm-2]

+[ro.crypto.state]: [encrypted]

+[ro.crypto.type]: [block]

+[ro.dalvik.vm.native.bridge]: [0]

+[ro.debuggable]: [0]

+[ro.error.receiver.system.apps]: [com.google.android.gms]

+[ro.expect.recovery_id]: [0x1c7c687f6c64667a58ab0a10b767eba752b6a8f3000000000000000000000000]

+[ro.facelock.black_timeout]: [400]

+[ro.facelock.det_timeout]: [1500]

+[ro.facelock.est_max_time]: [600]

+[ro.facelock.lively_timeout]: [2500]

+[ro.facelock.rec_timeout]: [2500]

+[ro.facelock.use_intro_anim]: [false]

+[ro.frp.pst]: [/dev/block/platform/soc.0/f9824900.sdhci/by-name/persistent]

+[ro.hardware]: [bullhead]

+[ro.hwui.drop_shadow_cache_size]: [6]

+[ro.hwui.gradient_cache_size]: [1]

+[ro.hwui.layer_cache_size]: [32]

+[ro.hwui.path_cache_size]: [16]

+[ro.hwui.r_buffer_cache_size]: [8]

+[ro.hwui.text_large_cache_height]: [1024]

+[ro.hwui.text_large_cache_width]: [2048]

+[ro.hwui.text_small_cache_height]: [1024]

+[ro.hwui.text_small_cache_width]: [1024]

+[ro.hwui.texture_cache_flushrate]: [0.4]

+[ro.hwui.texture_cache_size]: [56]

+[ro.min_freq_0]: [384000]

+[ro.opengles.version]: [196609]

+[ro.product.board]: [bullhead]

+[ro.product.brand]: [google]

+[ro.product.cpu.abi]: [arm64-v8a]

+[ro.product.cpu.abilist]: [arm64-v8a,armeabi-v7a,armeabi]

+[ro.product.cpu.abilist32]: [armeabi-v7a,armeabi]

+[ro.product.cpu.abilist64]: [arm64-v8a]

+[ro.product.device]: [bullhead]

+[ro.product.locale]: [en-US]

+[ro.product.manufacturer]: [LGE]

+[ro.product.model]: [Nexus 5X]

+[ro.product.name]: [bullhead]

+[ro.qc.sdk.audio.fluencetype]: [fluencepro]

+[ro.recovery_id]: [0x283acaaac18a21a31a027ca681afa5563a380d2c000000000000000000000000]

+[ro.revision]: [rev_1.0]

+[ro.ril.svdo]: [false]

+[ro.ril.svlte1x]: [false]

+[ro.runtime.firstboot]: [1458929596733]

+[ro.secure]: [1]

+[ro.serialno]: [01023f5e2fd2accf]

+[ro.setupwizard.enterprise_mode]: [1]

+[ro.sf.lcd_density]: [420]

+[ro.telephony.call_ring.multiple]: [0]

+[ro.telephony.default_cdma_sub]: [0]

+[ro.telephony.default_network]: [22]

+[ro.url.legal]: [http://www.google.com/intl/%s/mobile/android/basic/phone-legal.html]

+[ro.url.legal.android_privacy]: [http://www.google.com/intl/%s/mobile/android/basic/privacy.html]

+[ro.vendor.build.date]: [Thu Mar 3 01:32:28 UTC 2016]

+[ro.vendor.build.date.utc]: [1456968748]

+[ro.vendor.build.fingerprint]: [google/bullhead/bullhead:6.0.1/MHC19M/2652519:user/release-keys]

+[ro.vendor.extension_library]: [libqti-perfd-client.so]

+[ro.wifi.channels]: []

+[ro.zygote]: [zygote64_32]

+[selinux.reload_policy]: [1]

+[sensors.contexthub.lid_state]: [open]

+[service.bootanim.exit]: [1]

+[sys.boot_completed]: [1]

+[sys.ims.DATA_DAEMON_STATUS]: [1]

+[sys.ims.QMI_DAEMON_STATUS]: [1]

+[sys.ims.datadaemon.ims.netid]: [1675053681374]

+[sys.listeners.registered]: [true]

+[sys.oem_unlock_allowed]: [0]

+[sys.settings_global_version]: [65]

+[sys.settings_secure_version]: [80]

+[sys.settings_system_version]: [69]

+[sys.sysctl.extra_free_kbytes]: [24300]

+[sys.sysctl.tcp_def_init_rwnd]: [60]

+[sys.usb.config]: [mtp,adb]

+[sys.usb.configfs]: [0]

+[sys.usb.ffs.ready]: [1]

+[sys.usb.state]: [mtp,adb]

+[telephony.lteOnCdmaDevice]: [1]

+[vidc.debug.perf.mode]: [2]

+[vidc.enc.dcvs.extra-buff-count]: [2]

+[vold.decrypt]: [trigger_restart_framework]

+[vold.has_adoptable]: [0]

+[vold.post_fs_data_done]: [1]

+[wc_transport.clean_up]: [0]

+[wc_transport.hci_filter_status]: [1]

+[wc_transport.ref_count]: [1]

+[wc_transport.soc_initialized]: [1]

+[wc_transport.start_hci]: [true]

+[wifi.interface]: [wlan0]

+[wifi.supplicant_scan_interval]: [15]

+[wlan.driver.status]: [ok]

diff --git a/madb/testdata/getprop2.txt b/madb/testdata/getprop2.txt
new file mode 100644
index 0000000..e5c3876
--- /dev/null
+++ b/madb/testdata/getprop2.txt
@@ -0,0 +1,236 @@
+[camera.flash_off]: [0]

+[dalvik.vm.dex2oat-Xms]: [64m]

+[dalvik.vm.dex2oat-Xmx]: [512m]

+[dalvik.vm.dexopt-flags]: [m=y]

+[dalvik.vm.heapgrowthlimit]: [192m]

+[dalvik.vm.heapmaxfree]: [8m]

+[dalvik.vm.heapminfree]: [512k]

+[dalvik.vm.heapsize]: [512m]

+[dalvik.vm.heapstartsize]: [16m]

+[dalvik.vm.heaptargetutilization]: [0.75]

+[dalvik.vm.image-dex2oat-Xms]: [64m]

+[dalvik.vm.image-dex2oat-Xmx]: [64m]

+[dalvik.vm.isa.arm.features]: [div]

+[dalvik.vm.isa.arm64.features]: [default]

+[dalvik.vm.stack-trace-file]: [/data/anr/traces.txt]

+[debug.force_rtl]: [0]

+[dev.bootcomplete]: [1]

+[dhcp.wlan0.dns1]: [8.8.8.8]

+[dhcp.wlan0.dns2]: [8.8.4.4]

+[dhcp.wlan0.dns3]: []

+[dhcp.wlan0.dns4]: []

+[dhcp.wlan0.domain]: [corp.google.com]

+[dhcp.wlan0.gateway]: [100.110.127.254]

+[dhcp.wlan0.ipaddress]: [100.110.70.59]

+[dhcp.wlan0.leasetime]: [1800]

+[dhcp.wlan0.mask]: [255.255.192.0]

+[dhcp.wlan0.mtu]: []

+[dhcp.wlan0.pid]: [32624]

+[dhcp.wlan0.reason]: [RENEW]

+[dhcp.wlan0.result]: [ok]

+[dhcp.wlan0.server]: [172.17.179.177]

+[dhcp.wlan0.vendorInfo]: []

+[drm.service.enabled]: [true]

+[fmas.spkr_2ch]: [35,25]

+[fmas.spkr_6ch]: [35,20,110]

+[fmas.spkr_angles]: [10]

+[fmas.spkr_sgain]: [0]

+[gsm.current.phone-type]: [1]

+[gsm.network.type]: [Unknown]

+[gsm.operator.alpha]: []

+[gsm.operator.iso-country]: []

+[gsm.operator.isroaming]: [false]

+[gsm.operator.numeric]: []

+[gsm.sim.operator.alpha]: []

+[gsm.sim.operator.iso-country]: []

+[gsm.sim.operator.numeric]: []

+[gsm.sim.state]: [ABSENT]

+[gsm.version.baseband]: [0.09.30.0105_0054]

+[gsm.version.ril-impl]: [Qualcomm RIL 1.0]

+[init.svc.adbd]: [running]

+[init.svc.bootanim]: [stopped]

+[init.svc.debuggerd64]: [running]

+[init.svc.debuggerd]: [running]

+[init.svc.defaultcrypto]: [stopped]

+[init.svc.dhcpcd_bt-pan]: [stopped]

+[init.svc.dhcpcd_p2p]: [stopped]

+[init.svc.dhcpcd_wlan0]: [running]

+[init.svc.drm]: [running]

+[init.svc.dumpstate]: [stopped]

+[init.svc.flash_recovery]: [stopped]

+[init.svc.healthd]: [running]

+[init.svc.installd]: [running]

+[init.svc.iprenew_bt-pan]: [stopped]

+[init.svc.iprenew_p2p]: [stopped]

+[init.svc.iprenew_wlan0]: [stopped]

+[init.svc.keystore]: [running]

+[init.svc.kickstart]: [running]

+[init.svc.lmkd]: [running]

+[init.svc.logd]: [running]

+[init.svc.mdnsd]: [stopped]

+[init.svc.media]: [running]

+[init.svc.mtpd]: [stopped]

+[init.svc.netd]: [running]

+[init.svc.netmgrd]: [running]

+[init.svc.p2p_supplicant]: [running]

+[init.svc.pre-recovery]: [stopped]

+[init.svc.qmuxd]: [running]

+[init.svc.racoon]: [stopped]

+[init.svc.ril-daemon]: [running]

+[init.svc.sdcard]: [running]

+[init.svc.servicemanager]: [running]

+[init.svc.surfaceflinger]: [running]

+[init.svc.tlk_daemon]: [running]

+[init.svc.ueventd]: [running]

+[init.svc.vold]: [running]

+[init.svc.watchdogd]: [running]

+[init.svc.zygote]: [running]

+[init.svc.zygote_secondary]: [running]

+[keyguard.no_require_sim]: [true]

+[media.aac_51_output_enabled]: [true]

+[net.bt.name]: [Android]

+[net.change]: [net.dns4]

+[net.dns1]: [fe80::cc40:c3ff:fe9f:69ca]

+[net.dns2]: [fe80::f8ae:dbff:fe1c:3d5f]

+[net.dns3]: [8.8.8.8]

+[net.dns4]: [8.8.4.4]

+[net.hostname]: [android-bf64c3f2a8e45f71]

+[net.qtaguid_enabled]: [1]

+[net.tcp.default_init_rwnd]: [60]

+[persist.radio.adb_log_on]: [1]

+[persist.radio.apm_sim_not_pwdn]: [1]

+[persist.radio.dont_use_dsd]: [false]

+[persist.radio.eons.enabled]: [false]

+[persist.radio.netmgr_init]: [1]

+[persist.radio.snapshot_enabled]: [1]

+[persist.sys.dalvik.vm.lib.2]: [libart.so]

+[persist.sys.profiler_ms]: [0]

+[persist.sys.timezone]: [America/Los_Angeles]

+[persist.sys.usb.config]: [mtp,adb]

+[persist.tegra.compositor]: [glcomposer]

+[ril.ecclist]: [911,112]

+[ril.qcril_pre_init_lock_held]: [0]

+[ril.reload.count]: [1]

+[rild.libargs]: [-e wwan0]

+[rild.libpath]: [/vendor/lib64/libril-qc-qmi-1.so]

+[ro.adb.secure]: [1]

+[ro.allow.mock.location]: [0]

+[ro.audio.monitorRotation]: [true]

+[ro.baseband.arch]: [mdm]

+[ro.baseband]: [US]

+[ro.board.platform]: [tegra132]

+[ro.boot.baseband]: [US]

+[ro.boot.bootloader]: [3.44.1.0123]

+[ro.boot.bootreason]: [hard_reset]

+[ro.boot.misc_pagesize]: [2048]

+[ro.boot.mode]: [normal]

+[ro.boot.serialno]: [HT4BVWV00023]

+[ro.boot.wificountrycode]: [US]

+[ro.boot.wifimacaddr]: [B4:CE:F6:DF:10:EB]

+[ro.bootloader]: [3.44.1.0123]

+[ro.bootmode]: [normal]

+[ro.bt.bdaddr_path]: [/sys/module/flounder_bdaddress/parameters/bdaddress]

+[ro.build.characteristics]: [tablet,nosdcard]

+[ro.build.date.utc]: [1428939803]

+[ro.build.date]: [Mon Apr 13 15:43:23 UTC 2015]

+[ro.build.description]: [volantisg-user 5.1.1 LMY47X 1849464 release-keys]

+[ro.build.display.id]: [LMY47X]

+[ro.build.fingerprint]: [google/volantisg/flounder_lte:5.1.1/LMY47X/1849464:user/release-keys]

+[ro.build.flavor]: [volantisg-user]

+[ro.build.host]: [wpiz4.hot.corp.google.com]

+[ro.build.id]: [LMY47X]

+[ro.build.product]: [flounder_lte]

+[ro.build.tags]: [release-keys]

+[ro.build.type]: [user]

+[ro.build.user]: [android-build]

+[ro.build.version.all_codenames]: [REL]

+[ro.build.version.codename]: [REL]

+[ro.build.version.incremental]: [1849464]

+[ro.build.version.release]: [5.1.1]

+[ro.build.version.sdk]: [22]

+[ro.carrier]: [unknown]

+[ro.com.android.dataroaming]: [false]

+[ro.com.android.dateformat]: [MM-dd-yyyy]

+[ro.com.android.wifi-watchlist]: [GoogleGuest]

+[ro.com.google.clientidbase]: [android-google]

+[ro.com.widevine.cachesize]: [16777216]

+[ro.config.alarm_alert]: [Oxygen.ogg]

+[ro.config.notification_sound]: [Tethys.ogg]

+[ro.config.ringtone]: [Girtab.ogg]

+[ro.crypto.fs_crypto_blkdev]: [/dev/block/dm-0]

+[ro.crypto.fuse_sdcard]: [true]

+[ro.crypto.state]: [encrypted]

+[ro.dalvik.vm.native.bridge]: [0]

+[ro.debuggable]: [0]

+[ro.enable_boot_charger_mode]: [1]

+[ro.error.receiver.system.apps]: [com.google.android.gms]

+[ro.facelock.black_timeout]: [700]

+[ro.facelock.det_timeout]: [1500]

+[ro.facelock.est_max_time]: [500]

+[ro.facelock.lively_timeout]: [2500]

+[ro.facelock.rec_timeout]: [2500]

+[ro.facelock.use_intro_anim]: [true]

+[ro.factorytest]: [0]

+[ro.frp.pst]: [/dev/block/platform/sdhci-tegra.3/by-name/PST]

+[ro.hardware]: [flounder]

+[ro.hw.ks.ready]: [1]

+[ro.hwui.disable_scissor_opt]: [true]

+[ro.hwui.drop_shadow_cache_size]: [6]

+[ro.hwui.gradient_cache_size]: [1]

+[ro.hwui.layer_cache_size]: [48]

+[ro.hwui.path_cache_size]: [32]

+[ro.hwui.r_buffer_cache_size]: [8]

+[ro.hwui.text_large_cache_height]: [1024]

+[ro.hwui.text_large_cache_width]: [2048]

+[ro.hwui.text_small_cache_height]: [1024]

+[ro.hwui.text_small_cache_width]: [1024]

+[ro.hwui.texture_cache_flushrate]: [0.4]

+[ro.hwui.texture_cache_size]: [72]

+[ro.opengles.version]: [196609]

+[ro.product.board]: [flounder]

+[ro.product.brand]: [google]

+[ro.product.cpu.abi]: [arm64-v8a]

+[ro.product.cpu.abilist32]: [armeabi-v7a,armeabi]

+[ro.product.cpu.abilist64]: [arm64-v8a]

+[ro.product.cpu.abilist]: [arm64-v8a,armeabi-v7a,armeabi]

+[ro.product.device]: [flounder_lte]

+[ro.product.locale.language]: [en]

+[ro.product.locale.region]: [US]

+[ro.product.manufacturer]: [htc]

+[ro.product.model]: [Nexus 9]

+[ro.product.name]: [volantisg]

+[ro.revision]: [0]

+[ro.ril.def.agps.mode]: [1]

+[ro.ril.svdo]: [false]

+[ro.ril.svlte1x]: [false]

+[ro.runtime.firstboot]: [1456120066424]

+[ro.secure]: [1]

+[ro.serialno]: [HT4BVWV00023]

+[ro.setupwizard.enterprise_mode]: [1]

+[ro.setupwizard.require_network]: [any]

+[ro.sf.lcd_density]: [320]

+[ro.telephony.default_cdma_sub]: [0]

+[ro.telephony.default_network]: [9]

+[ro.url.legal.android_privacy]: [http://www.google.com/intl/%s/mobile/android/basic/privacy.html]

+[ro.url.legal]: [http://www.google.com/intl/%s/mobile/android/basic/phone-legal.html]

+[ro.vendor.build.date.utc]: [1428939811]

+[ro.vendor.build.date]: [Mon Apr 13 15:43:31 UTC 2015]

+[ro.vendor.build.fingerprint]: [google/volantisg/flounder_lte:5.1.1/LMY47X/1849464:user/release-keys]

+[ro.wifi.channels]: []

+[ro.zygote]: [zygote64_32]

+[selinux.reload_policy]: [1]

+[service.bootanim.exit]: [1]

+[sys.boot_completed]: [1]

+[sys.current.cnv]: [44]

+[sys.settings_global_version]: [6]

+[sys.settings_secure_version]: [60]

+[sys.settings_system_version]: [4]

+[sys.sysctl.extra_free_kbytes]: [36864]

+[sys.sysctl.tcp_def_init_rwnd]: [60]

+[sys.usb.config]: [mtp,adb]

+[sys.usb.state]: [mtp,adb]

+[telephony.lteOnCdmaDevice]: [1]

+[vold.decrypt]: [trigger_restart_framework]

+[vold.post_fs_data_done]: [1]

+[wifi.interface]: [wlan0]

+[wlan.driver.status]: [ok]

diff --git a/madb/testdata/projects/testAndroidDensitySplit/app/build.gradle b/madb/testdata/projects/testAndroidDensitySplit/app/build.gradle
new file mode 100644
index 0000000..8699dba
--- /dev/null
+++ b/madb/testdata/projects/testAndroidDensitySplit/app/build.gradle
@@ -0,0 +1,53 @@
+buildscript {
+    repositories {
+        jcenter()
+        mavenCentral()
+    }
+
+    dependencies {
+        classpath 'com.android.tools.build:gradle:1.3.0'
+        classpath 'com.jakewharton.sdkmanager:gradle-plugin:0.12.+'
+    }
+}
+
+apply plugin: 'android-sdk-manager'
+apply plugin: 'com.android.application'
+
+android {
+    compileSdkVersion 23
+    buildToolsVersion "23.0.1"
+
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_8
+        targetCompatibility JavaVersion.VERSION_1_8
+    }
+    defaultConfig {
+        applicationId "io.v.testProjectId"
+        minSdkVersion 23
+        targetSdkVersion 23
+        versionCode 1
+        versionName "1.0"
+    }
+    splits {
+        density {
+            enable true
+            reset()
+            include 'ldpi', 'mdpi'
+        }
+    }
+
+    // Give different version codes for the density splits.
+    applicationVariants.all { variant ->
+        variant.outputs.eachWithIndex { output, i ->
+            output.versionCodeOverride = i + 1
+        }
+    }
+}
+
+repositories {
+    mavenCentral()
+}
+
+dependencies {
+    compile fileTree(dir: 'libs', include: ['*.jar'])
+}
diff --git a/madb/testdata/projects/testAndroidDensitySplit/app/src/main/AndroidManifest.xml b/madb/testdata/projects/testAndroidDensitySplit/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..51518e6
--- /dev/null
+++ b/madb/testdata/projects/testAndroidDensitySplit/app/src/main/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest
+    package="io.v.testProjectPackage"
+    xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <uses-sdk android:minSdkVersion="23"/>
+
+    <application
+        android:allowBackup="true"
+        android:label="Test Project"
+        android:supportsRtl="true"
+        android:theme="@style/AppTheme">
+        <activity
+            android:name=".LauncherActivity"
+            android:label="@string/app_name">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+        <activity
+            android:name=".SecondActivity"
+            android:label="@string/app_name" >
+        </activity>
+        <activity android:name=".ThirdActivity" />
+    </application>
+
+</manifest>
diff --git a/madb/testdata/projects/testAndroidDensitySplit/build.gradle b/madb/testdata/projects/testAndroidDensitySplit/build.gradle
new file mode 100644
index 0000000..1b7886d
--- /dev/null
+++ b/madb/testdata/projects/testAndroidDensitySplit/build.gradle
@@ -0,0 +1,19 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+    repositories {
+        jcenter()
+    }
+    dependencies {
+        classpath 'com.android.tools.build:gradle:1.3.0'
+
+        // NOTE: Do not place your application dependencies here; they belong
+        // in the individual module build.gradle files
+    }
+}
+
+allprojects {
+    repositories {
+        jcenter()
+    }
+}
diff --git a/madb/testdata/projects/testAndroidDensitySplit/gradle.properties b/madb/testdata/projects/testAndroidDensitySplit/gradle.properties
new file mode 100644
index 0000000..1d3591c
--- /dev/null
+++ b/madb/testdata/projects/testAndroidDensitySplit/gradle.properties
@@ -0,0 +1,18 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+# Default value: -Xmx10248m -XX:MaxPermSize=256m
+# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
\ No newline at end of file
diff --git a/madb/testdata/projects/testAndroidDensitySplit/gradle/wrapper/gradle-wrapper.jar b/madb/testdata/projects/testAndroidDensitySplit/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..8c0fb64
--- /dev/null
+++ b/madb/testdata/projects/testAndroidDensitySplit/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/madb/testdata/projects/testAndroidDensitySplit/gradle/wrapper/gradle-wrapper.properties b/madb/testdata/projects/testAndroidDensitySplit/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..c83a3ba
--- /dev/null
+++ b/madb/testdata/projects/testAndroidDensitySplit/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Mon Nov 02 17:11:51 PST 2015
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.4-all.zip
diff --git a/madb/testdata/projects/testAndroidDensitySplit/gradlew b/madb/testdata/projects/testAndroidDensitySplit/gradlew
new file mode 100755
index 0000000..91a7e26
--- /dev/null
+++ b/madb/testdata/projects/testAndroidDensitySplit/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+    echo "$*"
+}
+
+die ( ) {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+    [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=$((i+1))
+    done
+    case $i in
+        (0) set -- ;;
+        (1) set -- "$args0" ;;
+        (2) set -- "$args0" "$args1" ;;
+        (3) set -- "$args0" "$args1" "$args2" ;;
+        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+    JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/madb/testdata/projects/testAndroidDensitySplit/settings.gradle b/madb/testdata/projects/testAndroidDensitySplit/settings.gradle
new file mode 100644
index 0000000..e7b4def
--- /dev/null
+++ b/madb/testdata/projects/testAndroidDensitySplit/settings.gradle
@@ -0,0 +1 @@
+include ':app'