New framework performance tests.

Change-Id: Ib8cc6704f508b7516ba0a30aa71bb88ef028691d
diff --git a/tests/FrameworkPerf/Android.mk b/tests/FrameworkPerf/Android.mk
new file mode 100644
index 0000000..03893d6
--- /dev/null
+++ b/tests/FrameworkPerf/Android.mk
@@ -0,0 +1,12 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := FrameworkPerf
+
+LOCAL_AAPT_FLAGS = -c 120dpi,240dpi,160dpi,161dpi,320dpi,nodpi
+
+include $(BUILD_PACKAGE)
diff --git a/tests/FrameworkPerf/AndroidManifest.xml b/tests/FrameworkPerf/AndroidManifest.xml
new file mode 100644
index 0000000..f69d550
--- /dev/null
+++ b/tests/FrameworkPerf/AndroidManifest.xml
@@ -0,0 +1,17 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.frameworkperf">
+    <uses-permission android:name="android.permission.WAKE_LOCK" />
+    <uses-sdk android:minSdkVersion="5" />
+
+    <application>
+        <activity android:name="FrameworkPerfActivity" android:label="Framework Perf">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        <service android:name="SchedulerService">
+        </service>
+    </application>
+</manifest>
diff --git a/tests/FrameworkPerf/res/drawable-161dpi/stat_sample_scale.png b/tests/FrameworkPerf/res/drawable-161dpi/stat_sample_scale.png
new file mode 100755
index 0000000..6c9ba0a
--- /dev/null
+++ b/tests/FrameworkPerf/res/drawable-161dpi/stat_sample_scale.png
Binary files differ
diff --git a/tests/FrameworkPerf/res/drawable-161dpi/wallpaper_goldengate_scale.jpg b/tests/FrameworkPerf/res/drawable-161dpi/wallpaper_goldengate_scale.jpg
new file mode 100644
index 0000000..2271091
--- /dev/null
+++ b/tests/FrameworkPerf/res/drawable-161dpi/wallpaper_goldengate_scale.jpg
Binary files differ
diff --git a/tests/FrameworkPerf/res/drawable-hdpi/stat_happy.png b/tests/FrameworkPerf/res/drawable-hdpi/stat_happy.png
new file mode 100755
index 0000000..27f5bb7
--- /dev/null
+++ b/tests/FrameworkPerf/res/drawable-hdpi/stat_happy.png
Binary files differ
diff --git a/tests/FrameworkPerf/res/drawable-mdpi/stat_happy.png b/tests/FrameworkPerf/res/drawable-mdpi/stat_happy.png
new file mode 100644
index 0000000..3a8791b
--- /dev/null
+++ b/tests/FrameworkPerf/res/drawable-mdpi/stat_happy.png
Binary files differ
diff --git a/tests/FrameworkPerf/res/drawable-nodpi/stat_sample.png b/tests/FrameworkPerf/res/drawable-nodpi/stat_sample.png
new file mode 100755
index 0000000..6c9ba0a
--- /dev/null
+++ b/tests/FrameworkPerf/res/drawable-nodpi/stat_sample.png
Binary files differ
diff --git a/tests/FrameworkPerf/res/drawable-nodpi/wallpaper_goldengate.jpg b/tests/FrameworkPerf/res/drawable-nodpi/wallpaper_goldengate.jpg
new file mode 100644
index 0000000..2271091
--- /dev/null
+++ b/tests/FrameworkPerf/res/drawable-nodpi/wallpaper_goldengate.jpg
Binary files differ
diff --git a/tests/FrameworkPerf/res/layout/main.xml b/tests/FrameworkPerf/res/layout/main.xml
new file mode 100644
index 0000000..2e9c500
--- /dev/null
+++ b/tests/FrameworkPerf/res/layout/main.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    >
+
+    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        >
+        <Button android:id="@+id/start"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/start"
+            />
+        <Button android:id="@+id/stop"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/stop"
+            />
+    </LinearLayout>
+
+    <ScrollView android:id="@+id/scroll"
+        android:layout_width="match_parent"
+        android:layout_height="0px"
+        android:layout_weight="1"
+        >
+        <TextView android:id="@+id/log"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="25dp"
+            android:textSize="12sp"
+            android:textColor="#ffffffff"
+            />
+    </ScrollView>
+
+</LinearLayout>
diff --git a/tests/FrameworkPerf/res/values/attrs.xml b/tests/FrameworkPerf/res/values/attrs.xml
new file mode 100644
index 0000000..5823537
--- /dev/null
+++ b/tests/FrameworkPerf/res/values/attrs.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources>
+    <!-- Base attributes that are available to all Item objects. -->
+    <declare-styleable name="MenuItem">
+
+        <!-- The ID of the item. -->
+        <attr name="android:id" />
+
+        <!-- The category applied to the item.
+             (This will be or'ed with the orderInCategory attribute.) -->
+        <attr name="android:menuCategory" />
+
+        <!-- The order within the category applied to the item.
+             (This will be or'ed with the category attribute.) -->
+        <attr name="android:orderInCategory" />
+
+        <!-- The title associated with the item. -->
+        <attr name="android:title" />
+
+        <!-- The condensed title associated with the item.  This is used in situations where the
+             normal title may be too long to be displayed. -->
+        <attr name="android:titleCondensed" />
+
+        <!-- The icon associated with this item.  This icon will not always be shown, so
+             the title should be sufficient in describing this item. -->
+        <attr name="android:icon" />
+
+        <!-- The alphabetic shortcut key.  This is the shortcut when using a keyboard
+             with alphabetic keys. -->
+        <attr name="android:alphabeticShortcut" />
+
+        <!-- The numeric shortcut key.  This is the shortcut when using a numeric (e.g., 12-key)
+             keyboard. -->
+        <attr name="android:numericShortcut" />
+
+        <!-- Whether the item is capable of displaying a check mark. -->
+        <attr name="android:checkable" />
+
+        <!-- Whether the item is checked.  Note that you must first have enabled checking with
+             the checkable attribute or else the check mark will not appear. -->
+        <attr name="android:checked" />
+
+        <!-- Whether the item is shown/visible. -->
+        <attr name="android:visible" />
+
+        <!-- Whether the item is enabled. -->
+        <attr name="android:enabled" />
+    </declare-styleable>
+</resources>
diff --git a/tests/FrameworkPerf/res/values/strings.xml b/tests/FrameworkPerf/res/values/strings.xml
new file mode 100644
index 0000000..82fd713
--- /dev/null
+++ b/tests/FrameworkPerf/res/values/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources>
+    <string name="start">Start</string>
+    <string name="stop">Stop</string>
+    <string name="data_usage_menu_roaming">Data roaming</string>
+    <string name="data_usage_menu_restrict_background">Restrict background data</string>
+    <string name="data_usage_menu_split_4g">Separate 4G usage</string>
+    <string name="data_usage_menu_show_wifi">Show Wi-Fi usage</string>
+    <string name="data_usage_menu_show_ethernet">Show Ethernet usage</string>
+</resources>
diff --git a/tests/FrameworkPerf/res/xml/simple.xml b/tests/FrameworkPerf/res/xml/simple.xml
new file mode 100644
index 0000000..4e938aa
--- /dev/null
+++ b/tests/FrameworkPerf/res/xml/simple.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:id="@+id/data_usage_menu_roaming"
+        android:title="@string/data_usage_menu_roaming"
+        android:checkable="true" />
+    <item
+        android:id="@+id/data_usage_menu_restrict_background"
+        android:title="@string/data_usage_menu_restrict_background"
+        android:checkable="true" />
+    <item
+        android:id="@+id/data_usage_menu_split_4g"
+        android:title="@string/data_usage_menu_split_4g"
+        android:checkable="true" />
+    <item
+        android:id="@+id/data_usage_menu_show_wifi"
+        android:title="@string/data_usage_menu_show_wifi"
+        android:checkable="true" />
+    <item
+        android:id="@+id/data_usage_menu_show_ethernet"
+        android:title="@string/data_usage_menu_show_ethernet"
+        android:checkable="true" />
+</menu>
diff --git a/tests/FrameworkPerf/src/com/android/frameworkperf/FrameworkPerfActivity.java b/tests/FrameworkPerf/src/com/android/frameworkperf/FrameworkPerfActivity.java
new file mode 100644
index 0000000..9f05a55
--- /dev/null
+++ b/tests/FrameworkPerf/src/com/android/frameworkperf/FrameworkPerfActivity.java
@@ -0,0 +1,694 @@
+/*
+ * 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.
+ */
+
+package com.android.frameworkperf;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.PowerManager;
+import android.os.Process;
+import android.os.SystemClock;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+
+/**
+ * So you thought sync used up your battery life.
+ */
+public class FrameworkPerfActivity extends Activity {
+    final Handler mHandler = new Handler();
+
+    TextView mLog;
+    PowerManager.WakeLock mPartialWakeLock;
+
+    long mMaxRunTime = 5000;
+    boolean mStarted;
+
+    final TestRunner mRunner = new TestRunner();
+
+    final Op[] mOpPairs = new Op[] {
+            new MethodCallOp(), new NoOp(),
+            new MethodCallOp(), new CpuOp(),
+            new MethodCallOp(), new SchedulerOp(),
+            new MethodCallOp(), new GcOp(),
+            new SchedulerOp(), new SchedulerOp(),
+            new GcOp(), new NoOp(),
+            new IpcOp(), new NoOp(),
+            new IpcOp(), new CpuOp(),
+            new IpcOp(), new SchedulerOp(),
+            new IpcOp(), new GcOp(),
+            new ParseXmlResOp(), new NoOp(),
+            new ParseLargeXmlResOp(), new NoOp(),
+            new LayoutInflaterOp(), new NoOp(),
+            new LayoutInflaterLargeOp(), new NoOp(),
+            new LoadSmallBitmapOp(), new NoOp(),
+            new LoadLargeBitmapOp(), new NoOp(),
+            new LoadSmallScaledBitmapOp(), new NoOp(),
+            new LoadLargeScaledBitmapOp(), new NoOp(),
+    };
+
+    final Op[] mAvailOps = new Op[] {
+            new NoOp(),
+            new CpuOp(),
+            new SchedulerOp(),
+            new MethodCallOp(),
+            new IpcOp(),
+            new ParseXmlResOp(),
+            new ParseLargeXmlResOp(),
+            new LoadSmallBitmapOp(),
+            new LoadLargeBitmapOp(),
+            new LoadSmallScaledBitmapOp(),
+            new LoadLargeScaledBitmapOp(),
+    };
+    
+    int mCurOpIndex = 0;
+
+    class RunResult {
+        final String name;
+        final String fgLongName;
+        final String bgLongName;
+        final long fgTime;
+        final long fgOps;
+        final long bgTime;
+        final long bgOps;
+
+        RunResult(TestRunner op) {
+            name = op.getName();
+            fgLongName = op.getForegroundLongName();
+            bgLongName = op.getBackgroundLongName();
+            fgTime = op.getForegroundTime();
+            fgOps = op.getForegroundOps();
+            bgTime = op.getBackgroundTime();
+            bgOps = op.getBackgroundOps();
+        }
+
+        float getFgMsPerOp() {
+            return fgOps != 0 ? (fgTime / (float)fgOps) : 0;
+        }
+
+        float getBgMsPerOp() {
+            return bgOps != 0 ? (bgTime / (float)bgOps) : 0;
+        }
+    }
+
+    final ArrayList<RunResult> mResults = new ArrayList<RunResult>();
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        // Set the layout for this activity.  You can find it
+        // in res/layout/hello_activity.xml
+        setContentView(R.layout.main);
+
+        findViewById(R.id.start).setOnClickListener(new View.OnClickListener() {
+            @Override public void onClick(View v) {
+                startRunning();
+            }
+        });
+        findViewById(R.id.stop).setOnClickListener(new View.OnClickListener() {
+            @Override public void onClick(View v) {
+                stopRunning();
+            }
+        });
+        mLog = (TextView)findViewById(R.id.log);
+
+        PowerManager pm = (PowerManager)getSystemService(POWER_SERVICE);
+        mPartialWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Scheduler");
+        mPartialWakeLock.setReferenceCounted(false);
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        stopRunning();
+        if (mPartialWakeLock.isHeld()) {
+            mPartialWakeLock.release();
+        }
+    }
+
+    void startCurOp() {
+        mRunner.run(mHandler, mOpPairs[mCurOpIndex], mOpPairs[mCurOpIndex+1], new Runnable() {
+            @Override public void run() {
+                RunResult result = new RunResult(mRunner);
+                log(String.format("%s: fg=%d*%gms/op (%dms) / bg=%d*%gms/op (%dms)",
+                        result.name, result.fgOps, result.getFgMsPerOp(), result.fgTime,
+                        result.bgOps, result.getBgMsPerOp(), result.bgTime));
+                mResults.add(result);
+                if (!mStarted) {
+                    log("Stop");
+                    stopRunning();
+                    return;
+                }
+                mCurOpIndex+=2;
+                if (mCurOpIndex >= mOpPairs.length) {
+                    log("Finished");
+                    stopRunning();
+                    return;
+                }
+                startCurOp();
+            }
+        });
+    }
+
+    void startRunning() {
+        if (!mStarted) {
+            log("Start");
+            mStarted = true;
+            updateWakeLock();
+            startService(new Intent(this, SchedulerService.class));
+            mCurOpIndex = 0;
+            mResults.clear();
+            startCurOp();
+        }
+    }
+
+    void stopRunning() {
+        if (mStarted) {
+            mStarted = false;
+            updateWakeLock();
+            stopService(new Intent(this, SchedulerService.class));
+            for (int i=0; i<mResults.size(); i++) {
+                RunResult result = mResults.get(i);
+                float fgMsPerOp = result.getFgMsPerOp();
+                float bgMsPerOp = result.getBgMsPerOp();
+                String fgMsPerOpStr = fgMsPerOp != 0 ? Float.toString(fgMsPerOp) : "";
+                String bgMsPerOpStr = bgMsPerOp != 0 ? Float.toString(bgMsPerOp) : "";
+                Log.i("Perf", "\t" + result.name + "\t" + result.fgOps
+                        + "\t" + result.getFgMsPerOp() + "\t" + result.fgTime
+                        + "\t" + result.fgLongName + "\t" + result.bgOps
+                        + "\t" + result.getBgMsPerOp() + "\t" + result.bgTime
+                        + "\t" + result.bgLongName);
+            }
+        }
+    }
+
+    void updateWakeLock() {
+        if (mStarted) {
+            getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+            if (!mPartialWakeLock.isHeld()) {
+                mPartialWakeLock.acquire();
+            }
+        } else {
+            getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+            if (mPartialWakeLock.isHeld()) {
+                mPartialWakeLock.release();
+            }
+        }
+    }
+
+    void log(String s) {
+        mLog.setText(mLog.getText() + "\n" + s);
+        Log.i("Perf", s);
+    }
+
+    enum BackgroundMode {
+        NOTHING,
+        CPU,
+        SCHEDULER
+    };
+
+    public class TestRunner {
+        Handler mHandler;
+        Op mForegroundOp;
+        Op mBackgroundOp;
+        Runnable mDoneCallback;
+
+        RunnerThread mBackgroundThread;
+        RunnerThread mForegroundThread;
+        long mStartTime;
+
+        boolean mBackgroundRunning;
+        boolean mForegroundRunning;
+
+        long mBackgroundEndTime;
+        long mBackgroundOps;
+        long mForegroundEndTime;
+        long mForegroundOps;
+
+        public TestRunner() {
+        }
+
+        public String getForegroundName() {
+            return mForegroundOp.getName();
+        }
+
+        public String getBackgroundName() {
+            return mBackgroundOp.getName();
+        }
+
+        public String getName() {
+            String fgName = mForegroundOp.getName();
+            String bgName = mBackgroundOp.getName();
+            StringBuilder res = new StringBuilder();
+            if (fgName != null) {
+                res.append(fgName);
+                res.append("Fg");
+            }
+            if (bgName != null) {
+                res.append(bgName);
+                res.append("Bg");
+            }
+            return res.toString();
+        }
+
+        public String getForegroundLongName() {
+            return mForegroundOp.getLongName();
+        }
+
+        public String getBackgroundLongName() {
+            return mBackgroundOp.getLongName();
+        }
+
+        public void run(Handler handler, Op foreground, Op background, Runnable doneCallback) {
+            mHandler = handler;
+            mForegroundOp = foreground;
+            mBackgroundOp = background;
+            mDoneCallback = doneCallback;
+            mBackgroundThread = new RunnerThread("background", new Runnable() {
+                @Override public void run() {
+                    boolean running;
+                    int ops = 0;
+                    do {
+                        running = mBackgroundOp.onRun();
+                        ops++;
+                    } while (evalRepeat(running, true) && running);
+                    mBackgroundEndTime = SystemClock.uptimeMillis();
+                    mBackgroundOps = ops * mBackgroundOp.getOpsPerRun();
+                    threadFinished(false);
+                }
+            }, Process.THREAD_PRIORITY_BACKGROUND);
+            mForegroundThread = new RunnerThread("background", new Runnable() {
+                @Override public void run() {
+                    boolean running;
+                    int ops = 0;
+                    do {
+                        running = mForegroundOp.onRun();
+                        ops++;
+                    } while (evalRepeat(true, running) && running);
+                    mForegroundEndTime = SystemClock.uptimeMillis();
+                    mForegroundOps = ops * mForegroundOp.getOpsPerRun();
+                    threadFinished(true);
+                }
+            }, Process.THREAD_PRIORITY_FOREGROUND);
+
+            mForegroundOp.onInit(FrameworkPerfActivity.this);
+            mBackgroundOp.onInit(FrameworkPerfActivity.this);
+
+            synchronized (this) {
+                mStartTime = SystemClock.uptimeMillis();
+                mBackgroundRunning = true;
+                mForegroundRunning = true;
+            }
+
+            mBackgroundThread.start();
+            mForegroundThread.start();
+        }
+
+        public long getForegroundTime() {
+            return mForegroundEndTime-mStartTime;
+        }
+
+        public long getForegroundOps() {
+            return mForegroundOps;
+        }
+
+        public long getBackgroundTime() {
+            return mBackgroundEndTime-mStartTime;
+        }
+
+        public long getBackgroundOps() {
+            return mBackgroundOps;
+        }
+
+        private boolean evalRepeat(boolean bgRunning, boolean fgRunning) {
+            synchronized (this) {
+                if (!bgRunning) {
+                    mBackgroundRunning = false;
+                }
+                if (!fgRunning) {
+                    mForegroundRunning = false;
+                }
+                if (!mBackgroundRunning && !mForegroundRunning) {
+                    return false;
+                }
+                long now = SystemClock.uptimeMillis();
+                if (now > (mStartTime+mMaxRunTime)) {
+                    return false;
+                }
+                return true;
+            }
+        }
+
+        private void threadFinished(boolean foreground) {
+            synchronized (this) {
+                if (foreground) {
+                    mForegroundRunning = false;
+                } else {
+                    mBackgroundRunning = false;
+                }
+                if (!mBackgroundRunning && !mForegroundRunning) {
+                    mHandler.post(new Runnable() {
+                        @Override public void run() {
+                            if (mDoneCallback != null) {
+                                mDoneCallback.run();
+                            }
+                        }
+                    });
+                }
+            }
+        }
+    }
+
+    class RunnerThread extends Thread {
+        private final Runnable mOp;
+        private final int mPriority;
+
+        RunnerThread(String name, Runnable op, int priority) {
+            super(name);
+            mOp = op;
+            mPriority = priority;
+        }
+
+        public void run() {
+            Process.setThreadPriority(mPriority);
+            mOp.run();
+        }
+    }
+
+    static public abstract class Op {
+        final String mName;
+        final String mLongName;
+
+        public Op(String name, String longName) {
+            mName = name;
+            mLongName = longName;
+        }
+
+        public String getName() {
+            return mName;
+        }
+
+        public String getLongName() {
+            return mLongName;
+        }
+
+        void onInit(Context context) {
+        }
+
+        abstract boolean onRun();
+
+        int getOpsPerRun() {
+            return 1;
+        }
+    }
+
+    static class NoOp extends Op {
+        NoOp() {
+            super(null, "Nothing");
+        }
+
+        boolean onRun() {
+            return false;
+        }
+
+        int getOpsPerRun() {
+            return 0;
+        }
+    }
+
+    static class CpuOp extends Op {
+        CpuOp() {
+            super("CPU", "Consume CPU");
+        }
+
+        boolean onRun() {
+            return true;
+        }
+    }
+
+    static class SchedulerOp extends Op {
+        SchedulerOp() {
+            super("Sched", "Change scheduler group");
+        }
+
+        boolean onRun() {
+            Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
+            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+            return true;
+        }
+    }
+
+    static class GcOp extends Op {
+        GcOp() {
+            super("Gc", "Run garbage collector");
+        }
+
+        boolean onRun() {
+            byte[] stuff = new byte[1024*1024];
+            return true;
+        }
+    }
+
+    static class MethodCallOp extends Op {
+        MethodCallOp() {
+            super("MethodCall", "Method call");
+        }
+
+        boolean onRun() {
+            final int N = getOpsPerRun();
+            for (int i=0; i<N; i++) {
+                someFunc(i);
+            }
+            return true;
+        }
+
+        int someFunc(int foo) {
+            return 0;
+        }
+
+        int getOpsPerRun() {
+            return 500;
+        }
+    }
+
+    static class IpcOp extends Op {
+        PackageManager mPm;
+        String mProcessName;
+
+        IpcOp() {
+            super("Ipc", "IPC to system process");
+        }
+
+        void onInit(Context context) {
+            mPm = context.getPackageManager();
+            mProcessName = context.getApplicationInfo().processName;
+        }
+
+        boolean onRun() {
+            final int N = getOpsPerRun();
+            for (int i=0; i<N; i++) {
+                mPm.queryContentProviders(mProcessName, Process.myUid(), 0);
+            }
+            return true;
+        }
+
+        int getOpsPerRun() {
+            return 100;
+        }
+    }
+
+    static class ParseXmlResOp extends Op {
+        Context mContext;
+
+        ParseXmlResOp() {
+            super("ParseXmlRes", "Parse compiled XML resource");
+        }
+
+        void onInit(Context context) {
+            mContext = context;
+        }
+
+        boolean onRun() {
+            SimpleInflater inf = new SimpleInflater(mContext);
+            inf.inflate(R.xml.simple);
+            return true;
+        }
+    }
+
+    static class ParseLargeXmlResOp extends Op {
+        Context mContext;
+
+        ParseLargeXmlResOp() {
+            super("ParseLargeXmlRes", "Parse large XML resource");
+        }
+
+        void onInit(Context context) {
+            mContext = context;
+        }
+
+        boolean onRun() {
+            SimpleInflater inf = new SimpleInflater(mContext);
+            inf.inflate(R.xml.simple_large);
+            return true;
+        }
+    }
+
+    static class LayoutInflaterOp extends Op {
+        Context mContext;
+
+        LayoutInflaterOp() {
+            super("LayoutInflaterOp", "Inflate layout resource");
+        }
+
+        void onInit(Context context) {
+            mContext = context;
+        }
+
+        boolean onRun() {
+            if (Looper.myLooper() == null) {
+                Looper.prepare();
+            }
+            LayoutInflater inf = (LayoutInflater)mContext.getSystemService(
+                    Context.LAYOUT_INFLATER_SERVICE);
+            inf.inflate(R.layout.small_layout, null);
+            return true;
+        }
+    }
+
+    static class LayoutInflaterLargeOp extends Op {
+        Context mContext;
+
+        LayoutInflaterLargeOp() {
+            super("LayoutInflaterLargeOp", "Inflate large layout resource");
+        }
+
+        void onInit(Context context) {
+            mContext = context;
+        }
+
+        boolean onRun() {
+            if (Looper.myLooper() == null) {
+                Looper.prepare();
+            }
+            LayoutInflater inf = (LayoutInflater)mContext.getSystemService(
+                    Context.LAYOUT_INFLATER_SERVICE);
+            inf.inflate(R.layout.large_layout, null);
+            return true;
+        }
+    }
+
+    static class LoadSmallBitmapOp extends Op {
+        Context mContext;
+
+        LoadSmallBitmapOp() {
+            super("LoadSmallBitmap", "Load small raw bitmap");
+        }
+
+        void onInit(Context context) {
+            mContext = context;
+        }
+
+        boolean onRun() {
+            BitmapFactory.Options opts = new BitmapFactory.Options();
+            opts.inScreenDensity = DisplayMetrics.DENSITY_DEVICE;
+            Bitmap bm = BitmapFactory.decodeResource(mContext.getResources(),
+                    R.drawable.stat_sample, opts);
+            bm.recycle();
+            return true;
+        }
+    }
+
+    static class LoadLargeBitmapOp extends Op {
+        Context mContext;
+
+        LoadLargeBitmapOp() {
+            super("LoadLargeBitmap", "Load large raw bitmap");
+        }
+
+        void onInit(Context context) {
+            mContext = context;
+        }
+
+        boolean onRun() {
+            BitmapFactory.Options opts = new BitmapFactory.Options();
+            opts.inScreenDensity = DisplayMetrics.DENSITY_DEVICE;
+            Bitmap bm = BitmapFactory.decodeResource(mContext.getResources(),
+                    R.drawable.wallpaper_goldengate, opts);
+            bm.recycle();
+            return true;
+        }
+    }
+
+    static class LoadSmallScaledBitmapOp extends Op {
+        Context mContext;
+
+        LoadSmallScaledBitmapOp() {
+            super("LoadSmallScaledBitmap", "Load small raw bitmap that is scaled for density");
+        }
+
+        void onInit(Context context) {
+            mContext = context;
+        }
+
+        boolean onRun() {
+            BitmapFactory.Options opts = new BitmapFactory.Options();
+            opts.inScreenDensity = DisplayMetrics.DENSITY_DEVICE;
+            Bitmap bm = BitmapFactory.decodeResource(mContext.getResources(),
+                    R.drawable.stat_sample_scale, opts);
+            bm.recycle();
+            return true;
+        }
+    }
+
+    static class LoadLargeScaledBitmapOp extends Op {
+        Context mContext;
+
+        LoadLargeScaledBitmapOp() {
+            super("LoadLargeScaledBitmap", "Load large raw bitmap that is scaled for density");
+        }
+
+        void onInit(Context context) {
+            mContext = context;
+        }
+
+        boolean onRun() {
+            BitmapFactory.Options opts = new BitmapFactory.Options();
+            opts.inScreenDensity = DisplayMetrics.DENSITY_DEVICE;
+            Bitmap bm = BitmapFactory.decodeResource(mContext.getResources(),
+                    R.drawable.wallpaper_goldengate_scale, opts);
+            bm.recycle();
+            return true;
+        }
+    }
+}
diff --git a/tests/FrameworkPerf/src/com/android/frameworkperf/SchedulerService.java b/tests/FrameworkPerf/src/com/android/frameworkperf/SchedulerService.java
new file mode 100644
index 0000000..7691e64
--- /dev/null
+++ b/tests/FrameworkPerf/src/com/android/frameworkperf/SchedulerService.java
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+package com.android.frameworkperf;
+
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+
+public class SchedulerService extends Service {
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        Notification status = new Notification(R.drawable.stat_happy, null,
+                System.currentTimeMillis());
+        status.flags |= Notification.FLAG_ONGOING_EVENT;
+        status.setLatestEventInfo(this, "Scheduler Test running",
+                "Scheduler Test running", PendingIntent.getActivity(this, 0,
+                    new Intent(this, FrameworkPerfActivity.class)
+                    .setAction(Intent.ACTION_MAIN)
+                    .addCategory(Intent.CATEGORY_LAUNCHER)
+                    .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK), 0));
+        startForeground(1, status);
+        return START_STICKY;
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+}
diff --git a/tests/FrameworkPerf/src/com/android/frameworkperf/SimpleInflater.java b/tests/FrameworkPerf/src/com/android/frameworkperf/SimpleInflater.java
new file mode 100644
index 0000000..5cbb12a
--- /dev/null
+++ b/tests/FrameworkPerf/src/com/android/frameworkperf/SimpleInflater.java
@@ -0,0 +1,147 @@
+/*
+ * 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.
+ */
+
+package com.android.frameworkperf;
+
+import java.io.IOException;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.util.AttributeSet;
+import android.util.Xml;
+import android.view.InflateException;
+
+public class SimpleInflater {
+    /** Menu tag name in XML. */
+    private static final String XML_MENU = "menu";
+    
+    /** Group tag name in XML. */
+    private static final String XML_GROUP = "group";
+    
+    /** Item tag name in XML. */
+    private static final String XML_ITEM = "item";
+
+    private Context mContext;
+
+    public SimpleInflater(Context context) {
+        mContext = context;
+    }
+
+    public void inflate(int menuRes) {
+        XmlResourceParser parser = null;
+        try {
+            parser = mContext.getResources().getLayout(menuRes);
+            AttributeSet attrs = Xml.asAttributeSet(parser);
+            
+            parseMenu(parser, attrs);
+        } catch (XmlPullParserException e) {
+            throw new InflateException("Error inflating menu XML", e);
+        } catch (IOException e) {
+            throw new InflateException("Error inflating menu XML", e);
+        } finally {
+            if (parser != null) parser.close();
+        }
+    }
+
+    private void parseMenu(XmlPullParser parser, AttributeSet attrs)
+            throws XmlPullParserException, IOException {
+        int eventType = parser.getEventType();
+        String tagName;
+        boolean lookingForEndOfUnknownTag = false;
+        String unknownTagName = null;
+
+        // This loop will skip to the menu start tag
+        do {
+            if (eventType == XmlPullParser.START_TAG) {
+                tagName = parser.getName();
+                if (tagName.equals(XML_MENU)) {
+                    // Go to next tag
+                    eventType = parser.next();
+                    break;
+                }
+                
+                throw new RuntimeException("Expecting menu, got " + tagName);
+            }
+            eventType = parser.next();
+        } while (eventType != XmlPullParser.END_DOCUMENT);
+        
+        boolean reachedEndOfMenu = false;
+        while (!reachedEndOfMenu) {
+            switch (eventType) {
+                case XmlPullParser.START_TAG:
+                    if (lookingForEndOfUnknownTag) {
+                        break;
+                    }
+                    
+                    tagName = parser.getName();
+                    if (tagName.equals(XML_ITEM)) {
+                        readItem(attrs);
+                    } else if (tagName.equals(XML_MENU)) {
+                        parseMenu(parser, attrs);
+                    } else {
+                        lookingForEndOfUnknownTag = true;
+                        unknownTagName = tagName;
+                    }
+                    break;
+                    
+                case XmlPullParser.END_TAG:
+                    tagName = parser.getName();
+                    if (lookingForEndOfUnknownTag && tagName.equals(unknownTagName)) {
+                        lookingForEndOfUnknownTag = false;
+                        unknownTagName = null;
+                    } else if (tagName.equals(XML_ITEM)) {
+                    } else if (tagName.equals(XML_MENU)) {
+                        reachedEndOfMenu = true;
+                    }
+                    break;
+                    
+                case XmlPullParser.END_DOCUMENT:
+                    throw new RuntimeException("Unexpected end of document");
+            }
+            
+            eventType = parser.next();
+        }
+    }
+
+    public void readItem(AttributeSet attrs) {
+        TypedArray a = mContext.obtainStyledAttributes(attrs,
+                com.android.internal.R.styleable.MenuItem);
+
+        // Inherit attributes from the group as default value
+        int itemId = a.getResourceId(R.styleable.MenuItem_android_id, 0);
+        final int category = a.getInt(R.styleable.MenuItem_android_menuCategory, 0);
+        final int order = a.getInt(R.styleable.MenuItem_android_orderInCategory, 0);
+        CharSequence itemTitle = a.getText(R.styleable.MenuItem_android_title);
+        CharSequence itemTitleCondensed = a.getText(R.styleable.MenuItem_android_titleCondensed);
+        int itemIconResId = a.getResourceId(R.styleable.MenuItem_android_icon, 0);
+        String itemAlphabeticShortcut = a.getString(R.styleable.MenuItem_android_alphabeticShortcut);
+        String itemNumericShortcut = a.getString(R.styleable.MenuItem_android_numericShortcut);
+        int itemCheckable = 0;
+        if (a.hasValue(R.styleable.MenuItem_android_checkable)) {
+            // Item has attribute checkable, use it
+            itemCheckable = a.getBoolean(R.styleable.MenuItem_android_checkable, false) ? 1 : 0;
+        }
+        boolean itemChecked = a.getBoolean(R.styleable.MenuItem_android_checked, false);
+        boolean itemVisible = a.getBoolean(R.styleable.MenuItem_android_visible, false);
+        boolean itemEnabled = a.getBoolean(R.styleable.MenuItem_android_enabled, false);
+
+        a.recycle();
+    }
+}