Merge change 27217 into eclair

* changes:
  XML Schema for layout configurations.
diff --git a/samples/ApiDemos/AndroidManifest.xml b/samples/ApiDemos/AndroidManifest.xml
index e28a096..5eb8ccb 100644
--- a/samples/ApiDemos/AndroidManifest.xml
+++ b/samples/ApiDemos/AndroidManifest.xml
@@ -108,6 +108,13 @@
             </intent-filter>
         </activity>
 
+        <activity android:name=".app.Animation" android:label="@string/activity_animation">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
+
         <activity android:name=".app.SaveRestoreState"
                 android:label="@string/activity_save_restore"
                 android:windowSoftInputMode="stateVisible|adjustResize">
diff --git a/samples/ApiDemos/res/anim/fade.xml b/samples/ApiDemos/res/anim/fade.xml
index 78b193d..617c5f2 100644
--- a/samples/ApiDemos/res/anim/fade.xml
+++ b/samples/ApiDemos/res/anim/fade.xml
@@ -16,4 +16,5 @@
 
 <alpha xmlns:android="http://schemas.android.com/apk/res/android"
        android:interpolator="@android:anim/accelerate_interpolator"
-       android:fromAlpha="0.0" android:toAlpha="1.0" android:duration="100" />
+       android:fromAlpha="0.0" android:toAlpha="1.0"
+       android:duration="@android:integer/config_longAnimTime" />
diff --git a/samples/ApiDemos/res/anim/hold.xml b/samples/ApiDemos/res/anim/hold.xml
new file mode 100644
index 0000000..65fbacf
--- /dev/null
+++ b/samples/ApiDemos/res/anim/hold.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+
+<translate xmlns:android="http://schemas.android.com/apk/res/android"
+       android:interpolator="@android:anim/accelerate_interpolator"
+       android:fromXDelta="0" android:toXDelta="0"
+       android:duration="@android:integer/config_longAnimTime" />
diff --git a/samples/ApiDemos/res/anim/slide_left.xml b/samples/ApiDemos/res/anim/slide_left.xml
index 8760491..0a591b0 100644
--- a/samples/ApiDemos/res/anim/slide_left.xml
+++ b/samples/ApiDemos/res/anim/slide_left.xml
@@ -15,5 +15,6 @@
 -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/accelerate_interpolator">
-    <translate android:fromXDelta="100%p" android:toXDelta="0" android:duration="150" />
+    <translate android:fromXDelta="100%p" android:toXDelta="0"
+        android:duration="@android:integer/config_shortAnimTime" />
 </set>
diff --git a/samples/ApiDemos/res/anim/slide_right.xml b/samples/ApiDemos/res/anim/slide_right.xml
index 7aaacdb..edeed24 100644
--- a/samples/ApiDemos/res/anim/slide_right.xml
+++ b/samples/ApiDemos/res/anim/slide_right.xml
@@ -15,5 +15,6 @@
 -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/accelerate_interpolator">
-    <translate android:fromXDelta="-100%p" android:toXDelta="0" android:duration="150" />
+    <translate android:fromXDelta="-100%p" android:toXDelta="0"
+            android:duration="@android:integer/config_shortAnimTime" />
 </set>
diff --git a/samples/ApiDemos/res/anim/zoom_enter.xml b/samples/ApiDemos/res/anim/zoom_enter.xml
new file mode 100644
index 0000000..7c29852
--- /dev/null
+++ b/samples/ApiDemos/res/anim/zoom_enter.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<!-- Special window zoom animation: this is the element that enters the screen,
+     it starts at 200% and scales down.  Goes with zoom_exit.xml. -->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+        android:interpolator="@android:anim/decelerate_interpolator">
+    <scale android:fromXScale="2.0" android:toXScale="1.0"
+           android:fromYScale="2.0" android:toYScale="1.0"
+           android:pivotX="50%p" android:pivotY="50%p"
+           android:duration="@android:integer/config_mediumAnimTime" />
+</set>
diff --git a/samples/ApiDemos/res/anim/zoom_exit.xml b/samples/ApiDemos/res/anim/zoom_exit.xml
new file mode 100644
index 0000000..29dfe99
--- /dev/null
+++ b/samples/ApiDemos/res/anim/zoom_exit.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<!-- Special window zoom animation: this is the element that exits the
+     screen, it is forced above the entering element and starts at its
+     normal size (filling the screen) and scales down while fading out.
+     This goes with zoom_enter.xml. -->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+        android:interpolator="@android:anim/decelerate_interpolator"
+        android:zAdjustment="top">
+    <scale android:fromXScale="1.0" android:toXScale=".5"
+           android:fromYScale="1.0" android:toYScale=".5"
+           android:pivotX="50%p" android:pivotY="50%p"
+           android:duration="@android:integer/config_mediumAnimTime" />
+    <alpha android:fromAlpha="1.0" android:toAlpha="0"
+            android:duration="@android:integer/config_mediumAnimTime"/>
+</set>
diff --git a/samples/ApiDemos/res/layout/activity_animation.xml b/samples/ApiDemos/res/layout/activity_animation.xml
new file mode 100644
index 0000000..446f735
--- /dev/null
+++ b/samples/ApiDemos/res/layout/activity_animation.xml
@@ -0,0 +1,42 @@
+<?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.
+-->
+
+<!-- Demonstrates starting and stopping a local service.
+     See corresponding Java code com.android.sdk.app.LocalSerice.java. -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:padding="4dip"
+    android:gravity="center_horizontal"
+    android:layout_width="fill_parent" android:layout_height="fill_parent">
+
+    <TextView
+        android:layout_width="fill_parent" android:layout_height="wrap_content"
+        android:layout_weight="0"
+        android:paddingBottom="4dip"
+        android:text="@string/activity_animation_msg"/>
+
+    <Button android:id="@+id/fade_animation"
+        android:layout_width="wrap_content" android:layout_height="wrap_content" 
+        android:text="@string/activity_animation_fade">
+        <requestFocus />
+    </Button>
+
+    <Button android:id="@+id/zoom_animation"
+        android:layout_width="wrap_content" android:layout_height="wrap_content" 
+        android:text="@string/activity_animation_zoom">
+    </Button>
+
+</LinearLayout>
+
diff --git a/samples/ApiDemos/res/values/strings.xml b/samples/ApiDemos/res/values/strings.xml
index 122483e..2800bff 100644
--- a/samples/ApiDemos/res/values/strings.xml
+++ b/samples/ApiDemos/res/values/strings.xml
@@ -47,6 +47,11 @@
 
     <string name="activity_translucent_blur">App/Activity/Translucent Blur</string>
 
