Merge "Add kernel BuildInfo classes"
diff --git a/Android.mk b/Android.mk
index 7db99fe..b4139e3 100644
--- a/Android.mk
+++ b/Android.mk
@@ -60,3 +60,38 @@
 
 # Build all sub-directories
 include $(call all-makefiles-under,$(LOCAL_PATH))
+
+########################################################
+# Zip up the built files and dist it as google-tradefed.zip
+ifneq (,$(filter tradefed, $(TARGET_BUILD_APPS)))
+
+tradefed_dist_host_jars := tradefed tradefed-tests ddmlib-prebuilt tf-prod-tests
+tradefed_dist_host_jar_files := $(foreach m, $(tradefed_dist_host_jars), $(HOST_OUT_JAVA_LIBRARIES)/$(m).jar)
+
+tradefed_dist_host_exes := tradefed.sh
+tradefed_dist_host_exe_files := $(foreach m, $(tradefed_dist_host_exes), $(BUILD_OUT_EXECUTABLES)/$(m))
+
+tradefed_dist_test_apks := TradeFedUiTestApp TradeFedTestApp
+tradefed_dist_test_apk_files := $(foreach m, $(tradefed_dist_test_apks), $(TARGET_OUT_DATA_APPS)/$(m).apk)
+
+tradefed_dist_files := \
+    $(tradefed_dist_host_jar_files) \
+    $(tradefed_dist_test_apk_files) \
+    $(tradefed_dist_host_exe_files)
+
+tradefed_dist_intermediates := $(call intermediates-dir-for,PACKAGING,tradefed_dist,HOST,COMMON)
+tradefed_dist_zip := $(tradefed_dist_intermediates)/tradefed.zip
+$(tradefed_dist_zip) : $(tradefed_dist_files)
+	@echo "Package: $@"
+	$(hide) rm -rf $(dir $@) && mkdir -p $(dir $@)
+	$(hide) cp -f $^ $(dir $@)
+	$(hide) cd $(dir $@) && zip -q $(notdir $@) $(notdir $^)
+
+.PHONY: tradefed_dist
+tradefed_dist : $(tradefed_dist_zip)
+
+$(call dist-for-goals, tradefed_dist, $(tradefed_dist_zip))
+dist: tradefed_dist
+
+endif  # tradefed in $(TARGET_BUILD_APPS)
+
diff --git a/prod-tests/src/com/android/graphics/tests/ImageProcessingTest.java b/prod-tests/src/com/android/graphics/tests/ImageProcessingTest.java
index f7af788..5a3157f 100644
--- a/prod-tests/src/com/android/graphics/tests/ImageProcessingTest.java
+++ b/prod-tests/src/com/android/graphics/tests/ImageProcessingTest.java
@@ -60,7 +60,7 @@
     private static final String TEST_RUN_NAME = "graphics_image_processing";
     private static final String ITEM_KEY = "frame_time";
     private static final Pattern FRAME_TIME_PATTERN =
-            Pattern.compile("^Renderscript frame time core: (\\d+) ms");
+            Pattern.compile("^Average frame time: (\\d+) ms");
 
     /**
      * Run the ImageProcessing benchmark test, parse test results.
diff --git a/src/com/android/tradefed/device/IWifiHelper.java b/src/com/android/tradefed/device/IWifiHelper.java
index 8b9357c..603da52 100644
--- a/src/com/android/tradefed/device/IWifiHelper.java
+++ b/src/com/android/tradefed/device/IWifiHelper.java
@@ -38,14 +38,6 @@
     void disableWifi() throws DeviceNotAvailableException;
 
     /**
-     * Disconnect from the wifi network identified by the provided integer.
-     *
-     * @param networkId the network id identifying its profile in wpa_supplicant configuration
-     * @throws DeviceNotAvailableException
-     */
-    void disconnectFromNetwork(int networkId) throws DeviceNotAvailableException;
-
-    /**
      * Waits until one of the expected wifi states occurs.
      *
      * @param expectedStates one or more wifi states to expect
@@ -64,35 +56,20 @@
      * ssid.
      *
      * @param ssid the ssid of network to add.
-     * @return an integer number identifying the profile created in wpa_supplicant configuration.
-     *         <code>null</code> if an error occured.
+     * @return <code>true</code> if network was added successfully, <code>false</code> otherwise.
      * @throws DeviceNotAvailableException
      */
