performance test initial work

- pts build added (not yet pulled by cts)

- cts-tradefed has PTS mode where binary is under android-pts

- performance measurement is delivered to host by throwing exception!
  : This can reduce the amount of code duplication as all existing
    instrumentation test code can be re-used.
    When performance test throws PtsException, it is treated as PASS and
    the message is stored to the xml.

- UI scrolling performance
  : scroll 10k list from top to bottom and measure time

- File system performance
  : sequential write, sequential read, update
    update with almost full disk
    actural disk I/O can be checked by monitoring /proc/self/io
    if kernel supports it. But it is only for debugging

Change-Id: Id83988fe34b6040bc20c98061a2b4f3d2f878b86
diff --git a/suite/pts/Android.mk b/suite/pts/Android.mk
new file mode 100644
index 0000000..07b30e3
--- /dev/null
+++ b/suite/pts/Android.mk
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2012 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.
+#
+
+include cts/suite/pts/PtsBuild.mk
+include $(call all-subdir-makefiles)
diff --git a/suite/pts/PtsBuild.mk b/suite/pts/PtsBuild.mk
new file mode 100644
index 0000000..cb79956
--- /dev/null
+++ b/suite/pts/PtsBuild.mk
@@ -0,0 +1,128 @@
+#
+# Copyright (C) 2012 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.
+#
+
+# several makefiles for CTS merged for PTS
+
+LOCAL_PATH:= $(call my-dir)
+
+# New packages should be added here
+PTS_TEST_PACKAGES = \
+    PtsDeviceFilePerf \
+    PtsDeviceUi
+
+PTS_SUPPORT_PACKAGES := \
+	TestDeviceSetup
+
+BUILD_PTS_PACKAGE := cts/suite/pts/build/test_package.mk
+
+PTS_JAVA_TEST_SCANNER := $(HOST_OUT_EXECUTABLES)/cts-java-scanner
+PTS_JAVA_TEST_SCANNER_DOCLET := $(HOST_OUT_JAVA_LIBRARIES)/cts-java-scanner-doclet.jar
+
+# Generator of test XMLs from scanner output.
+PTS_XML_GENERATOR := $(HOST_OUT_EXECUTABLES)/cts-xml-generator
+
+# File indicating which tests should be blacklisted due to problems.
+PTS_EXPECTATIONS := cts/suite/pts/expectations/knownfailures.txt
+
+PTS_TESTCASES_OUT := $(HOST_OUT)/pts-testcases
+
+define pts-get-package-paths
+	$(foreach pkg,$(1),$(PTS_TESTCASES_OUT)/$(pkg).apk)
+endef
+
+define pts-get-test-xmls
+	$(foreach name,$(1),$(PTS_TESTCASES_OUT)/$(name).xml)
+endef
+
+PTS_TEST_CASE_LIST := \
+	$(PTS_SUPPORT_PACKAGES)
+
+PTS_TEST_CASES := \
+		$(call pts-get-package-paths,$(PTS_TEST_PACKAGES))
+
+PTS_TEST_XMLS := $(call pts-get-test-xmls,$(PTS_TEST_PACKAGES))
+
+pts_dir := $(HOST_OUT)/pts
+pts_tools_src_dir := cts/tools
+
+pts_name := android-pts
+
+DDMLIB_JAR := $(HOST_OUT_JAVA_LIBRARIES)/ddmlib-prebuilt.jar
+junit_host_jar := $(HOST_OUT_JAVA_LIBRARIES)/junit.jar
+HOSTTESTLIB_JAR := $(HOST_OUT_JAVA_LIBRARIES)/hosttestlib.jar
+TF_JAR := $(HOST_OUT_JAVA_LIBRARIES)/tradefed-prebuilt.jar
+PTS_TF_JAR := $(HOST_OUT_JAVA_LIBRARIES)/cts-tradefed.jar
+PTS_TF_EXEC := $(HOST_OUT_EXECUTABLES)/pts-tradefed
+PTS_TF_README := $(pts_tools_src_dir)/tradefed-host/README
+
+
+DEFAULT_TEST_PLAN := $(pts_dir)/$(pts_name)/resource/plans/PTS.xml
+
+$(pts_dir)/all_pts_files_stamp: PRIVATE_JUNIT_HOST_JAR := $(junit_host_jar)
+
+$(pts_dir)/all_pts_files_stamp: $(PTS_TEST_CASES) $(PTS_TEST_CASE_LIST) $(junit_host_jar) $(HOSTTESTLIB_JAR) $(PTS_HOST_LIBRARY_JARS) $(TF_JAR) $(VMTESTSTF_JAR) $(PTS_TF_JAR) $(PTS_TF_EXEC) $(PTS_TF_README) $(ACP)
+# Make necessary directory for PTS
+	$(hide) rm -rf $(PRIVATE_PTS_DIR)
+	$(hide) mkdir -p $(TMP_DIR)
+	$(hide) mkdir -p $(PRIVATE_DIR)/docs
+	$(hide) mkdir -p $(PRIVATE_DIR)/tools
+	$(hide) mkdir -p $(PRIVATE_DIR)/repository/testcases
+	$(hide) mkdir -p $(PRIVATE_DIR)/repository/plans
+# Copy executable and JARs to PTS directory
+	$(hide) $(ACP) -fp $(DDMLIB_JAR) $(PRIVATE_JUNIT_HOST_JAR) $(HOSTTESTLIB_JAR) $(PTS_HOST_LIBRARY_JARS) $(TF_JAR) $(PTS_TF_JAR) $(PTS_TF_EXEC) $(PTS_TF_README) $(PRIVATE_DIR)/tools
+# Change mode of the executables
+	$(foreach apk,$(PTS_TEST_CASE_LIST),$(call copy-testcase-apk,$(apk)))
+	$(foreach testcase,$(PTS_TEST_CASES),$(call copy-testcase,$(testcase)))
+	$(hide) touch $@
+
+# Generate the default test plan for User.
+# Usage: buildCts.py <testRoot> <ctsOutputDir> <tempDir> <androidRootDir> <docletPath>
+
+$(DEFAULT_TEST_PLAN): $(pts_dir)/all_pts_files_stamp $(pts_tools_src_dir)/utils/buildCts.py $(HOST_OUT_JAVA_LIBRARIES)/descGen.jar $(PTS_TEST_XMLS) | $(ACP)
+	$(hide) $(ACP) -fp $(PTS_TEST_XMLS) $(PRIVATE_DIR)/repository/testcases
+	$(hide) $(pts_tools_src_dir)/utils/buildCts.py cts/suite/pts $(PRIVATE_DIR) $(TMP_DIR) \
+		$(TOP) $(HOST_OUT_JAVA_LIBRARIES)/descGen.jar -pts
+
+# Package PTS and clean up.
+INTERNAL_PTS_TARGET := $(pts_dir)/$(pts_name).zip
+$(INTERNAL_PTS_TARGET): PRIVATE_NAME := $(pts_name)
+$(INTERNAL_PTS_TARGET): PRIVATE_PTS_DIR := $(pts_dir)
+$(INTERNAL_PTS_TARGET): PRIVATE_DIR := $(pts_dir)/$(pts_name)
+$(INTERNAL_PTS_TARGET): TMP_DIR := $(pts_dir)/temp
+$(INTERNAL_PTS_TARGET): $(pts_dir)/all_pts_files_stamp $(DEFAULT_TEST_PLAN)
+	$(hide) echo "Package PTS: $@"
+	$(hide) cd $(dir $@) && zip -rq $(notdir $@) $(PRIVATE_NAME)
+
+.PHONY: pts
+pts: $(INTERNAL_PTS_TARGET)
+cts: pts
+# generate PTS during CTS build
+ifneq ($(filter cts, $(MAKECMDGOALS)),)
+$(call dist-for-goals,cts,$(INTERNAL_PTS_TARGET))
+endif
+
+define copy-testcase-apk
+
+$(hide) $(ACP) -fp $(call intermediates-dir-for,APPS,$(1))/package.apk \
+	$(PRIVATE_DIR)/repository/testcases/$(1).apk
+
+endef
+
+define copy-testcase
+
+$(hide) $(ACP) -fp $(1) $(PRIVATE_DIR)/repository/testcases/$(notdir $1)
+
+endef
diff --git a/suite/pts/build/test_package.mk b/suite/pts/build/test_package.mk
new file mode 100644
index 0000000..f677c3b
--- /dev/null
+++ b/suite/pts/build/test_package.mk
@@ -0,0 +1,49 @@
+# Copyright (C) 2012 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.
+
+# copied from cts/build. modified for PTS
+
+# Disable by default so "m pts" will work in emulator builds
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_PACKAGE)
+
+pts_package_apk := $(PTS_TESTCASES_OUT)/$(LOCAL_PACKAGE_NAME).apk
+pts_package_xml := $(PTS_TESTCASES_OUT)/$(LOCAL_PACKAGE_NAME).xml
+
+$(pts_package_apk): PRIVATE_PACKAGE := $(LOCAL_PACKAGE_NAME)
+$(pts_package_apk): $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME))/package.apk | $(ACP)
+	$(hide) mkdir -p $(PTS_TESTCASES_OUT)
+	$(hide) $(ACP) -fp $< $@
+
+$(pts_package_xml): PRIVATE_PATH := $(LOCAL_PATH)
+$(pts_package_xml): PRIVATE_INSTRUMENTATION := $(LOCAL_INSTRUMENTATION_FOR)
+$(pts_package_xml): PRIVATE_PACKAGE := $(LOCAL_PACKAGE_NAME)
+$(pts_package_xml): PRIVATE_TEST_PACKAGE := com.android.pts.$(notdir $(LOCAL_PATH))
+$(pts_package_xml): PRIVATE_MANIFEST := $(LOCAL_PATH)/AndroidManifest.xml
+$(pts_package_xml): PRIVATE_TEST_TYPE := $(if $(LOCAL_PTS_TEST_RUNNER),$(LOCAL_PTS_TEST_RUNNER),'')
+$(pts_package_xml): $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME))/package.apk $(PTS_EXPECTATIONS) $(PTS_JAVA_TEST_SCANNER_DOCLET) $(PTS_JAVA_TEST_SCANNER) $(PTS_XML_GENERATOR)
+	$(hide) echo Generating test description for java package $(PRIVATE_PACKAGE)
+	$(hide) mkdir -p $(PTS_TESTCASES_OUT)
+	$(hide) $(PTS_JAVA_TEST_SCANNER) \
+						-s $(PRIVATE_PATH) \
+						-d $(PTS_JAVA_TEST_SCANNER_DOCLET) | \
+			$(PTS_XML_GENERATOR) \
+						-t $(PRIVATE_TEST_TYPE) \
+						-m $(PRIVATE_MANIFEST) \
+						-i "$(PRIVATE_INSTRUMENTATION)" \
+						-n $(PRIVATE_PACKAGE) \
+						-p $(PRIVATE_TEST_PACKAGE) \
+						-e $(PTS_EXPECTATIONS) \
+						-o $@
diff --git a/suite/pts/deviceTests/Android.mk b/suite/pts/deviceTests/Android.mk
new file mode 100644
index 0000000..c141484
--- /dev/null
+++ b/suite/pts/deviceTests/Android.mk
@@ -0,0 +1,17 @@
+#
+# Copyright (C) 2012 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.
+#
+
+include $(call all-subdir-makefiles)
diff --git a/suite/pts/deviceTests/filesystemperf/Android.mk b/suite/pts/deviceTests/filesystemperf/Android.mk
new file mode 100644
index 0000000..3709ecc
--- /dev/null
+++ b/suite/pts/deviceTests/filesystemperf/Android.mk
@@ -0,0 +1,32 @@
+# Copyright (C) 2012 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.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+# don't include this package in any target
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+LOCAL_STATIC_JAVA_LIBRARIES := ptsutil
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := PtsDeviceFilePerf
+
+LOCAL_SDK_VERSION := 16
+
+include $(BUILD_PTS_PACKAGE)
+
diff --git a/suite/pts/deviceTests/filesystemperf/AndroidManifest.xml b/suite/pts/deviceTests/filesystemperf/AndroidManifest.xml
new file mode 100644
index 0000000..0492900
--- /dev/null
+++ b/suite/pts/deviceTests/filesystemperf/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.pts.filesystemperf">
+
+    <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+    <instrumentation android:name="android.test.InstrumentationTestRunner"
+            android:targetPackage="com.android.pts.filesystemperf"
+            android:label="UI Latency measurement" />
+</manifest>
diff --git a/suite/pts/deviceTests/filesystemperf/src/com/android/pts/filesystemperf/FileUtil.java b/suite/pts/deviceTests/filesystemperf/src/com/android/pts/filesystemperf/FileUtil.java
new file mode 100644
index 0000000..3b51630
--- /dev/null
+++ b/suite/pts/deviceTests/filesystemperf/src/com/android/pts/filesystemperf/FileUtil.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2012 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.pts.filesystemperf;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.Random;
+
+import com.android.pts.util.MeasureRun;
+
+import android.content.Context;
+import android.util.Log;
+
+public class FileUtil {
+    private static final String TAG = "FileUtil";
+    private static final Random mRandom = new Random(0);
+    private static long mFileId = 0;
+    /**
+     * create array with different data per each call
+     *
+     * @param length
+     * @param randomSeed
+     * @return
+     */
+    public static byte[] generateRandomData(int length) {
+        byte[] buffer = new byte[length];
+        int val = mRandom.nextInt();
+        for (int i = 0; i < length / 4; i++) {
+            // in little-endian
+            buffer[i * 4] = (byte)(val & 0x000000ff);
+            buffer[i * 4 + 1] = (byte)((val & 0x0000ff00) >> 8);
+            buffer[i * 4 + 2] = (byte)((val & 0x00ff0000) >> 16);
+            buffer[i * 4 + 3] = (byte)((val & 0xff000000) >> 24);
+            val++;
+        }
+        for (int i = (length / 4) * 4; i < length; i++) {
+            buffer[i] = 0;
+        }
+        return buffer;
+    }
+
+    /**
+     * create a new file under the given dirName.
+     * Existing files will not be affected.
+     * @param context
+     * @param dirName
+     * @return
+     */
+    public static File createNewFile(Context context, String dirName) {
+        File topDir = new File(context.getFilesDir(), dirName);
+        topDir.mkdir();
+        String[] list = topDir.list();
+
+        String newFileName;
+        while (true) {
+            newFileName = Long.toString(mFileId);
+            boolean fileExist = false;
+            for (String child : list) {
+                if (child.equals(newFileName)) {
+                    fileExist = true;
+                    break;
+                }
+            }
+            if (!fileExist) {
+                break;
+            }
+            mFileId++;
+        }
+        mFileId++;
+        //Log.i(TAG, "filename" + Long.toString(mFileId));
+        return new File(topDir, newFileName);
+    }
+
+    /**
+     * create multiple new files
+     * @param context
+     * @param dirName
+     * @param count number of files to create
+     * @return
+     */
+    public static File[] createNewFiles(Context context, String dirName, int count) {
+        File[] files = new File[count];
+        for (int i = 0; i < count; i++) {
+            files[i] = createNewFile(context, dirName);
+        }
+        return files;
+    }
+
+    /**
+     * write file with given byte array
+     * @param file
+     * @param data
+     * @param append will append if set true. Otherwise, write from beginning
+     * @throws IOException
+     */
+    public static void writeFile(File file, byte[] data, boolean append) throws IOException {
+        FileOutputStream out = new FileOutputStream(file, append);
+        out.write(data);
+        out.flush();
+        out.close();
+    }
+
+    /**
+     * create a new file with given length.
+     * @param context
+     * @param dirName
+     * @param length
+     * @return
+     * @throws IOException
+     */
+    public static File createNewFilledFile(Context context, String dirName, long length)
+            throws IOException {
+        final int BUFFER_SIZE = 10 * 1024 * 1024;
+        File file = createNewFile(context, dirName);
+        FileOutputStream out = new FileOutputStream(file);
+        byte[] data = generateRandomData(BUFFER_SIZE);
+        long written = 0;
+        while (written < length) {
+            out.write(data);
+            written += BUFFER_SIZE;
+        }
+        out.flush();
+        out.close();
+        return file;
+    }
+
+    /**
+     * remove given file or directory under the current app's files dir.
+     * @param context
+     * @param name
+     */
+    public static void removeFileOrDir(Context context, String name) {
+        File entry = new File(context.getFilesDir(), name);
+        if (entry.exists()) {
+            removeEntry(entry);
+        }
+    }
+
+    private static void removeEntry(File entry) {
+        if (entry.isDirectory()) {
+            String[] children = entry.list();
+            for (String child : children) {
+                removeEntry(new File(entry, child));
+            }
+        }
+        entry.delete();
+    }
+
+    /**
+     * measure time taken for each IO run with amount R/W
+     * @param count
+     * @param run
+     * @param readAmount returns amount of read in bytes for each interval.
+     *        Value will not be written if /proc/self/io does not exist.
+     * @param writeAmount returns amount of write in bytes for each interval.
+     * @return time per each interval
+     * @throws IOException
+     */
+    public static double[] measureIO(int count, double[] readAmount, double[] writeAmount,
+            MeasureRun run)  throws IOException {
+        double[] result = new double[count];
+        File procIo = new File("/proc/self/io");
+        boolean measureIo = procIo.exists();
+        long prev = System.currentTimeMillis();
+        RWAmount prevAmount = new RWAmount();
+        if (measureIo) {
+            prevAmount = getRWAmount(procIo);
+        }
+        for (int i = 0; i < count; i++) {
+            run.run(i);
+            long current =  System.currentTimeMillis();
+            result[i] = current - prev;
+            prev = current;
+            if (measureIo) {
+                RWAmount currentAmount = getRWAmount(procIo);
+                readAmount[i] = currentAmount.mRd - prevAmount.mRd;
+                writeAmount[i] = currentAmount.mWr - prevAmount.mWr;
+                prevAmount = currentAmount;
+            }
+        }
+        return result;
+    }
+
+    private static class RWAmount {
+        public double mRd = 0.0;
+        public double mWr = 0.0;
+    };
+
+    private static RWAmount getRWAmount(File file) throws IOException {
+        RWAmount amount = new RWAmount();
+
+        BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
+        String line;
+        while((line = br.readLine())!= null) {
+            if (line.startsWith("read_bytes")) {
+                amount.mRd = Double.parseDouble(line.split(" ")[1]);
+            } else if (line.startsWith("write_bytes")) {
+                amount.mWr = Double.parseDouble(line.split(" ")[1]);
+            }
+        }
+        br.close();
+        return amount;
+    }
+}
diff --git a/suite/pts/deviceTests/filesystemperf/src/com/android/pts/filesystemperf/FullUpdateTest.java b/suite/pts/deviceTests/filesystemperf/src/com/android/pts/filesystemperf/FullUpdateTest.java
new file mode 100644
index 0000000..261735c
--- /dev/null
+++ b/suite/pts/deviceTests/filesystemperf/src/com/android/pts/filesystemperf/FullUpdateTest.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2012 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.pts.filesystemperf;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import com.android.pts.util.MeasureRun;
+import com.android.pts.util.MeasureTime;
+import com.android.pts.util.PtsAndroidTestCase;
+import com.android.pts.util.ReportLog;
+import com.android.pts.util.SystemUtil;
+
+import android.util.Log;
+
+public class FullUpdateTest extends PtsAndroidTestCase {
+    private static final String DIR_INITIAL_FILL = "INITIAL_FILL";
+    private static final String DIR_WORK = "WORK";
+    private static final String TAG = "FullUpdateTest";
+
+    @Override
+    protected void tearDown() throws Exception {
+        FileUtil.removeFileOrDir(getContext(), DIR_INITIAL_FILL);
+        FileUtil.removeFileOrDir(getContext(), DIR_WORK);
+        super.tearDown();
+    }
+
+    // fill disk almost, update exceeding free space, then update some amount
+    // idea is to drain all free blocks and measure update performance
+    public void testAlmostFilledUpdate() throws IOException {
+        long freeDisk = SystemUtil.getFreeDiskSize(getContext());
+        final long FREE_SPACE_TO_LEAVE = 500L * 1024L * 1024L; // leave this much
+        long diskToFill = freeDisk - FREE_SPACE_TO_LEAVE;
+        Log.i(TAG, "free disk " + freeDisk + ", to fill " + diskToFill);
+        final long MAX_FILE_SIZE_TO_FILL = 1024L * 1024L * 1024L;
+        long filled = 0;
+        while (filled < diskToFill) {
+            long toFill = diskToFill - filled;
+            if (toFill > MAX_FILE_SIZE_TO_FILL) {
+                toFill = MAX_FILE_SIZE_TO_FILL;
+            }
+            Log.i(TAG, "Generating file " + toFill);
+            FileUtil.createNewFilledFile(getContext(),
+                    DIR_INITIAL_FILL, toFill);
+            filled += toFill;
+        }
+
+        // now about freeSpaceToLeave should be left
+        // and try updating exceeding the free space size
+        final long FILE_SIZE = FREE_SPACE_TO_LEAVE / 2;
+        File file = FileUtil.createNewFilledFile(getContext(),
+                DIR_WORK, FILE_SIZE);
+        final int BUFFER_SIZE = 10 * 1024 * 1024;
+        final byte[] data = FileUtil.generateRandomData(BUFFER_SIZE);
+        for (int i = 0; i < 10; i++) {
+            final FileOutputStream out = new FileOutputStream(file);
+            int numberRepeat = (int)(FILE_SIZE / BUFFER_SIZE);
+            double[] times = MeasureTime.measure(numberRepeat, new MeasureRun() {
+
+                @Override
+                public void run(int i) throws IOException {
+                    out.write(data);
+                    out.flush();
+                }
+            });
+            out.close();
+            getReportLog().printArray(i + "-th round MB/s",
+                    ReportLog.calcRatePerSecArray(BUFFER_SIZE / 1024 / 1024, times), true);
+        }
+    }
+}
diff --git a/suite/pts/deviceTests/filesystemperf/src/com/android/pts/filesystemperf/RWTest.java b/suite/pts/deviceTests/filesystemperf/src/com/android/pts/filesystemperf/RWTest.java
new file mode 100644
index 0000000..f44e797
--- /dev/null
+++ b/suite/pts/deviceTests/filesystemperf/src/com/android/pts/filesystemperf/RWTest.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2012 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.pts.filesystemperf;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import com.android.pts.util.MeasureRun;
+import com.android.pts.util.MeasureTime;
+import com.android.pts.util.PtsAndroidTestCase;
+import com.android.pts.util.ReportLog;
+import com.android.pts.util.SystemUtil;
+
+public class RWTest extends PtsAndroidTestCase {
+    private static final String DIR_SEQ_WR = "SEQ_WR";
+    private static final String DIR_SEQ_UPD = "SEQ_UPD";
+    private static final String DIR_SEQ_RD = "SEQ_RD";
+    private static final int BUFFER_SIZE = 10 * 1024 * 1024;
+
+    @Override
+    protected void tearDown() throws Exception {
+        FileUtil.removeFileOrDir(getContext(), DIR_SEQ_WR);
+        FileUtil.removeFileOrDir(getContext(), DIR_SEQ_UPD);
+        FileUtil.removeFileOrDir(getContext(), DIR_SEQ_RD);
+        super.tearDown();
+    }
+
+    public void testSingleSequentialWrite() throws IOException {
+        final int numberOfFiles = (int)(getFileSizeExceedingMemory() / BUFFER_SIZE);
+        getReportLog().printValue("files", numberOfFiles);
+        final byte[] data = FileUtil.generateRandomData(BUFFER_SIZE);
+        final File[] files = FileUtil.createNewFiles(getContext(), DIR_SEQ_WR,
+                numberOfFiles);
+        double[] rdAmount = new double[numberOfFiles];
+        double[] wrAmount = new double[numberOfFiles];
+        double[] times = FileUtil.measureIO(numberOfFiles, rdAmount, wrAmount, new MeasureRun() {
+
+            @Override
+            public void run(int i) throws IOException {
+                FileUtil.writeFile(files[i], data, false);
+            }
+        });
+        getReportLog().printArray("try " + numberOfFiles + " files, result MB/s",
+                ReportLog.calcRatePerSecArray(BUFFER_SIZE / 1024 / 1024, times), true);
+        getReportLog().printArray("Wr amount", wrAmount, true);
+    }
+
+
+    public void testSingleSequentialUpdate() throws IOException {
+        final long fileSize = getFileSizeExceedingMemory();
+        File file = FileUtil.createNewFilledFile(getContext(),
+                DIR_SEQ_UPD, fileSize);
+        final byte[] data = FileUtil.generateRandomData(BUFFER_SIZE);
+        for (int i = 0; i < 4; i++) {
+            final FileOutputStream out = new FileOutputStream(file);
+            int numberRepeat = (int)(fileSize / BUFFER_SIZE);
+            double[] rdAmount = new double[numberRepeat];
+            double[] wrAmount = new double[numberRepeat];
+            double[] times = FileUtil.measureIO(numberRepeat, rdAmount, wrAmount, new MeasureRun() {
+
+                @Override
+                public void run(int i) throws IOException {
+                    out.write(data);
+                    out.flush();
+                }
+            });
+            out.close();
+            getReportLog().printArray(i + "-th round MB/s",
+                    ReportLog.calcRatePerSecArray(BUFFER_SIZE / 1024 / 1024, times), true);
+            getReportLog().printArray("Wr amount", wrAmount, true);
+        }
+    }
+
+    public void testSingleSequentialRead() throws IOException {
+        final long fileSize = getFileSizeExceedingMemory();
+        long start = System.currentTimeMillis();
+        final File file = FileUtil.createNewFilledFile(getContext(),
+                DIR_SEQ_RD, fileSize);
+        long finish = System.currentTimeMillis();
+        getReportLog().printValue("write size " + fileSize + " result MB/s",
+                ReportLog.calcRatePerSec(fileSize / 1024 / 1024, finish - start));
+
+        final int NUMBER_READ = 4;
+
+        final byte[] data = new byte[BUFFER_SIZE];
+        double[] times = MeasureTime.measure(NUMBER_READ, new MeasureRun() {
+
+            @Override
+            public void run(int i) throws IOException {
+                final FileInputStream in = new FileInputStream(file);
+                long read = 0;
+                while (read < fileSize) {
+                    in.read(data);
+                    read += BUFFER_SIZE;
+                }
+                in.close();
+            }
+        });
+        getReportLog().printArray("read MB/s",
+                ReportLog.calcRatePerSecArray(fileSize / 1024 / 1024, times), true);
+    }
+
+    private long getFileSizeExceedingMemory() {
+        long freeDisk = SystemUtil.getFreeDiskSize(getContext());
+        long memSize = SystemUtil.getTotalMemory(getContext());
+        long diskSizeTarget = (2 * memSize / BUFFER_SIZE) * BUFFER_SIZE;
+        if (diskSizeTarget > freeDisk) {
+            fail("Free disk size " + freeDisk + " too small");
+        }
+        return diskSizeTarget;
+    }
+}
diff --git a/suite/pts/deviceTests/filesystemperf/src/com/android/pts/filesystemperf/TestTest.java b/suite/pts/deviceTests/filesystemperf/src/com/android/pts/filesystemperf/TestTest.java
new file mode 100644
index 0000000..32e1878
--- /dev/null
+++ b/suite/pts/deviceTests/filesystemperf/src/com/android/pts/filesystemperf/TestTest.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+
+// code for testing, will be removed / moved before release
+
+package com.android.pts.filesystemperf;
+
+import com.android.pts.util.PtsAndroidTestCase;
+
+public class TestTest extends PtsAndroidTestCase {
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    public void testPass() {
+        double[] array = new double[] {1.0, 2.0, 3.0};
+        getReportLog().printArray(" ", array, true);
+        getReportLog().printArray(" ", array, false);
+        getReportLog().printValue(" ", 1.0);
+        getReportLog().printValue(" ", 2.0);
+    }
+
+    public void testFail() throws Exception {
+        getReportLog().printValue(" ", 1.0);
+        getReportLog().printValue(" ", 2.0);
+        throw new Exception("failed");
+    }
+}
diff --git a/suite/pts/deviceTests/ptsutil/Android.mk b/suite/pts/deviceTests/ptsutil/Android.mk
new file mode 100644
index 0000000..d097036
--- /dev/null
+++ b/suite/pts/deviceTests/ptsutil/Android.mk
@@ -0,0 +1,29 @@
+# Copyright (C) 2012 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_MODULE := ptsutil
+
+LOCAL_SDK_VERSION := 16
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/suite/pts/deviceTests/ptsutil/src/com/android/pts/util/MeasureRun.java b/suite/pts/deviceTests/ptsutil/src/com/android/pts/util/MeasureRun.java
new file mode 100644
index 0000000..1f6f6ed
--- /dev/null
+++ b/suite/pts/deviceTests/ptsutil/src/com/android/pts/util/MeasureRun.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2012 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.pts.util;
+
+import java.io.IOException;
+
+/**
+ * interface for measuring time for each run.
+ */
+public interface MeasureRun {
+
+    abstract public void run(int i) throws IOException;
+}
diff --git a/suite/pts/deviceTests/ptsutil/src/com/android/pts/util/MeasureTime.java b/suite/pts/deviceTests/ptsutil/src/com/android/pts/util/MeasureTime.java
new file mode 100644
index 0000000..337a472
--- /dev/null
+++ b/suite/pts/deviceTests/ptsutil/src/com/android/pts/util/MeasureTime.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2012 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.pts.util;
+
+import java.io.IOException;
+
+public class MeasureTime {
+    /**
+     * measure time taken for each run for given count
+     * @param count
+     * @param run
+     * @return array of time taken in each run in msec.
+     * @throws IOException
+     */
+    public static double[] measure(int count, MeasureRun run)  throws IOException {
+        double[] result = new double[count];
+        long prev = System.currentTimeMillis();
+        for (int i = 0; i < count; i++) {
+            run.run(i);
+            long current =  System.currentTimeMillis();
+            result[i] = current - prev;
+            prev = current;
+        }
+        return result;
+    }
+}
diff --git a/suite/pts/deviceTests/ptsutil/src/com/android/pts/util/PtsActivityInstrumentationTestCase2.java b/suite/pts/deviceTests/ptsutil/src/com/android/pts/util/PtsActivityInstrumentationTestCase2.java
new file mode 100644
index 0000000..84c1718
--- /dev/null
+++ b/suite/pts/deviceTests/ptsutil/src/com/android/pts/util/PtsActivityInstrumentationTestCase2.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2012 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.pts.util;
+
+import android.app.Activity;
+import android.test.ActivityInstrumentationTestCase2;
+
+
+public class PtsActivityInstrumentationTestCase2<T extends Activity> extends
+        ActivityInstrumentationTestCase2<T> {
+
+    private ReportLog mReportLog = new ReportLog();
+
+    public PtsActivityInstrumentationTestCase2(Class<T> activityClass) {
+        super(activityClass);
+    }
+
+    public ReportLog getReportLog() {
+        return mReportLog;
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        mReportLog.throwReportToHost();
+    }
+
+}
diff --git a/suite/pts/deviceTests/ptsutil/src/com/android/pts/util/PtsAndroidTestCase.java b/suite/pts/deviceTests/ptsutil/src/com/android/pts/util/PtsAndroidTestCase.java
new file mode 100644
index 0000000..bf17956
--- /dev/null
+++ b/suite/pts/deviceTests/ptsutil/src/com/android/pts/util/PtsAndroidTestCase.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2012 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.pts.util;
+
+import android.test.AndroidTestCase;
+
+public class PtsAndroidTestCase extends AndroidTestCase {
+
+    private ReportLog mReportLog = new ReportLog();
+
+    public ReportLog getReportLog() {
+        return mReportLog;
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        mReportLog.throwReportToHost();
+    }
+}
diff --git a/suite/pts/deviceTests/ptsutil/src/com/android/pts/util/PtsException.java b/suite/pts/deviceTests/ptsutil/src/com/android/pts/util/PtsException.java
new file mode 100644
index 0000000..e8ec5be
--- /dev/null
+++ b/suite/pts/deviceTests/ptsutil/src/com/android/pts/util/PtsException.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2012 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.pts.util;
+
+/**
+ * Exception throw by PTS test to pass the result to host
+ * This should not be thrown by test app unless the result is complete.
+ */
+@SuppressWarnings("serial")
+public class PtsException extends Exception {
+    public PtsException(String message) {
+        super(message);
+    }
+}
diff --git a/suite/pts/deviceTests/ptsutil/src/com/android/pts/util/ReportLog.java b/suite/pts/deviceTests/ptsutil/src/com/android/pts/util/ReportLog.java
new file mode 100644
index 0000000..15be1aa
--- /dev/null
+++ b/suite/pts/deviceTests/ptsutil/src/com/android/pts/util/ReportLog.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2012 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.pts.util;
+
+import android.util.Log;
+
+import java.util.LinkedList;
+import java.util.List;
+
+
+/**
+ * Utility class to print performance measurement result back to host.
+ * For now, throws know exception with message.
+ *
+ * Format:
+ * LOG_SEPARATOR : separates each log
+ * Message = log [LOG_SEPARATOR log]*
+ * log for single value = classMethodName:line_number|header|d|value
+ * log for array = classMethodName:line_number|header|da|values|
+ *                     average average_value min|max value stddev value
+ */
+public class ReportLog {
+    private static final String TAG = "PtsReport";
+    private static final String LOG_SEPARATOR = "+++";
+    private static final String LOG_ELEM_SEPARATOR = "|";
+
+    private List<String> mMessages = new LinkedList<String> ();
+    /**
+     * print given value to the report
+     * @param header string to explain the contents. It can be unit for the value.
+     * @param val
+     */
+    public void printValue(String header, double val) {
+        String message = getClassMethodNames(4, true) + LOG_ELEM_SEPARATOR + header +
+                LOG_ELEM_SEPARATOR + "d" + LOG_ELEM_SEPARATOR + val;
+        mMessages.add(message);
+        Log.i(TAG, message);
+    }
+
+    /**
+     * array version of printValue
+     * @param header
+     * @param val
+     * @param addMin add minimum to the result. If false, add maximum to the result
+     */
+    public void printArray(String header, double[] val, boolean addMin) {
+        StringBuilder builder = new StringBuilder();
+        builder.append(getClassMethodNames(4, true) + LOG_ELEM_SEPARATOR + header +
+                LOG_ELEM_SEPARATOR + "da" + LOG_ELEM_SEPARATOR);
+        double average = 0.0;
+        double min = val[0];
+        double max = val[0];
+        for (double v : val) {
+            builder.append(v);
+            builder.append(" ");
+            average += v;
+            if (v > max) {
+                max = v;
+            }
+            if (v < min) {
+                min = v;
+            }
+        }
+        average /= val.length;
+        double power = 0;
+        for (double v : val) {
+            double delta = v - average;
+            power += (delta * delta);
+        }
+        power /= val.length;
+        double stdDev = Math.sqrt(power);
+        builder.append(LOG_ELEM_SEPARATOR + "average " + average +
+                (addMin ? (" min " + min) : (" max " + max)) + " stddev " + stdDev);
+        mMessages.add(builder.toString());
+        Log.i(TAG, builder.toString());
+    }
+
+    public void throwReportToHost() throws PtsException {
+        StringBuilder builder = new StringBuilder();
+        for (String entry : mMessages) {
+            builder.append(entry);
+            builder.append(LOG_SEPARATOR);
+        }
+        // delete the last separator
+        if (builder.length() >= LOG_SEPARATOR.length()) {
+            builder.delete(builder.length() - LOG_SEPARATOR.length(), builder.length());
+        }
+        throw new PtsException(builder.toString());
+    }
+
+    /**
+     * calculate rate per sec for given change happened during given timeInMSec.
+     * timeInSec with 0 value will be changed to small value to prevent divide by zero.
+     * @param change
+     * @param timeInMSec
+     * @return
+     */
+    public static double calcRatePerSec(double change, double timeInMSec) {
+        if (timeInMSec == 0) {
+            return change * 1000.0 / 0.001; // do not allow zero
+        } else {
+            return change * 1000.0 / timeInMSec;
+        }
+    }
+
+    /**
+     * array version of calcRatePerSecArray
+     * @param change
+     * @param timeInMSec
+     * @return
+     */
+    public static double[] calcRatePerSecArray(double change, double[] timeInMSec) {
+        double[] result = new double[timeInMSec.length];
+        change *= 1000.0;
+        for (int i = 0; i < timeInMSec.length; i++) {
+            if (timeInMSec[i] == 0) {
+                result[i] = change / 0.001;
+            } else {
+                result[i] = change / timeInMSec[i];
+            }
+        }
+        return result;
+    }
+
+    /**
+     * get classname.methodname from call stack of the current thread
+     *
+     * @return
+     */
+    public static String getClassMethodNames() {
+        return getClassMethodNames(4, false);
+    }
+
+    private static String getClassMethodNames(int depth, boolean addLineNumber) {
+        StackTraceElement[] elements = Thread.currentThread().getStackTrace();
+        String names = elements[depth].getClassName() + "." + elements[depth].getMethodName() +
+                (addLineNumber ? ":" + elements[depth].getLineNumber() : "");
+        return names;
+    }
+}
diff --git a/suite/pts/deviceTests/ptsutil/src/com/android/pts/util/SystemUtil.java b/suite/pts/deviceTests/ptsutil/src/com/android/pts/util/SystemUtil.java
new file mode 100644
index 0000000..7975463
--- /dev/null
+++ b/suite/pts/deviceTests/ptsutil/src/com/android/pts/util/SystemUtil.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2012 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.pts.util;
+
+import android.app.ActivityManager;
+import android.app.ActivityManager.MemoryInfo;
+import android.content.Context;
+import android.os.StatFs;
+
+public class SystemUtil {
+    public static long getFreeDiskSize(Context context) {
+        StatFs statFs = new StatFs(context.getFilesDir().getAbsolutePath());
+        return (long)statFs.getAvailableBlocks() * statFs.getBlockSize();
+    }
+
+    public static long getFreeMemory(Context context) {
+        MemoryInfo info = new MemoryInfo();
+        ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
+        activityManager.getMemoryInfo(info);
+        return info.availMem;
+    }
+
+    public static long getTotalMemory(Context context) {
+        MemoryInfo info = new MemoryInfo();
+        ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
+        activityManager.getMemoryInfo(info);
+        return info.totalMem; // TODO totalMem N/A in ICS.
+    }
+}
diff --git a/suite/pts/deviceTests/ui/Android.mk b/suite/pts/deviceTests/ui/Android.mk
new file mode 100644
index 0000000..f3c3944
--- /dev/null
+++ b/suite/pts/deviceTests/ui/Android.mk
@@ -0,0 +1,35 @@
+# Copyright (C) 2012 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.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+# don't include this package in any target
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+LOCAL_STATIC_JAVA_LIBRARIES := ptsutil
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := PtsDeviceUi
+
+LOCAL_SDK_VERSION := 16
+
+include $(BUILD_PTS_PACKAGE)
+
+
diff --git a/suite/pts/deviceTests/ui/AndroidManifest.xml b/suite/pts/deviceTests/ui/AndroidManifest.xml
new file mode 100644
index 0000000..f750d14
--- /dev/null
+++ b/suite/pts/deviceTests/ui/AndroidManifest.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.pts.ui">
+
+    <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+        <activity
+                android:name=".ScrollingActivity"
+                android:screenOrientation="portrait"
+                android:configChanges="keyboard|keyboardHidden|orientation|screenSize">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+    </application>
+    <instrumentation android:name="android.test.InstrumentationTestRunner"
+            android:targetPackage="com.android.pts.ui"
+            android:label="UI Latency measurement" />
+</manifest>
diff --git a/suite/pts/deviceTests/ui/src/com/android/pts/ui/ScrollingActivity.java b/suite/pts/deviceTests/ui/src/com/android/pts/ui/ScrollingActivity.java
new file mode 100644
index 0000000..7bdfff9
--- /dev/null
+++ b/suite/pts/deviceTests/ui/src/com/android/pts/ui/ScrollingActivity.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2012 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.pts.ui;
+
+import android.app.ListActivity;
+import android.os.Bundle;
+import android.widget.AbsListView;
+import android.widget.AbsListView.OnScrollListener;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class ScrollingActivity extends ListActivity implements OnScrollListener
+{
+    static final String TAG = "ScrollingActivity";
+    private static final int NUMBER_ELEMENTS = 10000;
+    private static final int SCROLL_TIME_IN_MS = 1;
+    private static final int WAIT_TIMEOUT_IN_SECS = 5 * 60;
+    private String[] mItems = new String[NUMBER_ELEMENTS];
+    private CountDownLatch mLatchStop = null;
+    private int mTargetLoc;
+
+    public void onCreate(Bundle icicle)
+    {
+        super.onCreate(icicle);
+        for (int i = 0; i < NUMBER_ELEMENTS; i++) {
+            mItems[i] = Integer.toString(i);
+        }
+        setListAdapter(new ArrayAdapter<String>(this,
+                android.R.layout.simple_list_item_1, mItems));
+        ListView view = getListView();
+        view.setOnScrollListener(this);
+        //view.setVelocityScale(100.0f);
+    }
+
+    public boolean scrollToTop() {
+        return doScroll(0);
+    }
+    public boolean scrollToBottom() {
+        return doScroll(NUMBER_ELEMENTS - 1);
+    }
+
+    private boolean doScroll(final int loc) {
+        mLatchStop = new CountDownLatch(1);
+        mTargetLoc = loc;
+        final ListView view = getListView();
+        runOnUiThread( new Runnable() {
+            @Override
+            public void run() {
+                view.smoothScrollToPositionFromTop(loc, 0, SCROLL_TIME_IN_MS);
+            }
+        });
+        boolean result = false;
+        try {
+            result = mLatchStop.await(WAIT_TIMEOUT_IN_SECS, TimeUnit.SECONDS);
+        } catch (InterruptedException e) {
+            // ignore
+        }
+        mLatchStop = null;
+        return result;
+    }
+    public void onStop()
+    {
+        super.onStop();
+    }
+
+    public void onResume()
+    {
+        super.onResume();
+    }
+
+    @Override
+    public void onScrollStateChanged(AbsListView view, int scrollState) {
+
+    }
+
+    @Override
+    public void onScroll(AbsListView view, int firstVisibleItem,
+            int visibleItemCount, int totalItemCount) {
+        //Log.i(TAG, "onScroll " + firstVisibleItem + " " + visibleItemCount);
+        if ((mTargetLoc >= firstVisibleItem) &&
+                (mTargetLoc <= (firstVisibleItem + visibleItemCount))) {
+            if (mLatchStop != null) {
+                mLatchStop.countDown();
+            }
+        }
+    }
+}
diff --git a/suite/pts/deviceTests/ui/src/com/android/pts/ui/ScrollingTest.java b/suite/pts/deviceTests/ui/src/com/android/pts/ui/ScrollingTest.java
new file mode 100644
index 0000000..a985591
--- /dev/null
+++ b/suite/pts/deviceTests/ui/src/com/android/pts/ui/ScrollingTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2012 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.pts.ui;
+
+import java.io.IOException;
+
+import com.android.pts.util.MeasureRun;
+import com.android.pts.util.MeasureTime;
+import com.android.pts.util.PtsActivityInstrumentationTestCase2;
+import com.android.pts.util.ReportLog;
+
+public class ScrollingTest extends PtsActivityInstrumentationTestCase2<ScrollingActivity> {
+    private ScrollingActivity mActivity;
+
+    public ScrollingTest() {
+        super(ScrollingActivity.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mActivity = getActivity();
+        getInstrumentation().waitForIdleSync();
+        try {
+            runTestOnUiThread(new Runnable() {
+                public void run() {
+                }
+            });
+        } catch (Throwable e) {
+            e.printStackTrace();
+            fail();
+        }
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        mActivity = null;
+        super.tearDown();
+    }
+
+    public void testFullScrolling() throws IOException {
+        final int NUMBER_REPEAT = 10;
+        final ScrollingActivity activity = mActivity;
+        double[] results = MeasureTime.measure(NUMBER_REPEAT, new MeasureRun() {
+
+            @Override
+            public void run(int i) throws IOException {
+                assertTrue(activity.scrollToBottom());
+                assertTrue(activity.scrollToTop());
+            }
+        });
+        getReportLog().printArray("ms", results, false);
+    }
+}
diff --git a/suite/pts/expectations/knownfailures.txt b/suite/pts/expectations/knownfailures.txt
new file mode 100644
index 0000000..0d4f101
--- /dev/null
+++ b/suite/pts/expectations/knownfailures.txt
@@ -0,0 +1,2 @@
+[
+]
diff --git a/suite/pts/tools/Android.mk b/suite/pts/tools/Android.mk
new file mode 100644
index 0000000..c141484
--- /dev/null
+++ b/suite/pts/tools/Android.mk
@@ -0,0 +1,17 @@
+#
+# Copyright (C) 2012 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.
+#
+
+include $(call all-subdir-makefiles)
diff --git a/suite/pts/tools/tradefed/Android.mk b/suite/pts/tools/tradefed/Android.mk
new file mode 100644
index 0000000..09e49b1
--- /dev/null
+++ b/suite/pts/tools/tradefed/Android.mk
@@ -0,0 +1,22 @@
+# Copyright (C) 2011 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.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_PREBUILT_EXECUTABLES := pts-tradefed
+include $(BUILD_HOST_PREBUILT)
+
diff --git a/suite/pts/tools/tradefed/pts-tradefed b/suite/pts/tools/tradefed/pts-tradefed
new file mode 100755
index 0000000..fedbfaf
--- /dev/null
+++ b/suite/pts/tools/tradefed/pts-tradefed
@@ -0,0 +1,86 @@
+#!/bin/bash
+
+# Copyright (C) 2011 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.
+
+# launcher script for cts-tradefed harness for PTS
+# can be used from an Android build environment, or a standalone pts zip
+
+checkFile() {
+    if [ ! -f "$1" ]; then
+        echo "Unable to locate $1"
+        exit
+    fi;
+}
+
+checkPath() {
+    if ! type -P $1 &> /dev/null; then
+        echo "Unable to find $1 in path."
+        exit
+    fi;
+}
+
+checkPath adb
+checkPath java
+
+# check java version
+JAVA_VERSION=$(java -version 2>&1 | head -n 1 | grep '[ "]1\.6[\. "$$]')
+if [ "${JAVA_VERSION}" == "" ]; then
+    echo "Wrong java version. 1.6 is required."
+    exit
+fi
+
+# check debug flag and set up remote debugging
+if [ -n "${TF_DEBUG}" ]; then
+  if [ -z "${TF_DEBUG_PORT}" ]; then
+    TF_DEBUG_PORT=10088
+  fi
+  RDBG_FLAG=-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=${TF_DEBUG_PORT}
+fi
+
+
+# check if in Android build env
+if [ ! -z ${ANDROID_BUILD_TOP} ]; then
+    HOST=`uname`
+    if [ "$HOST" == "Linux" ]; then
+        OS="linux-x86"
+    elif [ "$HOST" == "Darwin" ]; then
+        OS="darwin-x86"
+    else
+        echo "Unrecognized OS"
+        exit
+    fi;
+    PTS_ROOT=${ANDROID_BUILD_TOP}/out/host/${OS}/pts
+    if [ ! -d ${PTS_ROOT} ]; then
+        echo "Could not find $PTS_ROOT in Android build environment. Try 'make pts'"
+        exit
+    fi;
+fi;
+
+if [ -z ${PTS_ROOT} ]; then
+    # assume we're in an extracted pts install
+    PTS_ROOT="$(dirname $0)/../.."
+fi;
+
+JAR_DIR=${PTS_ROOT}/android-pts/tools
+JARS="ddmlib-prebuilt.jar tradefed-prebuilt.jar hosttestlib.jar cts-tradefed.jar"
+
+for JAR in $JARS; do
+    checkFile ${JAR_DIR}/${JAR}
+    JAR_PATH=${JAR_PATH}:${JAR_DIR}/${JAR}
+done
+
+java $RDBG_FLAG \
+  -cp ${JAR_PATH} -DCTS_ROOT=${PTS_ROOT} -DPTS=1 com.android.cts.tradefed.command.CtsConsole "$@"
+
diff --git a/tools/cts-java-scanner/src/com/android/cts/javascanner/DocletRunner.java b/tools/cts-java-scanner/src/com/android/cts/javascanner/DocletRunner.java
index 1039b17..592b145 100644
--- a/tools/cts-java-scanner/src/com/android/cts/javascanner/DocletRunner.java
+++ b/tools/cts-java-scanner/src/com/android/cts/javascanner/DocletRunner.java
@@ -69,6 +69,8 @@
         sourcePath.add("./development/tools/hosttestlib/src");
         sourcePath.add("./libcore/dalvik/src/main/java");
         sourcePath.add("./cts/tests/src");
+        // PTS adds PtsAndroidTestCase
+        sourcePath.add("./cts/suite/pts/deviceTests/ptsutil/src");
         sourcePath.add(sourceDir.toString());
         return join(sourcePath, ":");
     }