+    <string name="activity_animation">App/Activity/Animation</string>
+    <string name="activity_animation_msg">Press a button to launch an activity with a custom animation.</string>
+    <string name="activity_animation_fade">Fade in</string>
+    <string name="activity_animation_zoom">Zoom in</string>
+
     <string name="activity_save_restore">App/Activity/Save &amp; Restore State</string>
     <string name="save_restore_msg">Demonstration of saving and restoring activity state in onSaveInstanceState() and onCreate().</string>
     <string name="saves_state">This text field saves its state:</string>
diff --git a/samples/ApiDemos/src/com/example/android/apis/app/Animation.java b/samples/ApiDemos/src/com/example/android/apis/app/Animation.java
new file mode 100644
index 0000000..90831f5
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/app/Animation.java
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+
+package com.example.android.apis.app;
+
+// Need the following import to get access to the app resources, since this
+// class is in a sub-package.
+import com.example.android.apis.R;
+import com.example.android.apis.view.Controls1;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+
+
+/**
+ * <p>Example of explicitly starting and stopping the {@link LocalService}.
+ * This demonstrates the implementation of a service that runs in the same
+ * process as the rest of the application, which is explicitly started and stopped
+ * as desired.</p>
+ */
+public class Animation extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.activity_animation);
+
+        // Watch for button clicks.
+        Button button = (Button)findViewById(R.id.fade_animation);
+        button.setOnClickListener(mFadeListener);
+        button = (Button)findViewById(R.id.zoom_animation);
+        button.setOnClickListener(mZoomListener);
+    }
+
+    private OnClickListener mFadeListener = new OnClickListener() {
+        public void onClick(View v) {
+            // Request the next activity transition (here starting a new one).
+            startActivity(new Intent(Animation.this, Controls1.class));
+            // Supply a custom animation.  This one will just fade the new
+            // activity on top.  Note that we need to also supply an animation
+            // (here just doing nothing for the same amount of time) for the
+            // old activity to prevent it from going away too soon.
+            overridePendingTransition(R.anim.fade, R.anim.hold);
+        }
+    };
+
+    private OnClickListener mZoomListener = new OnClickListener() {
+        public void onClick(View v) {
+            // Request the next activity transition (here starting a new one).
+            startActivity(new Intent(Animation.this, Controls1.class));
+            // This is a more complicated animation, involving transformations
+            // on both this (exit) and the new (enter) activity.  Note how for
+            // the duration of the animation we force the exiting activity
+            // to be Z-ordered on top (even though it really isn't) to achieve
+            // the effect we want.
+            overridePendingTransition(R.anim.zoom_enter, R.anim.zoom_exit);
+        }
+    };
+}
+
diff --git a/samples/ApiDemos/src/com/example/android/apis/app/LocalService.java b/samples/ApiDemos/src/com/example/android/apis/app/LocalService.java
index 8791578..79324a4 100644
--- a/samples/ApiDemos/src/com/example/android/apis/app/LocalService.java
+++ b/samples/ApiDemos/src/com/example/android/apis/app/LocalService.java
@@ -24,6 +24,7 @@
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.Parcel;
+import android.util.Log;
 import android.widget.Toast;
 
 // Need the following import to get access to the app resources, since this
@@ -64,6 +65,14 @@
     }
 
     @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        Log.i("LocalService", "Received start id " + startId + ": " + intent);