-    Integer addOpenNetwork(String ssid) throws DeviceNotAvailableException;
+    boolean addOpenNetwork(String ssid) throws DeviceNotAvailableException;
 
     /**
      * Adds the WPA-PSK security network identified by ssid.
      *
      * @param ssid the ssid of network to add.
      * @param psk the WPA-PSK passphrase to use
-     * @return an integer number identifying the profile created in wpa_supplicant configuration.
-     *         <code>null</code> if an error occured.
+     * @return <code>true</code> if network was added successfully, <code>false</code> otherwise.
      * @throws DeviceNotAvailableException
      */
-    Integer addWpaPskNetwork(String ssid, String psk) throws DeviceNotAvailableException;
-
-    /**
-     * Associate with the wifi network identified by the provided integer.
-     *
-     * @param networkId the network id identifying its profile in wpa_supplicant configuration,
-     *            e.g. returned by AddOpenNetwork
-     * @return <code>true</code> if the call is successful. <code>false</code> if the call failed.
-     *         Note that a <code>true</code> return does not necessarily mean that the device has
-     *         successfully associated with the network, must call {@link #getWifiStatus()} or
-     *         {@link #waitForWifiState(WifiState...)} to verify.
-     * @throws DeviceNotAvailableException
-     */
-    boolean associateNetwork(int networkId) throws DeviceNotAvailableException;
+    boolean addWpaPskNetwork(String ssid, String psk) throws DeviceNotAvailableException;
 
     /**
      * Wait until an ip address is assigned to wifi adapter.
diff --git a/src/com/android/tradefed/device/TestDevice.java b/src/com/android/tradefed/device/TestDevice.java
index ca3d322..30734ac 100644
--- a/src/com/android/tradefed/device/TestDevice.java
+++ b/src/com/android/tradefed/device/TestDevice.java
@@ -1569,21 +1569,17 @@
             // TODO: return false here if failed?
             wifi.waitForWifiState(WifiState.SCANNING, WifiState.COMPLETED);
 
-            Integer networkId = null;
+            boolean added = false;
             if (wifiPsk != null) {
-                networkId = wifi.addWpaPskNetwork(wifiSsid, wifiPsk);
+                added = wifi.addWpaPskNetwork(wifiSsid, wifiPsk);
             } else {
-                networkId = wifi.addOpenNetwork(wifiSsid);
+                added = wifi.addOpenNetwork(wifiSsid);
             }
 
-            if (networkId == null) {
+            if (!added) {
                 CLog.e("Failed to add wifi network %s on %s", wifiSsid, getSerialNumber());
                 return false;
             }
-            if (!wifi.associateNetwork(networkId)) {
-                CLog.e("Failed to enable wifi network %s on %s", wifiSsid, getSerialNumber());
-                return false;
-            }
             if (!wifi.waitForWifiState(WifiState.COMPLETED)) {
                 CLog.e("wifi network %s failed to associate on %s", wifiSsid, getSerialNumber());
                 return false;
diff --git a/src/com/android/tradefed/device/WifiHelper.java b/src/com/android/tradefed/device/WifiHelper.java
index df7083c..dbacfbd 100644
--- a/src/com/android/tradefed/device/WifiHelper.java
+++ b/src/com/android/tradefed/device/WifiHelper.java
@@ -128,14 +128,6 @@
      * {@inheritDoc}
      */
     @Override