diff --git a/tools/tradefed-host/res/report/cts_result.xsl b/tools/tradefed-host/res/report/cts_result.xsl
index fd2b732..3dfd893 100644
--- a/tools/tradefed-host/res/report/cts_result.xsl
+++ b/tools/tradefed-host/res/report/cts_result.xsl
@@ -461,7 +461,7 @@
                         <TR>
                             <TH width="30%">Test</TH>
                             <TH width="5%">Result</TH>
-                            <TH>Failure Details</TH>
+                            <TH>Failure Details / Details</TH>
                         </TR>
 
                         <!-- test case -->
@@ -526,6 +526,11 @@
                                                             <xsl:value-of select="@result"/>
                                                         </div>
                                                     </TD>
+                                                    <TD class="failuredetails">
+                                                        <div class="details">
+                                                            <xsl:value-of select="FailedScene/@message"/>
+                                                        </div>
+                                                    </TD>
                                                     <TD class="failuredetails"></TD>
                                                 </xsl:if>
 
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildHelper.java b/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildHelper.java
index 8029b75..3c46d9b 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildHelper.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildHelper.java
@@ -30,11 +30,17 @@
 public class CtsBuildHelper {
 
     static final String CTS_DIR_NAME = "android-cts";
+    static final String PTS_DIR_NAME = "android-pts";
+    static private boolean mCtsMode = true;
     /** The root location of the extracted CTS package */
     private final File mRootDir;
     /** the {@link CTS_DIR_NAME} directory */
     private final File mCtsDir;
 
+    public static void changeToPtsMode() {
+        mCtsMode = false;
+    }
+
     /**
      * Creates a {@link CtsBuildHelper}.
      *
@@ -43,7 +49,7 @@
      */
     public CtsBuildHelper(File rootDir) {
         mRootDir = rootDir;
-        mCtsDir = new File(mRootDir, CTS_DIR_NAME);
+        mCtsDir = new File(mRootDir, mCtsMode ? CTS_DIR_NAME : PTS_DIR_NAME);
     }
 
     /**
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/command/CtsConsole.java b/tools/tradefed-host/src/com/android/cts/tradefed/command/CtsConsole.java
index a815a48..8e59cc5 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/command/CtsConsole.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/command/CtsConsole.java
@@ -246,6 +246,11 @@
     }
 
     public static void main(String[] args) throws InterruptedException {
+        // change to PTS mode before anything else
+        String ptsMode = System.getProperty("PTS");
+        if ((ptsMode != null) && ptsMode.equals("1")) {
+            CtsBuildHelper.changeToPtsMode();
+        }
         Console console = new CtsConsole();
         Console.startConsole(console, args);
     }
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/result/CtsXmlResultReporter.java b/tools/tradefed-host/src/com/android/cts/tradefed/result/CtsXmlResultReporter.java
index 686c90a..839b34d 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/result/CtsXmlResultReporter.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/result/CtsXmlResultReporter.java
@@ -44,6 +44,8 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /**
  * Writes results to an XML files in the CTS format.
@@ -96,6 +98,9 @@
 
     private File mLogDir;
 
+    private static final String PTS_PERFORMANCE_EXCEPTION = "com.android.pts.util.PtsException";
+    private static final Pattern mPtsLogPattern = Pattern.compile(
+            "com\\.android\\.pts\\.util\\.PtsException:\\s(.*)");
     public void setReportDir(File reportDir) {
         mReportDir = reportDir;
     }
@@ -216,7 +221,20 @@
      */
     @Override
     public void testFailed(TestFailure status, TestIdentifier test, String trace) {
-        mCurrentPkgResult.reportTestFailure(test, CtsTestStatus.FAIL, trace);
+        if (trace.startsWith(PTS_PERFORMANCE_EXCEPTION)) { //PTS result
+            Test tst = mCurrentPkgResult.findTest(test);
+            // this exception is always thrown as exception is thrown from tearDown.
+            // Just ignore it.
+            if (tst.getName().endsWith("testAndroidTestCaseSetupProperly")) {
+                return;
+            }
+            Matcher m = mPtsLogPattern.matcher(trace);
+            if (m.find()) {
+                mCurrentPkgResult.reportPerformanceResult(test, CtsTestStatus.PASS, m.group(1));
+            }
+        } else {
+            mCurrentPkgResult.reportTestFailure(test, CtsTestStatus.FAIL, trace);
+        }
     }
 
     /**
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/result/Test.java b/tools/tradefed-host/src/com/android/cts/tradefed/result/Test.java
index 3c78df7..df1d4f1 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/result/Test.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/result/Test.java
@@ -84,6 +84,10 @@
         return mMessage;
     }
 
+    public void setMessage(String message) {
+        mMessage = message;
+    }
+
     public String getStartTime() {
         return mStartTime;
     }
@@ -125,14 +129,20 @@
         serializer.attribute(CtsXmlResultReporter.ns, ENDTIME_ATTR, mEndTime);
 
         if (mMessage != null) {
-            serializer.startTag(CtsXmlResultReporter.ns, SCENE_TAG);
-            serializer.attribute(CtsXmlResultReporter.ns, MESSAGE_ATTR, mMessage);
-            if (mStackTrace != null) {
-                serializer.startTag(CtsXmlResultReporter.ns, STACK_TAG);
-                serializer.text(mStackTrace);
-                serializer.endTag(CtsXmlResultReporter.ns, STACK_TAG);
+            if (mResult == CtsTestStatus.PASS) { // PTS will add performance result
+                serializer.startTag(CtsXmlResultReporter.ns, SCENE_TAG);
+                serializer.attribute(CtsXmlResultReporter.ns, MESSAGE_ATTR, mMessage);
+                serializer.endTag(CtsXmlResultReporter.ns, SCENE_TAG);
+            } else {
+                serializer.startTag(CtsXmlResultReporter.ns, SCENE_TAG);
+                serializer.attribute(CtsXmlResultReporter.ns, MESSAGE_ATTR, mMessage);
+                if (mStackTrace != null) {
+                    serializer.startTag(CtsXmlResultReporter.ns, STACK_TAG);
+                    serializer.text(mStackTrace);
+                    serializer.endTag(CtsXmlResultReporter.ns, STACK_TAG);
+                }
+                serializer.endTag(CtsXmlResultReporter.ns, SCENE_TAG);
             }
-            serializer.endTag(CtsXmlResultReporter.ns, SCENE_TAG);
         }
         serializer.endTag(CtsXmlResultReporter.ns, TAG);
     }
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/result/TestPackageResult.java b/tools/tradefed-host/src/com/android/cts/tradefed/result/TestPackageResult.java
index 23590e7..5997ec8 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/result/TestPackageResult.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/result/TestPackageResult.java
@@ -275,6 +275,18 @@
     }
 
     /**
+     * report performance result
+     * @param test
+     * @param status
+     * @param perf
+     */
+    public void reportPerformanceResult(TestIdentifier test, CtsTestStatus status, String perf) {
+        Test result = findTest(test);
+        result.setResultStatus(status);
+        result.setMessage(perf);
+    }
+
+    /**
      * Report that the given test has completed.
      *
      * @param test
diff --git a/tools/utils/buildCts.py b/tools/utils/buildCts.py
index ada706c..77e93b1 100755
--- a/tools/utils/buildCts.py
+++ b/tools/utils/buildCts.py
@@ -54,20 +54,24 @@
 
   def __init__(self, argv):
     """Initialize the CtsBuilder from command line arguments."""
-    if not len(argv) == 6:
-      print 'Usage: %s <testRoot> <ctsOutputDir> <tempDir> <androidRootDir> <docletPath>' % argv[0]
+    if not (len(argv) == 6 or len(argv)==7):
+      print 'Usage: %s <testRoot> <ctsOutputDir> <tempDir> <androidRootDir> <docletPath> [-pts]' % argv[0]
       print ''
       print 'testRoot:       Directory under which to search for CTS tests.'
       print 'ctsOutputDir:   Directory in which the CTS repository should be created.'
       print 'tempDir:        Directory to use for storing temporary files.'
       print 'androidRootDir: Root directory of the Android source tree.'
       print 'docletPath:     Class path where the DescriptionGenerator doclet can be found.'
+      print '-pts:           generate plan for PTS.'
       sys.exit(1)
     self.test_root = sys.argv[1]
     self.out_dir = sys.argv[2]
     self.temp_dir = sys.argv[3]
     self.android_root = sys.argv[4]
     self.doclet_path = sys.argv[5]
+    self.isCts = True
+    if len(argv) ==7 and sys.argv[6] == "-pts":
+      self.isCts = False
 
     self.test_repository = os.path.join(self.out_dir, 'repository/testcases')
     self.plan_repository = os.path.join(self.out_dir, 'repository/plans')
@@ -99,6 +103,13 @@
       doc = tools.XmlFile(description)
       packages.append(doc.GetAttr('TestPackage', 'appPackageName'))
 
+    if not self.isCts: # PTS
+      plan = tools.TestPlan(packages)
+      plan.Include('.*')
+      plan.Exclude(r'android\.tests\.sigtest')
+      self.__WritePlan(plan, 'PTS')
+      return
+
     plan = tools.TestPlan(packages)
     plan.Exclude('android\.performance.*')
     self.__WritePlan(plan, 'CTS')
@@ -158,3 +169,4 @@
   if result != 0:
     sys.exit(result)
   builder.GenerateTestPlans()
+