+        // We want this service to continue running until it is explicitly
+        // stopped, so return sticky.
+        return START_STICKY;
+    }
+
+    @Override
     public void onDestroy() {
         // Cancel the persistent notification.
         mNM.cancel(R.string.local_service_started);
diff --git a/testrunner/run_command.py b/testrunner/run_command.py
index 79c7ea5..c522e00 100755
--- a/testrunner/run_command.py
+++ b/testrunner/run_command.py
@@ -84,7 +84,7 @@
       so.append("ERROR")
       error_occurred = True
     if pipe.returncode != 0:
-      logger.SilentLog("Error: %s returned %d error code" %(cmd,
+      logger.SilentLog("Error: %s returned %s error code" %(cmd,
           pipe.returncode))
       error_occurred = True
 
diff --git a/testrunner/runtest.py b/testrunner/runtest.py
index d76399d..e637b71 100755
--- a/testrunner/runtest.py
+++ b/testrunner/runtest.py
@@ -56,6 +56,9 @@
       "The runtest script works in two ways.  You can query it "
       "for a list of tests, or you can launch one or more tests.")
 
+  # default value for make -jX
+  _DEFAULT_JOBS=4
+
   def __init__(self):
     # disable logging of timestamp
     self._root_path = android_build.GetTop()
@@ -78,6 +81,9 @@
                       help="To view the list of tests")
     parser.add_option("-b", "--skip-build", dest="skip_build", default=False,
                       action="store_true", help="Skip build - just launch")
+    parser.add_option("-j", "--jobs", dest="make_jobs",
+                      metavar="X", default=self._DEFAULT_JOBS,
+                      help="Number of make jobs to use when building")
     parser.add_option("-n", "--skip_execute", dest="preview", default=False,
                       action="store_true",
                       help="Do not execute, just preview commands")
@@ -198,18 +204,31 @@
     logger.SilentLog("Building tests...")
     target_set = Set()
     extra_args_set = Set()
-    for test_suite in self._GetTestsToRun():
+    tests = self._GetTestsToRun()
+    for test_suite in tests:
       self._AddBuildTarget(test_suite, target_set, extra_args_set)
 
     if target_set:
       if self._options.coverage:
         coverage.EnableCoverageBuild()
+
+      # hack to build cts dependencies
+      # TODO: remove this when build dependency support added to runtest or
+      # cts dependencies are removed
+      if self._IsCtsTests(tests):
+        # need to use make since these fail building with ONE_SHOT_MAKEFILE
+        cmd=('make -j%s CtsTestStubs android.core.tests.runner' %
+              self._options.make_jobs)
+        logger.Log(cmd)
+        if not self._options.preview:
+          run_command.RunCommand(cmd, return_output=False)
       target_build_string = " ".join(list(target_set))
       extra_args_string = " ".join(list(extra_args_set))
       # mmm cannot be used from python, so perform a similar operation using
       # ONE_SHOT_MAKEFILE
-      cmd = 'ONE_SHOT_MAKEFILE="%s" make -C "%s" files %s' % (
-          target_build_string, self._root_path, extra_args_string)
+      cmd = 'ONE_SHOT_MAKEFILE="%s" make -j%s -C "%s" files %s' % (
+          target_build_string, self._options.make_jobs, self._root_path,
+          extra_args_string)
       logger.Log(cmd)
 
       if self._options.preview:
@@ -254,6 +273,13 @@
       tests.append(test)
     return tests
 
+  def _IsCtsTests(self, test_list):
+    """Check if any cts tests are included in given list of tests to run."""
+    for test in test_list:
+      if test.IsCts():
+        return True
+    return False
+
   def RunTests(self):
     """Main entry method - executes the tests according to command line args."""
     try:
diff --git a/testrunner/test_defs.xml b/testrunner/test_defs.xml
index c1362d1..6d38d1d 100644
--- a/testrunner/test_defs.xml
+++ b/testrunner/test_defs.xml
@@ -137,7 +137,7 @@
 <!--  cts tests -->
 
 <test name="cts-permission"
-    build_path="cts/tests"
+    build_path="cts/tests/tests/permission"
     package="com.android.cts.permission"
     runner="android.test.InstrumentationTestRunner"
     coverage_target="framework"
@@ -153,44 +153,44 @@
     cts="true" />
 
 <test name="cts-process"
-    build_path="cts/tests"
+    build_path="cts/tests/tests/process"
     package="com.android.cts.process"
     coverage_target="framework"
     cts="true" />
 
 <test name="cts-api-signature"
-    build_path="cts/tests"
+    build_path="cts/tests/SignatureTest"
     package="android.tests.sigtest"
     runner=".InstrumentationRunner"
     cts="true" />
 
 <test name="cts-api-signature-func"
-    build_path="cts/tests"
+    build_path="cts/tests/SignatureTest"
     package="android.tests.sigtest.tests"
     cts="true" />
 
 <test name="cts-apidemos"
-    build_path="cts/tests"
+    build_path="cts/tests/ApiDemosReferenceTest"
     package="android.apidemos.cts"
     coverage_target="ApiDemos"
     cts="true" />
 
 <test name="cts-app"
-    build_path="cts/tests"
+    build_path="cts/tests/tests/app"
     package="com.android.cts.app"
     runner="android.test.InstrumentationCtsTestRunner"
     coverage_target="framework"
     cts="true" />
 
 <test name="cts-content"
-    build_path="cts/tests"
+    build_path="cts/tests/tests/content"
     package="com.android.cts.content"
     runner="android.test.InstrumentationCtsTestRunner"
     coverage_target="framework"
     cts="true" />
 
 <test name="cts-database"
-    build_path="cts/tests"
+    build_path="cts/tests/tests/database"
     package="com.android.cts.database"
     runner="android.test.InstrumentationCtsTestRunner"
     coverage_target="framework"
@@ -204,21 +204,21 @@
     cts="true" />
 
 <test name="cts-graphics"
-    build_path="cts/tests"
+    build_path="cts/tests/tests/graphics"
     package="com.android.cts.graphics"
     runner="android.test.InstrumentationCtsTestRunner"
     coverage_target="framework"
     cts="true" />
 
 <test name="cts-hardware"
-    build_path="cts/tests"
+    build_path="cts/tests/tests/hardware"
     package="com.android.cts.hardware"
     runner="android.test.InstrumentationCtsTestRunner"
     coverage_target="framework"
     cts="true" />
 
 <test name="cts-location"
-    build_path="cts/tests"
+    build_path="cts/tests/tests/location"
     package="com.android.cts.location"
     runner="android.test.InstrumentationCtsTestRunner"
     coverage_target="framework"
@@ -232,86 +232,86 @@
     cts="true" />
 
 <test name="cts-net"
-    build_path="cts/tests"
+    build_path="cts/tests/tests/net"
     package="com.android.cts.net"
     runner="android.test.InstrumentationCtsTestRunner"
     coverage_target="framework"
     cts="true" />
 
 <test name="cts-os"
-    build_path="cts/tests"
+    build_path="cts/tests/tests/os"
     package="com.android.cts.os"
     runner="android.test.InstrumentationCtsTestRunner"
     coverage_target="framework"
     cts="true" />
 
 <test name="cts-perf1"
-    build_path="cts/tests"
+    build_path="cts/tests/tests/performance"
     package="com.android.cts.performance"
     runner="android.test.InstrumentationCtsTestRunner"
     cts="true" />
 
 <test name="cts-perf2"
-    build_path="cts/tests"
+    build_path="cts/tests/tests/performance2"
     package="com.android.cts.performance2"
     runner="android.test.InstrumentationCtsTestRunner"
     cts="true" />
 
 <test name="cts-perf3"
-    build_path="cts/tests"
+    build_path="cts/tests/tests/performance3"
     package="com.android.cts.performance3"
     runner="android.test.InstrumentationCtsTestRunner"
     cts="true" />
 
 <test name="cts-perf4"
-    build_path="cts/tests"
+    build_path="cts/tests/tests/performance4"
     package="com.android.cts.performance4"
     runner="android.test.InstrumentationCtsTestRunner"
     cts="true" />
 
 <test name="cts-perf5"
-    build_path="cts/tests"
+    build_path="cts/tests/tests/performance5"
     package="com.android.cts.performance5"
     runner="android.test.InstrumentationCtsTestRunner"
     cts="true" />
 
 <test name="cts-provider"
-    build_path="cts/tests"
+    build_path="cts/tests/tests/provider"
     package="com.android.cts.provider"
     runner="android.test.InstrumentationCtsTestRunner"
     coverage_target="framework"
     cts="true" />
 
 <test name="cts-text"
-    build_path="cts/tests"
+    build_path="cts/tests/tests/text"
     package="com.android.cts.text"
     runner="android.test.InstrumentationCtsTestRunner"
     coverage_target="framework"
     cts="true" />
 
 <test name="cts-telephony"
-    build_path="cts/tests"
+    build_path="cts/tests/tests/telephony"
     package="com.android.cts.telephony"
     runner="android.test.InstrumentationCtsTestRunner"
     coverage_target="framework"
     cts="true" />
 
 <test name="cts-util"
-    build_path="cts/tests"
+    build_path="cts/tests/tests/util"
     package="com.android.cts.util"
     runner="android.test.InstrumentationCtsTestRunner"
     coverage_target="framework"
     cts="true" />
 
 <test name="cts-view"
-    build_path="cts/tests"
+    build_path="cts/tests/tests/view"
     package="com.android.cts.view"
     runner="android.test.InstrumentationCtsTestRunner"
     coverage_target="framework"
     cts="true" />
 
 <test name="cts-widget"
-    build_path="cts/tests"
+    build_path="cts/tests/tests/widget"
     package="com.android.cts.widget"
     runner="android.test.InstrumentationCtsTestRunner"
     coverage_target="framework"
@@ -368,6 +368,13 @@
     package="com.android.providers.contacts.tests"
     coverage_target="ContactsProvider" />
 
+<test name="contacts"
+    build_path="packages/apps/Contacts"
+    package="com.android.contacts.tests"
+    runner="android.test.InstrumentationTestRunner"
+    coverage_target="Contacts"
+    description="Tests for the Contacts app." />
+
 <test name="gcontactsprov"
     build_path="packages/providers/GoogleContactsProvider/tests"
     package="com.android.providers.contactstests"
diff --git a/tools/ddms/app/src/com/android/ddms/AboutDialog.java b/tools/ddms/app/src/com/android/ddms/AboutDialog.java
index 2910e5e..e946aee 100644
--- a/tools/ddms/app/src/com/android/ddms/AboutDialog.java
+++ b/tools/ddms/app/src/com/android/ddms/AboutDialog.java
@@ -124,7 +124,11 @@
 
         // Text lines
         label = new Label(textArea, SWT.NONE);
-        label.setText("Dalvik Debug Monitor v" + Main.VERSION);
+        if (Main.sRevision != null && Main.sRevision.length() > 0) {
+            label.setText("Dalvik Debug Monitor Revision " + Main.sRevision);
+        } else {
+            label.setText("Dalvik Debug Monitor");
+        }
         label = new Label(textArea, SWT.NONE);
         label.setText("Copyright 2007, The Android Open Source Project");
         label = new Label(textArea, SWT.NONE);
diff --git a/tools/ddms/app/src/com/android/ddms/Main.java b/tools/ddms/app/src/com/android/ddms/Main.java
index d545ed9..050519f 100644
--- a/tools/ddms/app/src/com/android/ddms/Main.java
+++ b/tools/ddms/app/src/com/android/ddms/Main.java
@@ -21,10 +21,15 @@
 import com.android.ddmlib.Log;
 import com.android.sdkstats.SdkStatsService;
 
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.lang.management.ManagementFactory;
 import java.lang.management.RuntimeMXBean;
+import java.util.Properties;
 
 
 /**
@@ -32,8 +37,7 @@
  */
 public class Main {
 
-    /** User visible version number. */
-    public static final String VERSION = "0.8.1";
+    public static String sRevision;
 
     public Main() {
     }
@@ -67,7 +71,7 @@
                     "JAVA_STARTED_ON_FIRST_THREAD_" + (rt.getName().split("@"))[0], //$NON-NLS-1$
                     "1"); //$NON-NLS-1$
         }