-    public void disconnectFromNetwork(int networkId) throws DeviceNotAvailableException {
-        runWifiUtil("removeNetwork", "id", Integer.toString(networkId));
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
     public boolean waitForWifiState(WifiState... expectedStates) throws DeviceNotAvailableException {
         return waitForWifiState(DEFAULT_WIFI_STATE_TIMEOUT, expectedStates);
     }
@@ -230,24 +222,36 @@
      * {@inheritDoc}
      */
     @Override
-    public Integer addOpenNetwork(String ssid) throws DeviceNotAvailableException {
-        return asInt(runWifiUtil("addOpenNetwork", "ssid", ssid));
+    public boolean addOpenNetwork(String ssid) throws DeviceNotAvailableException {
+        int id = asInt(runWifiUtil("addOpenNetwork", "ssid", ssid));
+        if (id < 0) {
+            return false;
+        }
+        if (!asBool(runWifiUtil("associateNetwork", "id", Integer.toString(id)))) {
+            return false;
+        }
+        if (!asBool(runWifiUtil("saveConfiguration"))) {
+            return false;
+        }
+        return true;
     }
 
     /**
      * {@inheritDoc}
      */
     @Override
-    public Integer addWpaPskNetwork(String ssid, String psk) throws DeviceNotAvailableException {
-        return asInt(runWifiUtil("addWpaPskNetwork", "ssid", ssid, "psk", psk));
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public boolean associateNetwork(int networkId) throws DeviceNotAvailableException {
-        return asBool(runWifiUtil("associateNetwork", "id", Integer.toString(networkId)));
+    public boolean addWpaPskNetwork(String ssid, String psk) throws DeviceNotAvailableException {
+        int id = asInt(runWifiUtil("addWpaPskNetwork", "ssid", ssid, "psk", psk));
+        if (id < 0) {
+            return false;
+        }
+        if (!asBool(runWifiUtil("associateNetwork", "id", Integer.toString(id)))) {
+            return false;
+        }
+        if (!asBool(runWifiUtil("saveConfiguration"))) {
+            return false;
+        }
+        return true;
     }
 
     /**
diff --git a/src/com/android/tradefed/targetprep/DefaultTestsZipInstaller.java b/src/com/android/tradefed/targetprep/DefaultTestsZipInstaller.java
index a9b073a..c291344 100644
--- a/src/com/android/tradefed/targetprep/DefaultTestsZipInstaller.java
+++ b/src/com/android/tradefed/targetprep/DefaultTestsZipInstaller.java
@@ -23,6 +23,7 @@
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.device.ITestDevice.RecoveryMode;
 import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.util.ArrayUtil;
 import com.android.tradefed.util.FileUtil;
 
 import java.io.File;
@@ -35,7 +36,8 @@
  * A default implementation of tests zip installer.
  */
 public class DefaultTestsZipInstaller implements ITestsZipInstaller {
-    static final String LOG_TAG = "DefaultTestsZipInstaller";
+    private static final String DEVICE_DATA_PATH = buildAbsPath(FileListingService.DIRECTORY_DATA);
+    private static final File DEVICE_DATA_FILE = new File(DEVICE_DATA_PATH);
 
     /**
      * A list of /data subdirectories to NOT wipe when doing UserDataFlashOption.TESTS_ZIP
@@ -83,12 +85,11 @@
 
         File[] hostDataFiles = getTestsZipDataFiles(hostDir);
         for (File hostSubDir : hostDataFiles) {
-            device.syncFiles(hostSubDir, FileListingService.DIRECTORY_DATA);
+            device.syncFiles(hostSubDir, DEVICE_DATA_PATH);
         }
 
-        File deviceRootPath = new File(FileListingService.FILE_SEPARATOR
-                + FileListingService.DIRECTORY_DATA);
-        for (File dir : findDirs(hostDir, deviceRootPath)) {
+        // FIXME: this may end up mixing host slashes and device slashes
+        for (File dir : findDirs(hostDir, DEVICE_DATA_FILE)) {
             device.executeShellCommand("chown system.system " + dir.getPath());
         }
     }
@@ -105,6 +106,17 @@
         CLog.d("clearing " + FileListingService.DIRECTORY_DATA + " directory on device "
                 + device.getSerialNumber());
 
+        // Touch a file so that we can make sure the filesystem is mounted and r/w and usable.  If
+        // this method is a no-op, then the filesystem might be corrupt and mounted r/o, or might
+        // not be mounted at all.
+        String turtlePath = buildRelPath(DEVICE_DATA_PATH,
+                String.format("turtles-%d.txt", System.currentTimeMillis()));
+        boolean yayTurtle = device.pushString("I like turtles", turtlePath);
+        if (!yayTurtle) {
+            throw new TargetSetupError(String.format("Failed userdata write check on device %s",
+                    device.getSerialNumber()));
+        }
+
         IFileEntry dataEntry = device.getFileEntry(FileListingService.DIRECTORY_DATA);
         if (dataEntry == null) {
             throw new TargetSetupError(String.format("Could not find %s folder on %s",
@@ -120,6 +132,14 @@
         device.setRecoveryMode(cachedRecoveryMode);
     }
 
+    private static String buildRelPath(String... parts) {
+        return ArrayUtil.join(FileListingService.FILE_SEPARATOR, (Object[]) parts);
+    }
+
+    private static String buildAbsPath(String... parts) {
+        return FileListingService.FILE_SEPARATOR + buildRelPath(parts);
+    }
+
     /**
      * Retrieves the set of files contained in given tests.zip/DATA directory.
      * <p/>
diff --git a/src/com/android/tradefed/targetprep/PushFilePreparer.java b/src/com/android/tradefed/targetprep/PushFilePreparer.java
index 80b89b1..17baf8f 100644
--- a/src/com/android/tradefed/targetprep/PushFilePreparer.java
+++ b/src/com/android/tradefed/targetprep/PushFilePreparer.java
@@ -106,10 +106,18 @@
                 fail(String.format("Local source file '%s' does not exist", pair[0]));
                 continue;
             }
-
-            if (!device.pushFile(src, pair[1])) {
-                fail(String.format("Failed to push local '%s' to remote '%s'", pair[0], pair[1]));
-                continue;
+            if (src.isDirectory()) {
+                if (!device.pushDir(src, pair[1])) {
+                    fail(String.format("Failed to push local '%s' to remote '%s'", pair[0],
+                            pair[1]));
+                    continue;
+                }
+            } else {
+                if (!device.pushFile(src, pair[1])) {
+                    fail(String.format("Failed to push local '%s' to remote '%s'", pair[0],
+                            pair[1]));
+                    continue;
+                }
             }
         }
 
diff --git a/tests/src/com/android/tradefed/targetprep/DefaultTestsZipInstallerTest.java b/tests/src/com/android/tradefed/targetprep/DefaultTestsZipInstallerTest.java
index abbe040..7aa3cd8 100644
--- a/tests/src/com/android/tradefed/targetprep/DefaultTestsZipInstallerTest.java
+++ b/tests/src/com/android/tradefed/targetprep/DefaultTestsZipInstallerTest.java
@@ -70,10 +70,30 @@
         mDeviceBuild = new DeviceBuildInfo("1", TEST_STRING, TEST_STRING);
     }
 
+    public void testCantTouchFilesystem() throws Exception {
+        // expect initial android stop
+        EasyMock.expect(mMockDevice.getSerialNumber()).andStubReturn("serial_number_stub");
+        EasyMock.expect(mMockDevice.getRecoveryMode()).andReturn(RecoveryMode.AVAILABLE);
+        mMockDevice.setRecoveryMode(RecoveryMode.ONLINE);
+
+        // turtle!  (return false, for "write failed")
+        EasyMock.expect(mMockDevice.pushString((String) EasyMock.anyObject(),
+                (String) EasyMock.anyObject())).andReturn(false);
+
+        EasyMock.replay(mMockDevice);
+        try {
+            mZipInstaller.deleteData(mMockDevice);
+            fail("Didn't throw TargetSetupError on failed write test");
+        } catch (TargetSetupError e) {
+            // expected
+        }
+        EasyMock.verify(mMockDevice);
+    }
+
     /**
      * Exercise the core logic on a successful scenario.
      */
-    public void testPushTestsZipOntoData() throws DeviceNotAvailableException, TargetSetupError {
+    public void testPushTestsZipOntoData() throws Exception {
         // mock a filesystem with these contents:
         // /data/app
         // /data/$SKIP_THIS
@@ -81,11 +101,15 @@
                 mMockDevice, FileListingService.DIRECTORY_DATA, "app", SKIP_THIS);
 
         // expect initial android stop
-        EasyMock.expect(mMockDevice.getSerialNumber()).andReturn("serial_number_stub").anyTimes();
+        EasyMock.expect(mMockDevice.getSerialNumber()).andStubReturn("serial_number_stub");
         EasyMock.expect(mMockDevice.getRecoveryMode()).andReturn(RecoveryMode.AVAILABLE);
         mMockDevice.setRecoveryMode(RecoveryMode.ONLINE);
         EasyMock.expect(mMockDevice.executeShellCommand("stop")).andReturn("");
 
+        // turtle!  (to make sure filesystem is writable)
+        EasyMock.expect(mMockDevice.pushString((String) EasyMock.anyObject(),
+                (String) EasyMock.anyObject())).andReturn(true);
+
         // expect 'rm app' but not 'rm $SKIP_THIS'
         EasyMock.expect(mMockDevice.executeShellCommand(EasyMock.contains("rm -r data/app")))
                 .andReturn("");