-        
+
         Thread.setDefaultUncaughtExceptionHandler(new UncaughtHandler());
 
         // load prefs and init the default values
@@ -85,8 +89,12 @@
             System.exit(1);
         }
 
-        // ddms itself is wanted: send a ping for ourselves
-        SdkStatsService.ping("ddms", VERSION, null);  //$NON-NLS-1$
+        // get the ddms parent folder location
+        String ddmsParentLocation = System.getProperty("com.android.ddms.bindir"); //$NON-NLS-1$
+
+        // we're past the point where ddms can be called just to send a ping, so we can
+        // ping for ddms itself.
+        ping(ddmsParentLocation);
 
         DebugPortManager.setProvider(DebugPortProvider.getInstance());
 
@@ -94,17 +102,38 @@
         UIThread ui = UIThread.getInstance();
 
         try {
-            ui.runUI();
+            ui.runUI(ddmsParentLocation);
         } finally {
             PrefsDialog.save();
-    
+
             AndroidDebugBridge.terminate();
         }
 
         Log.d("ddms", "Bye");
-        
+
         // this is kinda bad, but on MacOS the shutdown doesn't seem to finish because of
         // a thread called AWT-Shutdown. This will help while I track this down.
         System.exit(0);
     }
+
+    public static void ping(String ddmsParentLocation) {
+        Properties p = new Properties();
+        try{
+            File sourceProp;
+            if (ddmsParentLocation != null && ddmsParentLocation.length() > 0) {
+                sourceProp = new File(ddmsParentLocation, "source.properties"); //$NON-NLS-1$
+            } else {
+                sourceProp = new File("source.properties"); //$NON-NLS-1$
+            }
+            p.load(new FileInputStream(sourceProp));
+            sRevision = p.getProperty("Pkg.Revision"); //$NON-NLS-1$
+            if (sRevision != null && sRevision.length() > 0) {
+                SdkStatsService.ping("ddms", sRevision, null);  //$NON-NLS-1$
+            }
+        } catch (FileNotFoundException e) {
+            // couldn't find the file? don't ping.
+        } catch (IOException e) {
+            // couldn't find the file? don't ping.
+        }
+    }
 }
diff --git a/tools/ddms/app/src/com/android/ddms/UIThread.java b/tools/ddms/app/src/com/android/ddms/UIThread.java
index 49d07b0..c98b3f1 100644
--- a/tools/ddms/app/src/com/android/ddms/UIThread.java
+++ b/tools/ddms/app/src/com/android/ddms/UIThread.java
@@ -404,8 +404,9 @@
 
     /**
      * Create SWT objects and drive the user interface event loop.
+     * @param location location of the folder that contains ddms.
      */
-    public void runUI() {
+    public void runUI(String ddmsParentLocation) {
         Display.setAppName("ddms");
         mDisplay = new Display();
         final Shell shell = new Shell(mDisplay);
@@ -445,9 +446,9 @@
         ClientData.setMethodProfilingHandler(new MethodProfilingHandler(shell));
 
         // [try to] ensure ADB is running
-        String adbLocation = System.getProperty("com.android.ddms.bindir"); //$NON-NLS-1$
-        if (adbLocation != null && adbLocation.length() != 0) {
-            adbLocation += File.separator + "adb"; //$NON-NLS-1$
+        String adbLocation;
+        if (ddmsParentLocation != null && ddmsParentLocation.length() != 0) {
+            adbLocation = ddmsParentLocation + File.separator + "adb"; //$NON-NLS-1$
         } else {
             adbLocation = "adb"; //$NON-NLS-1$
         }
diff --git a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/RawImage.java b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/RawImage.java
index e3d4e09..6132bd0 100644
--- a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/RawImage.java
+++ b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/RawImage.java
@@ -128,16 +128,75 @@
     }
 
     /**
+     * Returns a rotated version of the image
+     * The image is rotated counter-clockwise.
+     */
+    public RawImage getRotated() {
+        RawImage rotated = new RawImage();
+        rotated.version = this.version;
+        rotated.bpp = this.bpp;
+        rotated.size = this.size;
+        rotated.red_offset = this.red_offset;
+        rotated.red_length = this.red_length;
+        rotated.blue_offset = this.blue_offset;
+        rotated.blue_length = this.blue_length;
+        rotated.green_offset = this.green_offset;
+        rotated.green_length = this.green_length;
+        rotated.alpha_offset = this.alpha_offset;
+        rotated.alpha_length = this.alpha_length;
+
+        rotated.width = this.height;
+        rotated.height = this.width;
+
+        int count = this.data.length;
+        rotated.data = new byte[count];
+
+        int byteCount = this.bpp >> 3; // bpp is in bits, we want bytes to match our array
+        final int w = this.width;
+        final int h = this.height;
+        for (int y = 0 ; y < h ; y++) {
+            for (int x = 0 ; x < w ; x++) {
+                System.arraycopy(
+                        this.data, (y * w + x) * byteCount,
+                        rotated.data, ((w-x-1) * h + y) * byteCount,
+                        byteCount);
+            }
+        }
+
+        return rotated;
+    }
+
+    /**
+     * Returns an ARGB integer value for the pixel at <var>index</var> in {@link #data}.
+     */
+    public int getARGB(int index) {
+        int value;
+        if (bpp == 16) {
+            value = data[index] & 0x00FF;
+            value |= (data[index+1] << 8) & 0x0FF00;
+        } else if (bpp == 32) {
+            value = data[index] & 0x00FF;
+            value |= (data[index+1] & 0x00FF) << 8;
+            value |= (data[index+2] & 0x00FF) << 16;
+            value |= (data[index+3] & 0x00FF) << 24;
+        } else {
+            throw new UnsupportedOperationException("RawImage.getARGB(int) only works in 16 and 32 bit mode.");
+        }
+
+        int r = ((value >>> red_offset) & getMask(red_length)) << (8 - red_length);
+        int g = ((value >>> green_offset) & getMask(green_length)) << (8 - green_length);
+        int b = ((value >>> blue_offset) & getMask(blue_length)) << (8 - blue_length);
+        int a = ((value >>> alpha_offset) & getMask(alpha_length)) << (8 - alpha_length);
+
+        return a << 24 | r << 16 | g << 8 | b;
+    }
+
+    /**
      * creates a mask value based on a length and offset.
      * <p/>This value is compatible with org.eclipse.swt.graphics.PaletteData
      */
     private int getMask(int length, int offset) {
-        int res = 0;
-        for (int i = 0 ; i < length ; i++) {
-            res = (res << 1) + 1;
-        }
-
-        res = res << offset;
+        int res = getMask(length) << offset;
 
         // if the bpp is 32 bits then we need to invert it because the buffer is in little endian
         if (bpp == 32) {
@@ -146,4 +205,18 @@
 
         return res;
     }
+
+    /**
+     * Creates a mask value based on a length.
+     * @param length
+     * @return
+     */
+    private int getMask(int length) {
+        int res = 0;
+        for (int i = 0 ; i < length ; i++) {
+            res = (res << 1) + 1;
+        }
+
+        return res;
+    }
 }
diff --git a/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/ScreenShotDialog.java b/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/ScreenShotDialog.java
index 0319642..b60c837 100644
--- a/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/ScreenShotDialog.java
+++ b/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/ScreenShotDialog.java
@@ -48,6 +48,7 @@
     private Label mImageLabel;
     private Button mSave;
     private IDevice mDevice;
+    private RawImage mRawImage;
 
 
     /**
@@ -95,7 +96,9 @@
     private void createContents(final Shell shell) {
         GridData data;
 
-        shell.setLayout(new GridLayout(3, true));
+        final int colCount = 4;
+
+        shell.setLayout(new GridLayout(colCount, true));
 
         // "refresh" button
         Button refresh = new Button(shell, SWT.PUSH);
@@ -110,6 +113,22 @@
             }
         });
 
+        // "rotate" button
+        Button rotate = new Button(shell, SWT.PUSH);
+        rotate.setText("Rotate");
+        data = new GridData(GridData.HORIZONTAL_ALIGN_CENTER);
+        data.widthHint = 80;
+        rotate.setLayoutData(data);
+        rotate.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                if (mRawImage != null) {
+                    mRawImage = mRawImage.getRotated();
+                    updateImageDisplay(shell);
+                }
+            }
+        });
+
         // "save" button
         mSave = new Button(shell, SWT.PUSH);
         mSave.setText("Save");
@@ -140,13 +159,13 @@
         mBusyLabel = new Label(shell, SWT.NONE);
         mBusyLabel.setText("Preparing...");
         data = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING);
-        data.horizontalSpan = 3;
+        data.horizontalSpan = colCount;
         mBusyLabel.setLayoutData(data);
 
         // space for the image
         mImageLabel = new Label(shell, SWT.BORDER);
         data = new GridData(GridData.HORIZONTAL_ALIGN_CENTER);
-        data.horizontalSpan = 3;
+        data.horizontalSpan = colCount;
         mImageLabel.setLayoutData(data);
         Display display = shell.getDisplay();
         mImageLabel.setImage(ImageHelper.createPlaceHolderArt(
@@ -156,22 +175,43 @@
         shell.setDefaultButton(done);
     }
 
-    /*
-     * Capture a new image from the device.
+    /**
+     * Captures a new image from the device, and display it.
      */
     private void updateDeviceImage(Shell shell) {
         mBusyLabel.setText("Capturing...");     // no effect
 
         shell.setCursor(shell.getDisplay().getSystemCursor(SWT.CURSOR_WAIT));
 
-        Image image = getDeviceImage();
-        if (image == null) {
+        mRawImage = getDeviceImage();
+
+        updateImageDisplay(shell);
+    }
+
+    /**
+     * Updates the display with {@link #mRawImage}.
+     * @param shell
+     */
+    private void updateImageDisplay(Shell shell) {
+        Image image;
+        if (mRawImage == null) {
             Display display = shell.getDisplay();
             image = ImageHelper.createPlaceHolderArt(
                     display, 320, 240, display.getSystemColor(SWT.COLOR_BLUE));
+
             mSave.setEnabled(false);
             mBusyLabel.setText("Screen not available");
         } else {
+            // convert raw data to an Image.
+            PaletteData palette = new PaletteData(
+                    mRawImage.getRedMask(),
+                    mRawImage.getGreenMask(),
+                    mRawImage.getBlueMask());
+
+            ImageData imageData = new ImageData(mRawImage.width, mRawImage.height,
+                    mRawImage.bpp, palette, 1, mRawImage.data);
+            image = new Image(getParent().getDisplay(), imageData);
+
             mSave.setEnabled(true);
             mBusyLabel.setText("Captured image:");
         }
@@ -184,40 +224,19 @@
         shell.setCursor(shell.getDisplay().getSystemCursor(SWT.CURSOR_ARROW));
     }
 
-    /*
-     * Grab an image from an ADB-connected device.
+    /**
+     * Grabs an image from an ADB-connected device and returns it as a {@link RawImage}.
      */
-    private Image getDeviceImage() {
-        RawImage rawImage;
-
+    private RawImage getDeviceImage() {
         try {
-            rawImage = mDevice.getScreenshot();
+            return mDevice.getScreenshot();
         }
         catch (IOException ioe) {
             Log.w("ddms", "Unable to get frame buffer: " + ioe.getMessage());
             return null;
         }
-
-        // device/adb not available?
-        if (rawImage == null)
-            return null;
-
-        // convert raw data to an Image.
-        PaletteData palette = new PaletteData(
-                rawImage.getRedMask(),
-                rawImage.getGreenMask(),
-                rawImage.getBlueMask());
-        ImageData imageData = new ImageData(rawImage.width, rawImage.height,
-                rawImage.bpp, palette, 1, rawImage.data);
-
-        if (imageData != null) {
-            return new Image(getParent().getDisplay(), imageData);
-        }
-
-        return null;
     }
 
-
     /*
      * Prompt the user to save the image to disk.
      */
diff --git a/tools/jarutils/src/com/android/jarutils/JavaResourceFilter.java b/tools/jarutils/src/com/android/jarutils/JavaResourceFilter.java
index d9f8da6..1a97e28 100644
--- a/tools/jarutils/src/com/android/jarutils/JavaResourceFilter.java
+++ b/tools/jarutils/src/com/android/jarutils/JavaResourceFilter.java
@@ -45,10 +45,10 @@
 
         // get the file name from the path
         String fileName = segments[segments.length-1];
-        
+
         return checkFileForPackaging(fileName);
     }
-    
+
     /**
      * Checks whether a folder and its content is valid for packaging into the .apk as
      * standard Java resource.
@@ -84,13 +84,14 @@
      * @return true if the file should be packaged as standard java resources.
      */
     public static boolean checkFileForPackaging(String fileName, String extension) {
-        return "aidl".equalsIgnoreCase(extension) == false &&
-            "java".equalsIgnoreCase(extension) == false &&
-            "class".equalsIgnoreCase(extension) == false &&
-            "package.html".equalsIgnoreCase(fileName) == false &&
-            "overview.html".equalsIgnoreCase(fileName) == false &&
-            ".cvsignore".equalsIgnoreCase(fileName) == false &&
-            ".DS_Store".equals(fileName) == false && 
-            fileName.charAt(fileName.length()-1) != '~';
+        return "aidl".equalsIgnoreCase(extension) == false &&       // Aidl files
+            "java".equalsIgnoreCase(extension) == false &&          // Java files
+            "class".equalsIgnoreCase(extension) == false &&         // Java class files
+            "scc".equalsIgnoreCase(extension) == false &&           // VisualSourceSafe
+            "package.html".equalsIgnoreCase(fileName) == false &&   // Javadoc
+            "overview.html".equalsIgnoreCase(fileName) == false &&  // Javadoc
+            ".cvsignore".equalsIgnoreCase(fileName) == false &&     // CVS
+            ".DS_Store".equals(fileName) == false &&                // Mac resources
+            fileName.charAt(fileName.length()-1) != '~';            // Backup files
     }
 }
diff --git a/tools/screenshot/src/com/android/screenshot/Screenshot.java b/tools/screenshot/src/com/android/screenshot/Screenshot.java
index 06e1f6b..a3fe520 100644
--- a/tools/screenshot/src/com/android/screenshot/Screenshot.java
+++ b/tools/screenshot/src/com/android/screenshot/Screenshot.java
@@ -208,53 +208,21 @@
         if (rawImage == null)
             return;
 
-        assert rawImage.bpp == 16;
-
-        BufferedImage image;
-
         if (landscape) {
-            // convert raw data to an Image
-            image = new BufferedImage(rawImage.height, rawImage.width,
-                    BufferedImage.TYPE_INT_ARGB);
+            rawImage = rawImage.getRotated();
+        }
 
-            byte[] buffer = rawImage.data;
-            int index = 0;
-            for (int y = 0 ; y < rawImage.height ; y++) {
-                for (int x = 0 ; x < rawImage.width ; x++) {
+        // convert raw data to an Image
+        BufferedImage image = new BufferedImage(rawImage.width, rawImage.height,
+                BufferedImage.TYPE_INT_ARGB);
 
-                    int value = buffer[index++] & 0x00FF;
-                    value |= (buffer[index++] << 8) & 0x0FF00;
-
-                    int r = ((value >> 11) & 0x01F) << 3;
-                    int g = ((value >> 5) & 0x03F) << 2;
-                    int b = ((value >> 0) & 0x01F) << 3;
-
-                    value = 0xFF << 24 | r << 16 | g << 8 | b;
-
-                    image.setRGB(y, rawImage.width - x - 1, value);
-                }
-            }
-        } else {
-            // convert raw data to an Image
-            image = new BufferedImage(rawImage.width, rawImage.height,
-                    BufferedImage.TYPE_INT_ARGB);
-
-            byte[] buffer = rawImage.data;
-            int index = 0;
-            for (int y = 0 ; y < rawImage.height ; y++) {
-                for (int x = 0 ; x < rawImage.width ; x++) {
-
-                    int value = buffer[index++] & 0x00FF;
-                    value |= (buffer[index++] << 8) & 0x0FF00;
-
-                    int r = ((value >> 11) & 0x01F) << 3;
-                    int g = ((value >> 5) & 0x03F) << 2;
-                    int b = ((value >> 0) & 0x01F) << 3;
-
-                    value = 0xFF << 24 | r << 16 | g << 8 | b;
-
-                    image.setRGB(x, y, value);
-                }
+        int index = 0;
+        int IndexInc = rawImage.bpp >> 3;
+        for (int y = 0 ; y < rawImage.height ; y++) {
+            for (int x = 0 ; x < rawImage.width ; x++) {
+                int value = rawImage.getARGB(index);
+                index += IndexInc;
+                image.setRGB(x, y, value);
             }
         }
 
diff --git a/tools/scripts/android_rules.xml b/tools/scripts/android_rules.xml
index 8b7fe88..5b32736 100644
--- a/tools/scripts/android_rules.xml
+++ b/tools/scripts/android_rules.xml
@@ -230,10 +230,12 @@
     <!-- Compiles this project's .java files into .class files. -->
     <target name="compile" depends="-resource-src, -aidl"
                 description="Compiles project's .java files into .class files">
-        <!-- Allows to inject additional classpath from another (build|property) file.
-             As ant properties are immutable, the injected value will have priority
-             over the one defined below -->
-        <property name="extensible.classpath" value="." />
+        <!-- If android rules are used for a test project, its classpath should include
+             tested project's location -->
+        <condition property="extensible.classpath"
+                           value="${tested.project.absolute.dir}/bin/classes" else=".">
+            <isset property="tested.project.absolute.dir" />
+        </condition>
         <javac encoding="ascii" target="1.5" debug="true" extdirs=""
                 destdir="${out.classes.absolute.dir}"
                 bootclasspathref="android.target.classpath"
@@ -278,8 +280,14 @@
         <package-helper sign.package="false" />
     </target>
 
+    <target name="-compile-tested-if-test" if="tested.project.dir" unless="do.not.compile.again">
+       <subant target="compile">
+            <fileset dir="${tested.project.absolute.dir}" includes="build.xml" />
+       </subant>
+    </target>
+
     <!-- Builds debug output package, provided all the necessary files are already dexed -->
-    <target name="debug" depends="-package-debug-sign"
+    <target name="debug" depends="-compile-tested-if-test, -package-debug-sign"
                 description="Builds the application and signs it with a debug key.">
         <zipalign-helper in.package="${out.debug.unaligned.package}"
                                    out.package="${out.debug.package}" />
diff --git a/tools/scripts/android_test_rules.xml b/tools/scripts/android_test_rules.xml
index fd8a521..78503ae 100644
--- a/tools/scripts/android_test_rules.xml
+++ b/tools/scripts/android_test_rules.xml
@@ -43,27 +43,22 @@
                       location="${instrumentation.absolute.dir}/classes" />
     </target>
 
-    <!-- Invoking this target sets the value of extensible.classpath, which is being added to javac
-         classpath in target 'compile' (android_rules.xml) -->
-    <target name="-set-run-tests-classpath">
-        <property name="extensible.classpath"
-                      location="${tested.project.absolute.dir}/bin/classes" />
-    </target>
-
     <!-- Ensures that tested project is installed on the device before we run the tests.
          Used for ordinary tests, without coverage measurement -->
     <target name="-install-tested-project">
+        <property name="do.not.compile.again" value="true" />
         <subant target="install">
             <fileset dir="${tested.project.absolute.dir}" includes="build.xml" />
         </subant>
     </target>
 
-    <target name="run-tests" depends="-set-run-tests-classpath, -install-tested-project, install"
+    <target name="run-tests" depends="-install-tested-project, install"
                 description="Runs tests from the package defined in test.package property">
         <run-tests-helper />
     </target>
 
     <target name="-install-instrumented">
+        <property name="do.not.compile.again" value="true" />
         <subant target="-install-with-emma">
             <property name="out.absolute.dir" value="${instrumentation.absolute.dir}" />
             <fileset dir="${tested.project.absolute.dir}" includes="build.xml" />
@@ -103,6 +98,7 @@
         <delete dir="${instrumentation.absolute.dir}" />
         <delete file="coverage.ec" />
         <delete file="coverage.em" />
+        <echo>Saving the report file in ${basedir}/coverage/coverage.html</echo>
     </target>
 
 </project>
diff --git a/tools/sdkmanager/app/src/com/android/sdkmanager/Main.java b/tools/sdkmanager/app/src/com/android/sdkmanager/Main.java
index 6236599..fa2870d 100644
--- a/tools/sdkmanager/app/src/com/android/sdkmanager/Main.java
+++ b/tools/sdkmanager/app/src/com/android/sdkmanager/Main.java
@@ -42,10 +42,10 @@
 /**
  * Main class for the 'android' application.
  */
-class Main {
+public class Main {
 
     /** Java property that defines the location of the sdk/tools directory. */
-    private final static String TOOLSDIR = "com.android.sdkmanager.toolsdir";
+    public final static String TOOLSDIR = "com.android.sdkmanager.toolsdir";
     /** Java property that defines the working directory. On Windows the current working directory
      *  is actually the tools dir, in which case this is used to get the original CWD. */
     private final static String WORKDIR = "com.android.sdkmanager.workdir";
diff --git a/tools/sdkmanager/app/src/com/android/sdkmanager/internal/repository/AboutPage.java b/tools/sdkmanager/app/src/com/android/sdkmanager/internal/repository/AboutPage.java
index 49aad29..742a065 100755
--- a/tools/sdkmanager/app/src/com/android/sdkmanager/internal/repository/AboutPage.java
+++ b/tools/sdkmanager/app/src/com/android/sdkmanager/internal/repository/AboutPage.java
@@ -17,16 +17,20 @@
 package com.android.sdkmanager.internal.repository;

 

 

+import com.android.sdkmanager.*;

 import org.eclipse.swt.SWT;

+import org.eclipse.swt.graphics.Image;

 import org.eclipse.swt.layout.GridData;

 import org.eclipse.swt.layout.GridLayout;

 import org.eclipse.swt.widgets.Composite;

 import org.eclipse.swt.widgets.Label;

 

-/*

- * TODO list

- * - Change version to be a constant pulled from somewhere.

- */

+import java.io.File;

+import java.io.FileInputStream;

+import java.io.FileNotFoundException;

+import java.io.IOException;

+import java.io.InputStream;

+import java.util.Properties;

 

 public class AboutPage extends Composite {

 

@@ -45,11 +49,21 @@
     }

 

     private void createContents(Composite parent) {

-        parent.setLayout(new GridLayout(1, false));

+        parent.setLayout(new GridLayout(2, false));

+

+        Label logo = new Label(parent, SWT.NONE);

+        InputStream imageStream = this.getClass().getResourceAsStream("logo.png");

+

+        if (imageStream != null) {

+            Image img = new Image(parent.getShell().getDisplay(), imageStream);

+            logo.setImage(img);

+        }

 

         mLabel = new Label(parent, SWT.NONE);

-        mLabel.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, true, 1, 1));

-        mLabel.setText("Android SDK Updater.\n\nVersion 0.1.\n\nCopyright (C) 2009 The Android Open Source Project.");

+        mLabel.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, false, false, 1, 1));

+        mLabel.setText(String.format(

+                "Android SDK Updater.\nRevision %1$s\nCopyright (C) 2009 The Android Open Source Project.",

+                getRevision()));

     }

 

     @Override

@@ -69,4 +83,28 @@
 

     // End of hiding from SWT Designer

     //$hide<<$

+

+    private String getRevision() {

+        Properties p = new Properties();

+        try{

+            String toolsdir = System.getProperty(Main.TOOLSDIR);

+            File sourceProp;

+            if (toolsdir == null || toolsdir.length() == 0) {

+                sourceProp = new File("source.properties"); //$NON-NLS-1$

+            } else {

+                sourceProp = new File(toolsdir, "source.properties"); //$NON-NLS-1$

+            }

+            p.load(new FileInputStream(sourceProp));

+            String revision = p.getProperty("Pkg.Revision"); //$NON-NLS-1$

+            if (revision != null) {

+                return revision;

+            }

+        } catch (FileNotFoundException e) {

+            // couldn't find the file? don't ping.

+        } catch (IOException e) {

+            // couldn't find the file? don't ping.

+        }

+

+        return "?";

+    }

 }

diff --git a/tools/sdkmanager/app/src/com/android/sdkmanager/internal/repository/logo.png b/tools/sdkmanager/app/src/com/android/sdkmanager/internal/repository/logo.png
new file mode 100644
index 0000000..0f1670d
--- /dev/null
+++ b/tools/sdkmanager/app/src/com/android/sdkmanager/internal/repository/logo.png
Binary files differ
diff --git a/tools/traceview/etc/traceview b/tools/traceview/etc/traceview
index 8f52e77..3d1771f 100755
--- a/tools/traceview/etc/traceview
+++ b/tools/traceview/etc/traceview
@@ -14,27 +14,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# This script assumes that the path to this script is something like:
-#
-# /somepath/outdir/archdir/hostdir/bindir/traceview
-#
-# where "somepath" is some pathname (like "/work/android/device/")
-#       "outdir"   is a subdirectory (like "out")
-#       "archdir"  is a subdirectory (like "linux-x86-release")
-#       "hostdir"  is a subdirectory (like "host")
-#       "bindir"   is a subdirectory (like "bin")
-#
-# e.g. /work/android/device/out/linux-x86-release/host/bin/traceview
-#
-# and that the following directories also exist:
-#
-# /somepath/outdir/archdir/hostdir/lib/
-# /somepath/outdir/archdir/hostdir/framework/
-#
-# where:
-#       "lib", and "framework" are at the same level as "bindir",
-#        and are the literal names.
-
 # Set up prog to be the path of this script, including following symlinks,
 # and set up progdir to be the fully-qualified pathname of its directory.
 prog="$0"
@@ -118,4 +97,4 @@
     exit 1
 fi
 
-exec "${javaCmd}" $javaOpts -Djava.ext.dirs="$frameworkdir" -jar "$jarpath" "$@"
+exec "${javaCmd}" $javaOpts -Djava.ext.dirs="$frameworkdir" -Dcom.android.traceview.toolsdir="$progdir" -jar "$jarpath" "$@"
diff --git a/tools/traceview/etc/traceview.bat b/tools/traceview/etc/traceview.bat
index 2da8a3b..02fbe85 100755
--- a/tools/traceview/etc/traceview.bat
+++ b/tools/traceview/etc/traceview.bat
@@ -55,4 +55,4 @@
 :SetPath
 set javaextdirs=%swt_path%;%frameworkdir%
 
-call java -Djava.ext.dirs=%javaextdirs% -jar %jarpath% %*
+call java -Djava.ext.dirs=%javaextdirs% -Dcom.android.traceview.toolsdir= -jar %jarpath% %*
diff --git a/tools/traceview/src/com/android/traceview/MainWindow.java b/tools/traceview/src/com/android/traceview/MainWindow.java
index 5800f81..00fcc8b 100644
--- a/tools/traceview/src/com/android/traceview/MainWindow.java
+++ b/tools/traceview/src/com/android/traceview/MainWindow.java
@@ -30,14 +30,16 @@
 import org.eclipse.swt.widgets.Shell;
 
 import java.io.File;
-import java.io.IOException;
 import java.io.FileInputStream;
+import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
+import java.io.IOException;
 import java.nio.channels.FileChannel;
 import java.util.HashMap;
+import java.util.Properties;
 
 public class MainWindow extends ApplicationWindow {
-    
+
     private final static String PING_NAME = "Traceview";
     private final static String PING_VERSION = "1.0";
 
@@ -99,10 +101,10 @@
 
     /**
      * Convert the old two-file format into the current concatenated one.
-     * 
+     *
      * @param base Base path of the two files, i.e. base.key and base.data
      * @return Path to a temporary file that will be deleted on exit.
-     * @throws IOException 
+     * @throws IOException
      */
     private static String makeTempTraceFile(String base) throws IOException {
         // Make a temporary file that will go away on exit and prepare to
@@ -127,13 +129,45 @@
         // Return the path of the temp file.
         return temp.getPath();
     }
-    
+
+    /**
+     * Returns the tools revision number.
+     */
+    private static String getRevision() {
+        Properties p = new Properties();
+        try{
+            String toolsdir = System.getProperty("com.android.traceview.toolsdir"); //$NON-NLS-1$
+            File sourceProp;
+            if (toolsdir == null || toolsdir.length() == 0) {
+                sourceProp = new File("source.properties"); //$NON-NLS-1$
+            } else {
+                sourceProp = new File(toolsdir, "source.properties"); //$NON-NLS-1$
+            }
+            p.load(new FileInputStream(sourceProp));
+            String revision = p.getProperty("Pkg.Revision"); //$NON-NLS-1$
+            if (revision != null && revision.length() > 0) {
+                return revision;
+            }
+        } catch (FileNotFoundException e) {
+            // couldn't find the file? don't ping.
+        } catch (IOException e) {
+            // couldn't find the file? don't ping.
+        }
+
+        return null;
+    }
+
+
     public static void main(String[] args) {
         TraceReader reader = null;
         boolean regression = false;
-        
+
         // ping the usage server
-        SdkStatsService.ping(PING_NAME, PING_VERSION, null);
+
+        String revision = getRevision();
+        if (revision != null) {
+            SdkStatsService.ping(PING_NAME, revision, null);
+        }
 
         // Process command line arguments
         int argc = 0;