Merge change 5420 into donut

* changes:
  Added two test cases to trace the failure in closing the hw decoder and the current playtime is greater than the duration.
diff --git a/Android.mk b/Android.mk
index 0e8793d..f32129e 100644
--- a/Android.mk
+++ b/Android.mk
@@ -82,6 +82,7 @@
 	core/java/android/app/IWallpaperService.aidl \
 	core/java/android/app/IWallpaperServiceCallback.aidl \
 	core/java/android/backup/IBackupManager.aidl \
+	core/java/android/backup/IRestoreObserver.aidl \
 	core/java/android/backup/IRestoreSession.aidl \
 	core/java/android/bluetooth/IBluetoothA2dp.aidl \
 	core/java/android/bluetooth/IBluetoothDevice.aidl \
diff --git a/api/current.xml b/api/current.xml
index 658a87c..673d053 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -3452,39 +3452,6 @@
  visibility="public"
 >
 </field>
-<field name="donut_resource_pad26"
- type="int"
- transient="false"
- volatile="false"
- value="16843398"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="donut_resource_pad27"
- type="int"
- transient="false"
- volatile="false"
- value="16843397"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="donut_resource_pad28"
- type="int"
- transient="false"
- volatile="false"
- value="16843396"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="donut_resource_pad3"
  type="int"
  transient="false"
@@ -5311,6 +5278,17 @@
  visibility="public"
 >
 </field>
+<field name="largeScreens"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843398"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="launchMode"
  type="int"
  transient="false"
@@ -6191,6 +6169,17 @@
  visibility="public"
 >
 </field>
+<field name="normalScreens"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843397"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="numColumns"
  type="int"
  transient="false"
@@ -7577,6 +7566,17 @@
  visibility="public"
 >
 </field>
+<field name="smallScreens"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843396"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="smoothScrollbar"
  type="int"
  transient="false"
@@ -22088,6 +22088,19 @@
 <parameter name="position" type="int">
 </parameter>
 </method>
+<method name="itemForPosition"
+ return="android.app.LauncherActivity.ListItem"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+<parameter name="position" type="int">
+</parameter>
+</method>
 <method name="makeListItems"
  return="java.util.List&lt;android.app.LauncherActivity.ListItem&gt;"
  abstract="false"
@@ -22196,6 +22209,16 @@
  visibility="public"
 >
 </field>
+<field name="resolveInfo"
+ type="android.content.pm.ResolveInfo"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 </class>
 <class name="ListActivity"
  extends="android.app.Activity"
@@ -34798,6 +34821,17 @@
  visibility="public"
 >
 </field>
+<field name="CONFIG_SCREEN_LAYOUT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="256"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="CONFIG_TOUCHSCREEN"
  type="int"
  transient="false"
@@ -35266,6 +35300,28 @@
  type="int"
  transient="false"
  volatile="false"
+ value="2048"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="FLAG_SUPPORTS_NORMAL_SCREENS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1024"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="FLAG_SUPPORTS_SMALL_SCREENS"
+ type="int"
+ transient="false"
+ volatile="false"
  value="512"
  static="true"
  final="true"
@@ -39465,6 +39521,50 @@
  visibility="public"
 >
 </field>
+<field name="SCREENLAYOUT_LARGE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SCREENLAYOUT_NORMAL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SCREENLAYOUT_SMALL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SCREENLAYOUT_UNDEFINED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="TOUCHSCREEN_FINGER"
  type="int"
  transient="false"
@@ -39599,6 +39699,16 @@
  visibility="public"
 >
 </field>
+<field name="screenLayout"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="touchscreen"
  type="int"
  transient="false"
@@ -139378,6 +139488,19 @@
  visibility="public"
 >
 </method>
+<method name="buildDrawingCache"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="autoScale" type="boolean">
+</parameter>
+</method>
 <method name="cancelLongPress"
  return="void"
  abstract="false"
@@ -139954,6 +140077,19 @@
  visibility="public"
 >
 </method>
+<method name="getDrawingCache"
+ return="android.graphics.Bitmap"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="autoScale" type="boolean">
+</parameter>
+</method>
 <method name="getDrawingCacheBackgroundColor"
  return="int"
  abstract="false"
@@ -180577,7 +180713,7 @@
 <method name="startMethodTracing"
  return="void"
  abstract="false"
- native="true"
+ native="false"
  synchronized="false"
  static="true"
  final="false"
diff --git a/camera/libcameraservice/CameraService.cpp b/camera/libcameraservice/CameraService.cpp
index e945056..022fe5a 100644
--- a/camera/libcameraservice/CameraService.cpp
+++ b/camera/libcameraservice/CameraService.cpp
@@ -98,7 +98,7 @@
     LOGD("CameraService::connect E (pid %d, client %p)", callingPid,
             cameraClient->asBinder().get());
 
-    Mutex::Autolock lock(mLock);
+    Mutex::Autolock lock(mServiceLock);
     sp<Client> client;
     if (mClient != 0) {
         sp<Client> currentClient = mClient.promote();
@@ -125,13 +125,14 @@
             LOGD("New client (pid %d) connecting, old reference was dangling...",
                     callingPid);
             mClient.clear();
-            if (mUsers > 0) {
-                LOGD("Still have client, rejected");
-                return client;
-            }
         }
     }
 
+    if (mUsers > 0) {
+        LOGD("Still have client, rejected");
+        return client;
+    }
+
     // create a new Client object
     client = new Client(this, cameraClient, callingPid);
     mClient = client;
@@ -152,7 +153,7 @@
     // destructor won't be called with the lock held.
     sp<Client> client;
 
-    Mutex::Autolock lock(mLock);
+    Mutex::Autolock lock(mServiceLock);
 
     if (mClient == 0) {
         // This happens when we have already disconnected.
@@ -390,8 +391,6 @@
     // from the user directly, or called by the destructor.
     if (mHardware == 0) return;
 
-    mCameraService->removeClient(mCameraClient);
-
     LOGD("hardware teardown");
     // Before destroying mHardware, we must make sure it's in the
     // idle state.
@@ -402,6 +401,7 @@
     mHardware->release();
     mHardware.clear();
 
+    mCameraService->removeClient(mCameraClient);
     mCameraService->decUsers();
 
     LOGD("Client::disconnect() X (pid %d)", callingPid);
@@ -410,11 +410,14 @@
 // pass the buffered ISurface to the camera service
 status_t CameraService::Client::setPreviewDisplay(const sp<ISurface>& surface)
 {
-    LOGD("setPreviewDisplay(%p) (pid %d)", surface.get(), getCallingPid());
+    LOGD("setPreviewDisplay(%p) (pid %d)",
+         ((surface == NULL) ? NULL : surface.get()), getCallingPid());
     Mutex::Autolock lock(mLock);
     status_t result = checkPid();
     if (result != NO_ERROR) return result;
+
     Mutex::Autolock surfaceLock(mSurfaceLock);
+    result = NO_ERROR;
     // asBinder() is safe on NULL (returns NULL)
     if (surface->asBinder() != mSurface->asBinder()) {
         if (mSurface != 0 && !mUseOverlay) {
@@ -422,8 +425,17 @@
             mSurface->unregisterBuffers();
         }
         mSurface = surface;
+        // If preview has been already started, set overlay or register preview
+        // buffers now.
+        if (mHardware->previewEnabled()) {
+            if (mUseOverlay) {
+                result = setOverlay();
+            } else if (mSurface != 0) {
+                result = registerPreviewBuffers();
+            }
+        }
     }
-    return NO_ERROR;
+    return result;
 }
 
 // set the preview callback flag to affect how the received frames from
@@ -436,7 +448,7 @@
     mPreviewCallbackFlag = callback_flag;
 }
 
-// start preview mode, must call setPreviewDisplay first
+// start preview mode
 status_t CameraService::Client::startCameraMode(camera_mode mode)
 {
     int callingPid = getCallingPid();
@@ -456,16 +468,18 @@
         return INVALID_OPERATION;
     }
 
-    if (mSurface == 0) {
-        LOGE("setPreviewDisplay must be called before startCameraMode!");
-        return INVALID_OPERATION;
-    }
-
     switch(mode) {
     case CAMERA_RECORDING_MODE:
+        if (mSurface == 0) {
+            LOGE("setPreviewDisplay must be called before startRecordingMode.");
+            return INVALID_OPERATION;
+        }
         return startRecordingMode();
 
     default: // CAMERA_PREVIEW_MODE
+        if (mSurface == 0) {
+            LOGD("mSurface is not set yet.");
+        }
         return startPreviewMode();
     }
 }
@@ -498,6 +512,62 @@
     return ret;
 }
 
+status_t CameraService::Client::setOverlay()
+{
+    LOGD("setOverlay");
+    int w, h;
+    CameraParameters params(mHardware->getParameters());
+    params.getPreviewSize(&w, &h);
+
+    const char *format = params.getPreviewFormat();
+    int fmt;
+    if (!strcmp(format, "yuv422i"))
+        fmt = OVERLAY_FORMAT_YCbCr_422_I;
+    else if (!strcmp(format, "rgb565"))
+        fmt = OVERLAY_FORMAT_RGB_565;
+    else {
+        LOGE("Invalid preview format for overlays");
+        return -EINVAL;
+    }
+
+    status_t ret = NO_ERROR;
+    if (mSurface != 0) {
+        sp<OverlayRef> ref = mSurface->createOverlay(w, h, fmt);
+        ret = mHardware->setOverlay(new Overlay(ref));
+    } else {
+        ret = mHardware->setOverlay(NULL);
+    }
+    if (ret != NO_ERROR) {
+        LOGE("mHardware->setOverlay() failed with status %d\n", ret);
+    }
+    return ret;
+}
+
+status_t CameraService::Client::registerPreviewBuffers()
+{
+    int w, h;
+    CameraParameters params(mHardware->getParameters());
+    params.getPreviewSize(&w, &h);
+
+    uint32_t transform = 0;
+    if (params.getOrientation() ==
+        CameraParameters::CAMERA_ORIENTATION_PORTRAIT) {
+      LOGV("portrait mode");
+      transform = ISurface::BufferHeap::ROT_90;
+    }
+    ISurface::BufferHeap buffers(w, h, w, h,
+                                 PIXEL_FORMAT_YCbCr_420_SP,
+                                 transform,
+                                 0,
+                                 mHardware->getPreviewHeap());
+
+    status_t ret = mSurface->registerBuffers(buffers);
+    if (ret != NO_ERROR) {
+        LOGE("registerBuffers failed with status %d", ret);
+    }
+    return ret;
+}
+
 status_t CameraService::Client::startPreviewMode()
 {
     LOGD("startPreviewMode (pid %d)", getCallingPid());
@@ -511,55 +581,24 @@
 #if DEBUG_DUMP_PREVIEW_FRAME_TO_FILE
     debug_frame_cnt = 0;
 #endif
-    status_t ret = UNKNOWN_ERROR;
-    int w, h;
-    CameraParameters params(mHardware->getParameters());
-    params.getPreviewSize(&w, &h);
+    status_t ret = NO_ERROR;
 
     if (mUseOverlay) {
-        const char *format = params.getPreviewFormat();
-        int fmt;
-        LOGD("Use Overlays");
-        if (!strcmp(format, "yuv422i"))
-            fmt = OVERLAY_FORMAT_YCbCr_422_I;
-        else if (!strcmp(format, "rgb565"))
-            fmt = OVERLAY_FORMAT_RGB_565;
-        else {
-            LOGE("Invalid preview format for overlays");
-            return -EINVAL;
+        // If preview display has been set, set overlay now.
+        if (mSurface != 0) {
+            ret = setOverlay();
         }
-        sp<OverlayRef> ref = mSurface->createOverlay(w, h, fmt);
-        ret = mHardware->setOverlay(new Overlay(ref));
-        if (ret != NO_ERROR) {
-            LOGE("mHardware->setOverlay() failed with status %d\n", ret);
-            return ret;
-        }
+        if (ret != NO_ERROR) return ret;
         ret = mHardware->startPreview(NULL, mCameraService.get());
-        if (ret != NO_ERROR)
-            LOGE("mHardware->startPreview() failed with status %d\n", ret);
-
     } else {
         ret = mHardware->startPreview(previewCallback,
                                       mCameraService.get());
-        if (ret == NO_ERROR) {
-
-            mSurface->unregisterBuffers();
-
-            uint32_t transform = 0;
-            if (params.getOrientation() ==
-                CameraParameters::CAMERA_ORIENTATION_PORTRAIT) {
-              LOGV("portrait mode");
-              transform = ISurface::BufferHeap::ROT_90;
-            }
-            ISurface::BufferHeap buffers(w, h, w, h,
-                                         PIXEL_FORMAT_YCbCr_420_SP,
-                                         transform,
-                                         0,
-                                         mHardware->getPreviewHeap());
-
-            mSurface->registerBuffers(buffers);
-        } else {
-          LOGE("mHardware->startPreview() failed with status %d", ret);
+        if (ret != NO_ERROR) return ret;
+        // If preview display has been set, register preview buffers now.
+        if (mSurface != 0) {
+           // Unregister here because the surface registered with raw heap.
+           mSurface->unregisterBuffers();
+           ret = registerPreviewBuffers();
         }
     }
     return ret;
@@ -661,7 +700,7 @@
     sp<Client> client = 0;
     CameraService *service = static_cast<CameraService*>(user);
     if (service != NULL) {
-        Mutex::Autolock ourLock(service->mLock);
+        Mutex::Autolock ourLock(service->mServiceLock);
         if (service->mClient != 0) {
             client = service->mClient.promote();
             if (client == 0) {
@@ -1104,7 +1143,7 @@
         result.append(buffer);
         write(fd, result.string(), result.size());
     } else {
-        AutoMutex lock(&mLock);
+        AutoMutex lock(&mServiceLock);
         if (mClient != 0) {
             sp<Client> currentClient = mClient.promote();
             sprintf(buffer, "Client (%p) PID: %d\n",
diff --git a/camera/libcameraservice/CameraService.h b/camera/libcameraservice/CameraService.h
index 729e539..0f07673 100644
--- a/camera/libcameraservice/CameraService.h
+++ b/camera/libcameraservice/CameraService.h
@@ -157,6 +157,8 @@
         status_t                startCameraMode(camera_mode mode);
         status_t                startPreviewMode();
         status_t                startRecordingMode();
+        status_t                setOverlay();
+        status_t                registerPreviewBuffers();
 
         // Ensures atomicity among the public methods
         mutable     Mutex                       mLock;
@@ -199,7 +201,7 @@
     virtual     void                        incUsers();
     virtual     void                        decUsers();
 
-    mutable     Mutex                       mLock;
+    mutable     Mutex                       mServiceLock;
                 wp<Client>                  mClient;
 
 #if DEBUG_HEAP_LEAKS
diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
index 841e3df..c90b862 100644
--- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
+++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
@@ -17,6 +17,7 @@
 package com.android.commands.bmgr;
 
 import android.backup.IBackupManager;
+import android.backup.IRestoreObserver;
 import android.backup.IRestoreSession;
 import android.backup.RestoreSet;
 import android.os.RemoteException;
@@ -61,6 +62,16 @@
         String op = args[0];
         mNextArg = 1;
 
+        if ("enabled".equals(op)) {
+            doEnabled();
+            return;
+        }
+
+        if ("enable".equals(op)) {
+            doEnable();
+            return;
+        }
+
         if ("run".equals(op)) {
             doRun();
             return;
@@ -90,6 +101,41 @@
         showUsage();
     }
 
+    private String enableToString(boolean enabled) {
+        return enabled ? "enabled" : "disabled";
+    }
+
+    private void doEnabled() {
+        try {
+            boolean isEnabled = mBmgr.isBackupEnabled();
+            System.out.println("Backup Manager currently "
+                    + enableToString(isEnabled));
+        } catch (RemoteException e) {
+            System.err.println(e.toString());
+            System.err.println(BMGR_NOT_RUNNING_ERR);
+        }
+    }
+
+    private void doEnable() {
+        String arg = nextArg();
+        if (arg == null) {
+            showUsage();
+            return;
+        }
+
+        try {
+            boolean enable = Boolean.parseBoolean(arg);
+            mBmgr.setBackupEnabled(enable);
+            System.out.println("Backup Manager now " + enableToString(enable));
+        } catch (NumberFormatException e) {
+            showUsage();
+            return;
+        } catch (RemoteException e) {
+            System.err.println(e.toString());
+            System.err.println(BMGR_NOT_RUNNING_ERR);
+        }
+    }
+
     private void doRun() {
         try {
             mBmgr.backupNow();
@@ -123,11 +169,14 @@
 
     private void doTransport() {
         try {
-            int which = Integer.parseInt(nextArg());
-            int old = mBmgr.selectBackupTransport(which);
-            System.out.println("Selected transport " + which + " (formerly " + old + ")");
-        } catch (NumberFormatException e) {
-            showUsage();
+            String which = nextArg();
+            String old = mBmgr.selectBackupTransport(which);
+            if (old == null) {
+                System.out.println("Unknown transport '" + which
+                        + "' specified; no changes made.");
+            } else {
+                System.out.println("Selected transport " + which + " (formerly " + old + ")");
+            }
         } catch (RemoteException e) {
             System.err.println(e.toString());
             System.err.println(BMGR_NOT_RUNNING_ERR);
@@ -143,7 +192,7 @@
 
         // The rest of the 'list' options work with a restore session on the current transport
         try {
-            int curTransport = mBmgr.getCurrentTransport();
+            String curTransport = mBmgr.getCurrentTransport();
             mRestore = mBmgr.beginRestoreSession(curTransport);
             if (mRestore == null) {
                 System.err.println(BMGR_NOT_RUNNING_ERR);
@@ -152,6 +201,8 @@
 
             if ("sets".equals(arg)) {
                 doListRestoreSets();
+            } else if ("transports".equals(arg)) {
+                doListTransports();
             }
 
             mRestore.endRestoreSession();
@@ -162,6 +213,22 @@
     }
 
     private void doListTransports() {
+        try {
+            String current = mBmgr.getCurrentTransport();
+            String[] transports = mBmgr.listAllTransports();
+            if (transports == null || transports.length == 0) {
+                System.out.println("No transports available.");
+                return;
+            }
+
+            for (String t : transports) {
+                String pad = (t.equals(current)) ? "  * " : "    ";
+                System.out.println(pad + t);
+            }
+        } catch (RemoteException e) {
+            System.err.println(e.toString());
+            System.err.println(BMGR_NOT_RUNNING_ERR);
+        }
     }
 
     private void doListRestoreSets() {
@@ -170,9 +237,7 @@
             if (sets == null || sets.length == 0) {
                 System.out.println("No restore sets available");
             } else {
-                for (RestoreSet s : sets) {
-                    System.out.println("  " + s.token + " : " + s.name);
-                }
+                printRestoreSets(sets);
             }
         } catch (RemoteException e) {
             System.err.println(e.toString());
@@ -180,17 +245,45 @@
         }
     }
 
+    private void printRestoreSets(RestoreSet[] sets) {
+        for (RestoreSet s : sets) {
+            System.out.println("  " + s.token + " : " + s.name);
+        }
+    }
+
+    class RestoreObserver extends IRestoreObserver.Stub {
+        boolean done;
+        public void restoreStarting(int numPackages) {
+            System.out.println("restoreStarting: " + numPackages + " packages");
+        }
+
+        public void onUpdate(int nowBeingRestored) {
+            System.out.println("onUpdate: " + nowBeingRestored);
+        }
+
+        public void restoreFinished(int error) {
+            System.out.println("restoreFinished: " + error);
+            synchronized (this) {
+                done = true;
+                this.notify();
+            }
+        }
+    }
+
     private void doRestore() {
-        int token;
+        long token;
         try {
-            token = Integer.parseInt(nextArg());
+            token = Long.parseLong(nextArg());
         } catch (NumberFormatException e) {
             showUsage();
             return;
         }
 
+        RestoreObserver observer = new RestoreObserver();
+
         try {
-            int curTransport = mBmgr.getCurrentTransport();
+            boolean didRestore = false;
+            String curTransport = mBmgr.getCurrentTransport();
             mRestore = mBmgr.beginRestoreSession(curTransport);
             if (mRestore == null) {
                 System.err.println(BMGR_NOT_RUNNING_ERR);
@@ -200,15 +293,35 @@
             for (RestoreSet s : sets) {
                 if (s.token == token) {
                     System.out.println("Scheduling restore: " + s.name);
-                    mRestore.performRestore(token);
+                    mRestore.performRestore(token, observer);
+                    didRestore = true;
                     break;
                 }
             }
+            if (!didRestore) {
+                if (sets == null || sets.length == 0) {
+                    System.out.println("No available restore sets; no restore performed");
+                } else {
+                    System.out.println("No matching restore set token.  Available sets:");
+                    printRestoreSets(sets);
+                }
+            }
             mRestore.endRestoreSession();
         } catch (RemoteException e) {
             System.err.println(e.toString());
             System.err.println(BMGR_NOT_RUNNING_ERR);
         }
+
+        // now wait for it to be done
+        synchronized (observer) {
+            while (!observer.done) {
+                try {
+                    observer.wait();
+                } catch (InterruptedException ex) {
+                }
+            }
+        }
+        System.out.println("done");
     }
 
     private String nextArg() {
@@ -222,11 +335,43 @@
 
     private static void showUsage() {
         System.err.println("usage: bmgr [backup|restore|list|transport|run]");
-        System.err.println("       bmgr backup [-f] package");
+        System.err.println("       bmgr backup PACKAGE");
+        System.err.println("       bmgr enable BOOL");
+        System.err.println("       bmgr enabled");
+        System.err.println("       bmgr list transports");
         System.err.println("       bmgr list sets");
-        System.err.println("       #bmgr list transports");
-        System.err.println("       #bmgr transport which#");
-        System.err.println("       bmgr restore token#");
+        System.err.println("       bmgr transport WHICH");
+        System.err.println("       bmgr restore TOKEN");
         System.err.println("       bmgr run");
+        System.err.println("");
+        System.err.println("The 'backup' command schedules a backup pass for the named package.");
+        System.err.println("Note that the backup pass will effectively be a no-op if the package");
+        System.err.println("does not actually have changed data to store.");
+        System.err.println("");
+        System.err.println("The 'enable' command enables or disables the entire backup mechanism.");
+        System.err.println("If the argument is 'true' it will be enabled, otherwise it will be");
+        System.err.println("disabled.  When disabled, neither backup or restore operations will");
+        System.err.println("be performed.");
+        System.err.println("");
+        System.err.println("The 'enabled' command reports the current enabled/disabled state of");
+        System.err.println("the backup mechanism.");
+        System.err.println("");
+        System.err.println("The 'list transports' command reports the names of the backup transports");
+        System.err.println("currently available on the device.  These names can be passed as arguments");
+        System.err.println("to the 'transport' command.  The currently selected transport is indicated");
+        System.err.println("with a '*' character.");
+        System.err.println("");
+        System.err.println("The 'list sets' command reports the token and name of each restore set");
+        System.err.println("available to the device via the current transport.");
+        System.err.println("");
+        System.err.println("The 'transport' command designates the named transport as the currently");
+        System.err.println("active one.  This setting is persistent across reboots.");
+        System.err.println("");
+        System.err.println("The 'restore' command initiates a restore operation, using the restore set");
+        System.err.println("from the current transport whose token matches the argument.");
+        System.err.println("");
+        System.err.println("The 'run' command causes any scheduled backup operation to be initiated");
+        System.err.println("immediately, without the usual waiting period for batching together");
+        System.err.println("data changes.");
     }
-}
\ No newline at end of file
+}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 79588ea..62dc651 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -3697,6 +3697,13 @@
          */
         Locale.setDefault(data.config.locale);
 
+        /*
+         * Update the system configuration since its preloaded and might not
+         * reflect configuration changes. The configuration object passed
+         * in AppBindData can be safely assumed to be up to date
+         */
+        Resources.getSystem().updateConfiguration(mConfiguration, null);
+
         data.info = getPackageInfoNoCheck(data.appInfo);
 
         if (data.debugMode != IApplicationThread.DEBUG_OFF) {
diff --git a/core/java/android/app/BackupAgent.java b/core/java/android/app/BackupAgent.java
index e810775..0ac8a1e 100644
--- a/core/java/android/app/BackupAgent.java
+++ b/core/java/android/app/BackupAgent.java
@@ -67,7 +67,7 @@
      *                 here after writing the requested data to dataFd.
      */
     public abstract void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
-             ParcelFileDescriptor newState);
+             ParcelFileDescriptor newState) throws IOException;
     
     /**
      * The application is being restored from backup, and should replace any
@@ -120,6 +120,9 @@
             BackupDataOutput output = new BackupDataOutput(data.getFileDescriptor());
             try {
                 BackupAgent.this.onBackup(oldState, output, newState);
+            } catch (IOException ex) {
+                Log.d(TAG, "onBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex);
+                throw new RuntimeException(ex);
             } catch (RuntimeException ex) {
                 Log.d(TAG, "onBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex);
                 throw ex;
diff --git a/core/java/android/app/LauncherActivity.java b/core/java/android/app/LauncherActivity.java
index 8d249da..accdda9 100644
--- a/core/java/android/app/LauncherActivity.java
+++ b/core/java/android/app/LauncherActivity.java
@@ -60,26 +60,20 @@
      * An item in the list
      */
     public static class ListItem {
+        public ResolveInfo resolveInfo;
         public CharSequence label;
-        //public CharSequence description;
         public Drawable icon;
         public String packageName;
         public String className;
         public Bundle extras;
         
         ListItem(PackageManager pm, ResolveInfo resolveInfo, IconResizer resizer) {
+            this.resolveInfo = resolveInfo;
             label = resolveInfo.loadLabel(pm);
             if (label == null && resolveInfo.activityInfo != null) {
                 label = resolveInfo.activityInfo.name;
             }
             
-            /*
-            if (resolveInfo.activityInfo != null &&
-                    resolveInfo.activityInfo.applicationInfo != null) {
-                description = resolveInfo.activityInfo.applicationInfo.loadDescription(pm);
-            }
-            */
-            
             icon = resizer.createIconThumbnail(resolveInfo.loadIcon(pm));
             packageName = resolveInfo.activityInfo.applicationInfo.packageName;
             className = resolveInfo.activityInfo.name;
@@ -122,6 +116,14 @@
             return intent;
         }
 
+        public ListItem itemForPosition(int position) {
+            if (mActivitiesList == null) {
+                return null;
+            }
+
+            return mActivitiesList.get(position);
+        }
+
         public int getCount() {
             return mActivitiesList != null ? mActivitiesList.size() : 0;
         }
@@ -354,6 +356,16 @@
     }
     
     /**
+     * Return the {@link ListItem} for a specific position in our
+     * {@link android.widget.ListView}.
+     * @param position The item to return
+     */
+    protected ListItem itemForPosition(int position) {
+        ActivityAdapter adapter = (ActivityAdapter) mAdapter;
+        return adapter.itemForPosition(position);
+    }
+    
+    /**
      * Get the base intent to use when running
      * {@link PackageManager#queryIntentActivities(Intent, int)}.
      */
diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java
index 6ddf50f..6fe4896 100644
--- a/core/java/android/app/SearchDialog.java
+++ b/core/java/android/app/SearchDialog.java
@@ -33,7 +33,6 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.database.Cursor;
-import android.graphics.drawable.AnimationDrawable;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.Bundle;
@@ -107,7 +106,7 @@
     private Button mGoButton;
     private ImageButton mVoiceButton;
     private View mSearchPlate;
-    private AnimationDrawable mWorkingSpinner;
+    private Drawable mWorkingSpinner;
 
     // interaction with searchable application
     private SearchableInfo mSearchable;
@@ -188,7 +187,7 @@
         mGoButton = (Button) findViewById(com.android.internal.R.id.search_go_btn);
         mVoiceButton = (ImageButton) findViewById(com.android.internal.R.id.search_voice_btn);
         mSearchPlate = findViewById(com.android.internal.R.id.search_plate);
-        mWorkingSpinner = (AnimationDrawable) getContext().getResources().
+        mWorkingSpinner = getContext().getResources().
                 getDrawable(com.android.internal.R.drawable.search_spinner);
         
         // attach listeners
@@ -423,11 +422,11 @@
         if (working) {
             mSearchAutoComplete.setCompoundDrawablesWithIntrinsicBounds(
                     null, null, mWorkingSpinner, null);
-            mWorkingSpinner.start();
+//            mWorkingSpinner.start();
         } else {
             mSearchAutoComplete.setCompoundDrawablesWithIntrinsicBounds(
                     null, null, null, null);
-            mWorkingSpinner.stop();
+//            mWorkingSpinner.stop();
         }
     }
     
@@ -601,7 +600,7 @@
                     mSearchPlate.getPaddingBottom());
         } else {
             PackageManager pm = getContext().getPackageManager();
-            Drawable icon = null;
+            Drawable icon;
             try {
                 ActivityInfo info = pm.getActivityInfo(mLaunchComponent, 0);
                 icon = pm.getApplicationIcon(info.applicationInfo);
diff --git a/core/java/android/backup/BackupHelperAgent.java b/core/java/android/backup/BackupHelperAgent.java
index 3720d50..5d0c4a2 100644
--- a/core/java/android/backup/BackupHelperAgent.java
+++ b/core/java/android/backup/BackupHelperAgent.java
@@ -34,7 +34,7 @@
 
     @Override
     public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
-             ParcelFileDescriptor newState) {
+             ParcelFileDescriptor newState) throws IOException {
         mDispatcher.performBackup(oldState, data, newState);
     }
 
diff --git a/core/java/android/backup/BackupHelperDispatcher.java b/core/java/android/backup/BackupHelperDispatcher.java
index b25c3e3..6ccb83e 100644
--- a/core/java/android/backup/BackupHelperDispatcher.java
+++ b/core/java/android/backup/BackupHelperDispatcher.java
@@ -19,7 +19,10 @@
 import android.os.ParcelFileDescriptor;
 import android.util.Log;
 
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
 import java.io.IOException;
+import java.io.FileDescriptor;
 import java.util.TreeMap;
 import java.util.Map;
 
@@ -27,6 +30,11 @@
 public class BackupHelperDispatcher {
     private static final String TAG = "BackupHelperDispatcher";
 
+    private static class Header {
+        int chunkSize; // not including the header
+        String keyPrefix;
+    }
+
     TreeMap<String,BackupHelper> mHelpers = new TreeMap<String,BackupHelper>();
     
     public BackupHelperDispatcher() {
@@ -36,13 +44,63 @@
         mHelpers.put(keyPrefix, helper);
     }
 
-    /** TODO: Make this save and restore the key prefix. */
     public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
-             ParcelFileDescriptor newState) {
-        // Write out the state files -- mHelpers is a TreeMap, so the order is well defined.
-        for (Map.Entry<String,BackupHelper> entry: mHelpers.entrySet()) {
-            data.setKeyPrefix(entry.getKey());
-            entry.getValue().performBackup(oldState, data, newState);
+             ParcelFileDescriptor newState) throws IOException {
+        // First, do the helpers that we've already done, since they're already in the state
+        // file.
+        int err;
+        Header header = new Header();
+        TreeMap<String,BackupHelper> helpers = (TreeMap<String,BackupHelper>)mHelpers.clone();
+        FileDescriptor oldStateFD = null;
+        FileDescriptor newStateFD = newState.getFileDescriptor();
+
+        if (oldState != null) {
+            oldStateFD = oldState.getFileDescriptor();
+            while ((err = readHeader_native(header, oldStateFD)) >= 0) {
+                if (err == 0) {
+                    BackupHelper helper = helpers.get(header.keyPrefix);
+                    Log.d(TAG, "handling existing helper '" + header.keyPrefix + "' " + helper);
+                    if (helper != null) {
+                        doOneBackup(oldState, data, newState, header, helper);
+                        helpers.remove(header.keyPrefix);
+                    } else {
+                        skipChunk_native(oldStateFD, header.chunkSize);
+                    }
+                }
+            }
+        }
+
+        // Then go through and do the rest that we haven't done.
+        for (Map.Entry<String,BackupHelper> entry: helpers.entrySet()) {
+            header.keyPrefix = entry.getKey();
+            Log.d(TAG, "handling new helper '" + header.keyPrefix + "'");
+            BackupHelper helper = entry.getValue();
+            doOneBackup(oldState, data, newState, header, helper);
+        }
+    }
+
+    private void doOneBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
+            ParcelFileDescriptor newState, Header header, BackupHelper helper) 
+            throws IOException {
+        int err;
+        FileDescriptor newStateFD = newState.getFileDescriptor();
+
+        // allocate space for the header in the file
+        int pos = allocateHeader_native(header, newStateFD);
+        if (pos < 0) {
+            throw new IOException("allocateHeader_native failed (error " + pos + ")");
+        }
+
+        data.setKeyPrefix(header.keyPrefix);
+
+        // do the backup
+        helper.performBackup(oldState, data, newState);
+
+        // fill in the header (seeking back to pos).  The file pointer will be returned to
+        // where it was at the end of performBackup.  Header.chunkSize will not be filled in.
+        err = writeHeader_native(header, newStateFD, pos);
+        if (err != 0) {
+            throw new IOException("writeHeader_native failed (error " + err + ")");
         }
     }
 
@@ -83,5 +141,11 @@
             helper.writeRestoreSnapshot(newState);
         }
     }
+
+    private static native int readHeader_native(Header h, FileDescriptor fd);
+    private static native int skipChunk_native(FileDescriptor fd, int bytesToSkip);
+
+    private static native int allocateHeader_native(Header h, FileDescriptor fd);
+    private static native int writeHeader_native(Header h, FileDescriptor fd, int pos);
 }
 
diff --git a/core/java/android/backup/BackupManager.java b/core/java/android/backup/BackupManager.java
index 8df7eae..5b4ac0d 100644
--- a/core/java/android/backup/BackupManager.java
+++ b/core/java/android/backup/BackupManager.java
@@ -68,9 +68,11 @@
      * {@link android.app.BackupAgent} subclass will be scheduled when you call this method.
      */
     public void dataChanged() {
-        try {
-            mService.dataChanged(mContext.getPackageName());
-        } catch (RemoteException e) {
+        if (mService != null) {
+            try {
+                mService.dataChanged(mContext.getPackageName());
+            } catch (RemoteException e) {
+            }
         }
     }
 
@@ -81,11 +83,13 @@
      *
      * {@hide}
      */
-    public IRestoreSession beginRestoreSession(int transportID) {
+    public IRestoreSession beginRestoreSession(String transport) {
         IRestoreSession binder = null;
-        try {
-            binder = mService.beginRestoreSession(transportID);
-        } catch (RemoteException e) {
+        if (mService != null) {
+            try {
+                binder = mService.beginRestoreSession(transport);
+            } catch (RemoteException e) {
+            }
         }
         return binder;
     }
diff --git a/core/java/android/backup/IBackupManager.aidl b/core/java/android/backup/IBackupManager.aidl
index d6283d0..1f11762 100644
--- a/core/java/android/backup/IBackupManager.aidl
+++ b/core/java/android/backup/IBackupManager.aidl
@@ -48,6 +48,24 @@
     void agentDisconnected(String packageName);
 
     /**
+     * Enable/disable the backup service entirely.  When disabled, no backup
+     * or restore operations will take place.  Data-changed notifications will
+     * still be observed and collected, however, so that changes made while the
+     * mechanism was disabled will still be backed up properly if it is enabled
+     * at some point in the future.
+     *
+     * <p>Callers must hold the android.permission.BACKUP permission to use this method.
+     */
+    void setBackupEnabled(boolean isEnabled);
+
+    /**
+     * Report whether the backup mechanism is currently enabled.
+     *
+     * <p>Callers must hold the android.permission.BACKUP permission to use this method.
+     */
+    boolean isBackupEnabled();
+
+    /**
      * Schedule an immediate backup attempt for all pending updates.  This is
      * primarily intended for transports to use when they detect a suitable
      * opportunity for doing a backup pass.  If there are no pending updates to
@@ -63,23 +81,32 @@
      * Identify the currently selected transport.  Callers must hold the
      * android.permission.BACKUP permission to use this method.
      */
-    int getCurrentTransport();
+    String getCurrentTransport();
 
     /**
-     * Specify a default backup transport.  Callers must hold the
+     * Request a list of all available backup transports' names.  Callers must
+     * hold the android.permission.BACKUP permission to use this method.
+     */
+    String[] listAllTransports();
+
+    /**
+     * Specify the current backup transport.  Callers must hold the
      * android.permission.BACKUP permission to use this method.
      *
-     * @param transportID The ID of the transport to select.  This should be one
+     * @param transport The name of the transport to select.  This should be one
      * of {@link BackupManager.TRANSPORT_GOOGLE} or {@link BackupManager.TRANSPORT_ADB}.
-     * @return The ID of the previously selected transport.
+     * @return The name of the previously selected transport.  If the given transport
+     *   name is not one of the currently available transports, no change is made to
+     *   the current transport setting and the method returns null.
      */
-    int selectBackupTransport(int transportID);
+    String selectBackupTransport(String transport);
 
     /**
      * Begin a restore session with the given transport (which may differ from the
      * currently-active backup transport).
      *
+     * @param transport The name of the transport to use for the restore operation.
      * @return An interface to the restore session, or null on error.
      */
-    IRestoreSession beginRestoreSession(int transportID);
+    IRestoreSession beginRestoreSession(String transportID);
 }
diff --git a/core/java/android/backup/IRestoreObserver.aidl b/core/java/android/backup/IRestoreObserver.aidl
new file mode 100644
index 0000000..59e59fc
--- /dev/null
+++ b/core/java/android/backup/IRestoreObserver.aidl
@@ -0,0 +1,50 @@
+/*
+ * 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 android.backup;
+
+/**
+ * Callback class for receiving progress reports during a restore operation.
+ *
+ * @hide
+ */
+interface IRestoreObserver {
+    /**
+     * The restore operation has begun.
+     *
+     * @param numPackages The total number of packages being processed in
+     *   this restore operation.
+     */
+    void restoreStarting(int numPackages);
+
+    /**
+     * An indication of which package is being restored currently, out of the
+     * total number provided in the restoreStarting() callback.  This method
+     * is not guaranteed to be called.
+     *
+     * @param nowBeingRestored The index, between 1 and the numPackages parameter
+     *   to the restoreStarting() callback, of the package now being restored.
+     */
+    void onUpdate(int nowBeingRestored);
+
+    /**
+     * The restore operation has completed.
+     *
+     * @param error Zero on success; a nonzero error code if the restore operation
+     *   as a whole failed.
+     */
+    void restoreFinished(int error);
+}
diff --git a/core/java/android/backup/IRestoreSession.aidl b/core/java/android/backup/IRestoreSession.aidl
index 6bca865..2a1fbc1 100644
--- a/core/java/android/backup/IRestoreSession.aidl
+++ b/core/java/android/backup/IRestoreSession.aidl
@@ -17,6 +17,7 @@
 package android.backup;
 
 import android.backup.RestoreSet;
+import android.backup.IRestoreObserver;
 
 /**
  * Binder interface used by clients who wish to manage a restore operation.  Every
@@ -41,8 +42,10 @@
      *
      * @param token The token from {@link getAvailableRestoreSets()} corresponding to
      *   the restore set that should be used.
+     * @param observer If non-null, this binder points to an object that will receive
+     *   progress callbacks during the restore operation.
      */
-    int performRestore(int token);
+    int performRestore(long token, IRestoreObserver observer);
 
     /**
      * End this restore session.  After this method is called, the IRestoreSession binder
diff --git a/core/java/android/backup/SharedPreferencesBackupHelper.java b/core/java/android/backup/SharedPreferencesBackupHelper.java
index f492629..4a7b399 100644
--- a/core/java/android/backup/SharedPreferencesBackupHelper.java
+++ b/core/java/android/backup/SharedPreferencesBackupHelper.java
@@ -30,7 +30,7 @@
     private Context mContext;
     private String[] mPrefGroups;
 
-    public SharedPreferencesBackupHelper(Context context, String[] prefGroups) {
+    public SharedPreferencesBackupHelper(Context context, String... prefGroups) {
         super(context);
 
         mContext = context;
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 85d877a..27783ef 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -235,6 +235,12 @@
     public static final int CONFIG_ORIENTATION = 0x0080;
     /**
      * Bit in {@link #configChanges} that indicates that the activity
+     * can itself handle changes to the screen layout.  Set from the
+     * {@link android.R.attr#configChanges} attribute.
+     */
+    public static final int CONFIG_SCREEN_LAYOUT = 0x0100;
+    /**
+     * Bit in {@link #configChanges} that indicates that the activity
      * can itself handle changes to the font scaling factor.  Set from the
      * {@link android.R.attr#configChanges} attribute.  This is
      * not a core resource configutation, but a higher-level value, so its
@@ -248,8 +254,8 @@
      * Contains any combination of {@link #CONFIG_FONT_SCALE},
      * {@link #CONFIG_MCC}, {@link #CONFIG_MNC},
      * {@link #CONFIG_LOCALE}, {@link #CONFIG_TOUCHSCREEN},
-     * {@link #CONFIG_KEYBOARD}, {@link #CONFIG_NAVIGATION}, and
-     * {@link #CONFIG_ORIENTATION}.  Set from the
+     * {@link #CONFIG_KEYBOARD}, {@link #CONFIG_NAVIGATION},
+     * {@link #CONFIG_ORIENTATION}, and {@link #CONFIG_SCREEN_LAYOUT}.  Set from the
      * {@link android.R.attr#configChanges} attribute.
      */
     public int configChanges;
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 2a2cf93..bcf95b6 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -138,10 +138,27 @@
 
     /**
      * Value for {@link #flags}: true when the application's window can be
-     * expanded over default window size in target density (320x480 for
-     * 1.0 density, 480x720 for 1.5 density etc)
+     * reduced in size for smaller screens.  Corresponds to
+     * {@link android.R.styleable#AndroidManifestSupportsScreens_smallScreens
+     * android:smallScreens}.
      */
-    public static final int FLAG_SUPPORTS_LARGE_SCREENS = 1<<9;
+    public static final int FLAG_SUPPORTS_SMALL_SCREENS = 1<<9;
+    
+    /**
+     * Value for {@link #flags}: true when the application's window can be
+     * displayed on normal screens.  Corresponds to
+     * {@link android.R.styleable#AndroidManifestSupportsScreens_normalScreens
+     * android:normalScreens}.
+     */
+    public static final int FLAG_SUPPORTS_NORMAL_SCREENS = 1<<10; 
+    
+    /**
+     * Value for {@link #flags}: true when the application's window can be
+     * increased in size for larger screens.  Corresponds to
+     * {@link android.R.styleable#AndroidManifestSupportsScreens_largeScreens
+     * android:smallScreens}.
+     */
+    public static final int FLAG_SUPPORTS_LARGE_SCREENS = 1<<11;
     
     /**
      * Value for {@link #flags}: this is false if the application has set
@@ -149,7 +166,7 @@
      * 
      * {@hide}
      */
-    public static final int FLAG_ALLOW_BACKUP = 1<<10;
+    public static final int FLAG_ALLOW_BACKUP = 1<<12;
     
     /**
      * Indicates that the application supports any densities;
@@ -164,7 +181,9 @@
      * {@link #FLAG_PERSISTENT}, {@link #FLAG_FACTORY_TEST}, and
      * {@link #FLAG_ALLOW_TASK_REPARENTING}
      * {@link #FLAG_ALLOW_CLEAR_USER_DATA}, {@link #FLAG_UPDATED_SYSTEM_APP},
-     * {@link #FLAG_TEST_ONLY}.
+     * {@link #FLAG_TEST_ONLY}, {@link #FLAG_SUPPORTS_SMALL_SCREENS},
+     * {@link #FLAG_SUPPORTS_NORMAL_SCREENS},
+     * {@link #FLAG_SUPPORTS_LARGE_SCREENS}.
      */
     public int flags = 0;
     
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index ab9518e..558b0c3 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -668,6 +668,11 @@
         }
         sa.recycle();
 
+        // Resource boolean are -1, so 1 means we don't know the value.
+        int supportsSmallScreens = 1;
+        int supportsNormalScreens = 1;
+        int supportsLargeScreens = 1;
+        
         int outerDepth = parser.getDepth();
         while ((type=parser.next()) != parser.END_DOCUMENT
                && (type != parser.END_TAG || parser.getDepth() > outerDepth)) {
@@ -876,8 +881,24 @@
 
                 XmlUtils.skipCurrentTag(parser);
 
-            } else if (tagName.equals("expandable")) {
-                pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS;
+            } else if (tagName.equals("supports-screens")) {
+                sa = res.obtainAttributes(attrs,
+                        com.android.internal.R.styleable.AndroidManifestSupportsScreens);
+
+                // This is a trick to get a boolean and still able to detect
+                // if a value was actually set.
+                supportsSmallScreens = sa.getInteger(
+                        com.android.internal.R.styleable.AndroidManifestSupportsScreens_smallScreens,
+                        supportsSmallScreens);
+                supportsNormalScreens = sa.getInteger(
+                        com.android.internal.R.styleable.AndroidManifestSupportsScreens_normalScreens,
+                        supportsNormalScreens);
+                supportsLargeScreens = sa.getInteger(
+                        com.android.internal.R.styleable.AndroidManifestSupportsScreens_largeScreens,
+                        supportsLargeScreens);
+
+                sa.recycle();
+                
                 XmlUtils.skipCurrentTag(parser);
             } else {
                 Log.w(TAG, "Bad element under <manifest>: "
@@ -910,7 +931,20 @@
             pkg.usesLibraryFiles = new String[pkg.usesLibraries.size()];
             pkg.usesLibraries.toArray(pkg.usesLibraryFiles);
         }
-        // TODO: enable all density & expandable if target sdk is higher than donut 
+        
+        if (supportsSmallScreens < 0 || (supportsSmallScreens > 0
+                && pkg.applicationInfo.targetSdkVersion
+                        >= android.os.Build.VERSION_CODES.CUR_DEVELOPMENT)) {
+            pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS;
+        }
+        if (supportsNormalScreens != 0) {
+            pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS;
+        }
+        if (supportsLargeScreens < 0 || (supportsLargeScreens > 0
+                && pkg.applicationInfo.targetSdkVersion
+                        >= android.os.Build.VERSION_CODES.CUR_DEVELOPMENT)) {
+            pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS;
+        }
         
         int size = pkg.supportsDensityList.size();
         if (size > 0) {
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 1c91736..5c7b01f 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -601,7 +601,7 @@
     public native final void setConfiguration(int mcc, int mnc, String locale,
             int orientation, int touchscreen, int density, int keyboard,
             int keyboardHidden, int navigation, int screenWidth, int screenHeight,
-            int majorVersion);
+            int screenLayout, int majorVersion);
 
     /**
      * Retrieve the resource identifier for the given resource name.
diff --git a/core/java/android/content/res/CompatibilityInfo.java b/core/java/android/content/res/CompatibilityInfo.java
index 179b9bd..4e6fe07 100644
--- a/core/java/android/content/res/CompatibilityInfo.java
+++ b/core/java/android/content/res/CompatibilityInfo.java
@@ -65,7 +65,7 @@
     /**
      *  A compatibility flags
      */
-    private int compatibilityFlags;
+    private int mCompatibilityFlags;
     
     /**
      * A flag mask to tell if the application needs scaling (when mApplicationScale != 1.0f)
@@ -101,7 +101,11 @@
      */
     public final float applicationInvertedScale;
 
-
+    /**
+     * The flags from ApplicationInfo.
+     */
+    public final int appFlags;
+    
     /**
      * Window size in Compatibility Mode, in real pixels. This is updated by
      * {@link DisplayMetrics#updateMetrics}.
@@ -117,8 +121,10 @@
     private int mXOffset;
 
     public CompatibilityInfo(ApplicationInfo appInfo) {
+        appFlags = appInfo.flags;
+        
         if ((appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) != 0) {
-            compatibilityFlags = EXPANDABLE | CONFIGURED_EXPANDABLE;
+            mCompatibilityFlags = EXPANDABLE | CONFIGURED_EXPANDABLE;
         }
         
         float packageDensityScale = -1.0f;
@@ -149,13 +155,16 @@
         }
         applicationInvertedScale = 1.0f / applicationScale;
         if (applicationScale != 1.0f) {
-            compatibilityFlags |= SCALING_REQUIRED;
+            mCompatibilityFlags |= SCALING_REQUIRED;
         }
     }
 
     private CompatibilityInfo() {
+        appFlags = ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS
+                | ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS
+                | ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS;
         applicationScale = applicationInvertedScale = 1.0f;
-        compatibilityFlags = EXPANDABLE | CONFIGURED_EXPANDABLE;
+        mCompatibilityFlags = EXPANDABLE | CONFIGURED_EXPANDABLE;
     }
 
     /**
@@ -175,9 +184,9 @@
      */
     public void setExpandable(boolean expandable) {
         if (expandable) {
-            compatibilityFlags |= CompatibilityInfo.EXPANDABLE;
+            mCompatibilityFlags |= CompatibilityInfo.EXPANDABLE;
         } else {
-            compatibilityFlags &= ~CompatibilityInfo.EXPANDABLE;
+            mCompatibilityFlags &= ~CompatibilityInfo.EXPANDABLE;
         }
     }
 
@@ -185,20 +194,20 @@
      * @return true if the application is configured to be expandable.
      */
     public boolean isConfiguredExpandable() {
-        return (compatibilityFlags & CompatibilityInfo.CONFIGURED_EXPANDABLE) != 0;
+        return (mCompatibilityFlags & CompatibilityInfo.CONFIGURED_EXPANDABLE) != 0;
     }
 
     /**
      * @return true if the scaling is required
      */
     public boolean isScalingRequired() {
-        return (compatibilityFlags & SCALING_REQUIRED) != 0;
+        return (mCompatibilityFlags & SCALING_REQUIRED) != 0;
     }
     
     @Override
     public String toString() {
         return "CompatibilityInfo{scale=" + applicationScale +
-                ", compatibility flag=" + compatibilityFlags + "}"; 
+                ", compatibility flag=" + mCompatibilityFlags + "}"; 
     }
 
     /**
@@ -222,13 +231,13 @@
      * @param params the window's parameter
      */
     public Translator getTranslator(WindowManager.LayoutParams params) {
-        if ( (compatibilityFlags & CompatibilityInfo.SCALING_EXPANDABLE_MASK)
+        if ( (mCompatibilityFlags & CompatibilityInfo.SCALING_EXPANDABLE_MASK)
                 == CompatibilityInfo.EXPANDABLE) {
             if (DBG) Log.d(TAG, "no translation required");
             return null;
         }
         
-        if ((compatibilityFlags & CompatibilityInfo.EXPANDABLE) == 0) {
+        if ((mCompatibilityFlags & CompatibilityInfo.EXPANDABLE) == 0) {
             if ((params.flags & WindowManager.LayoutParams.FLAG_NO_COMPATIBILITY_SCALING) != 0) {
                 if (DBG) Log.d(TAG, "translation for surface view selected");
                 return new Translator(X_SHIFT_WINDOW, false, 1.0f, 1.0f);
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index bb3486c..577aa60 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -116,6 +116,18 @@
      */
     public int orientation;
     
+    public static final int SCREENLAYOUT_UNDEFINED = 0;
+    public static final int SCREENLAYOUT_SMALL = 1;
+    public static final int SCREENLAYOUT_NORMAL = 2;
+    public static final int SCREENLAYOUT_LARGE = 3;
+    
+    /**
+     * Overall layout of the screen.  May be one of
+     * {@link #SCREENLAYOUT_SMALL}, {@link #SCREENLAYOUT_NORMAL},
+     * or {@link #SCREENLAYOUT_LARGE}.
+     */
+    public int screenLayout;
+    
     /**
      * Construct an invalid Configuration.  You must call {@link #setToDefaults}
      * for this object to be valid.  {@more}
@@ -141,6 +153,7 @@
         hardKeyboardHidden = o.hardKeyboardHidden;
         navigation = o.navigation;
         orientation = o.orientation;
+        screenLayout = o.screenLayout;
     }
 
     public String toString() {
@@ -165,6 +178,8 @@
         sb.append(navigation);
         sb.append(" orien=");
         sb.append(orientation);
+        sb.append(" layout=");
+        sb.append(screenLayout);
         sb.append('}');
         return sb.toString();
     }
@@ -183,6 +198,7 @@
         hardKeyboardHidden = HARDKEYBOARDHIDDEN_UNDEFINED;
         navigation = NAVIGATION_UNDEFINED;
         orientation = ORIENTATION_UNDEFINED;
+        screenLayout = SCREENLAYOUT_UNDEFINED;
     }
 
     /** {@hide} */
@@ -253,6 +269,11 @@
             changed |= ActivityInfo.CONFIG_ORIENTATION;
             orientation = delta.orientation;
         }
+        if (delta.screenLayout != SCREENLAYOUT_UNDEFINED
+                && screenLayout != delta.screenLayout) {
+            changed |= ActivityInfo.CONFIG_SCREEN_LAYOUT;
+            screenLayout = delta.screenLayout;
+        }
         
         return changed;
     }
@@ -276,9 +297,11 @@
      * {@link android.content.pm.ActivityInfo#CONFIG_KEYBOARD
      * PackageManager.ActivityInfo.CONFIG_KEYBOARD},
      * {@link android.content.pm.ActivityInfo#CONFIG_NAVIGATION
-     * PackageManager.ActivityInfo.CONFIG_NAVIGATION}, or
+     * PackageManager.ActivityInfo.CONFIG_NAVIGATION},
      * {@link android.content.pm.ActivityInfo#CONFIG_ORIENTATION
-     * PackageManager.ActivityInfo.CONFIG_ORIENTATION}.
+     * PackageManager.ActivityInfo.CONFIG_ORIENTATION}, or
+     * {@link android.content.pm.ActivityInfo#CONFIG_SCREEN_LAYOUT
+     * PackageManager.ActivityInfo.CONFIG_SCREEN_LAYOUT}.
      */
     public int diff(Configuration delta) {
         int changed = 0;
@@ -319,6 +342,10 @@
                 && orientation != delta.orientation) {
             changed |= ActivityInfo.CONFIG_ORIENTATION;
         }
+        if (delta.screenLayout != SCREENLAYOUT_UNDEFINED
+                && screenLayout != delta.screenLayout) {
+            changed |= ActivityInfo.CONFIG_SCREEN_LAYOUT;
+        }
         
         return changed;
     }
@@ -368,6 +395,7 @@
         dest.writeInt(hardKeyboardHidden);
         dest.writeInt(navigation);
         dest.writeInt(orientation);
+        dest.writeInt(screenLayout);
     }
 
     public static final Parcelable.Creator<Configuration> CREATOR
@@ -399,6 +427,7 @@
         hardKeyboardHidden = source.readInt();
         navigation = source.readInt();
         orientation = source.readInt();
+        screenLayout = source.readInt();
     }
 
     public int compareTo(Configuration that) {
@@ -428,6 +457,8 @@
         n = this.navigation - that.navigation;
         if (n != 0) return n;
         n = this.orientation - that.orientation;
+        if (n != 0) return n;
+        n = this.screenLayout - that.screenLayout;
         //if (n != 0) return n;
         return n;
     }
@@ -450,6 +481,6 @@
         return ((int)this.fontScale) + this.mcc + this.mnc
                 + this.locale.hashCode() + this.touchscreen
                 + this.keyboard + this.keyboardHidden + this.hardKeyboardHidden
-                + this.navigation + this.orientation;
+                + this.navigation + this.orientation + this.screenLayout;
     }
 }
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index cb9d46e..d7512bb 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -1267,7 +1267,8 @@
             }
             if (metrics != null) {
                 mMetrics.setTo(metrics);
-                mMetrics.updateMetrics(mCompatibilityInfo, mConfiguration.orientation);
+                mMetrics.updateMetrics(mCompatibilityInfo,
+                        mConfiguration.orientation, mConfiguration.screenLayout);
             }
             mMetrics.scaledDensity = mMetrics.density * mConfiguration.fontScale;
 
@@ -1299,7 +1300,7 @@
                     mConfiguration.touchscreen,
                     (int)(mMetrics.density*160), mConfiguration.keyboard,
                     keyboardHidden, mConfiguration.navigation, width, height,
-                    sSdkVersion);
+                    mConfiguration.screenLayout, sSdkVersion);
             int N = mDrawableCache.size();
             if (DEBUG_CONFIG) {
                 Log.d(TAG, "Cleaning up drawables config changes: 0x"
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index ca579b6..3ce951f 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -39,13 +39,16 @@
 public class Camera {
     private static final String TAG = "Camera";
     
-    // These match the enum in libs/android_runtime/android_hardware_Camera.cpp
-    private static final int SHUTTER_CALLBACK = 0;
-    private static final int RAW_PICTURE_CALLBACK = 1;
-    private static final int JPEG_PICTURE_CALLBACK = 2;
-    private static final int PREVIEW_CALLBACK = 3;
-    private static final int AUTOFOCUS_CALLBACK = 4;
-    private static final int ERROR_CALLBACK = 5;
+    // These match the enums in frameworks/base/include/ui/Camera.h
+    private static final int CAMERA_MSG_ERROR = 0;
+    private static final int CAMERA_MSG_SHUTTER = 1;
+    private static final int CAMERA_MSG_FOCUS = 2;
+    private static final int CAMERA_MSG_ZOOM = 3;
+    private static final int CAMERA_MSG_PREVIEW_FRAME = 4;
+    private static final int CAMERA_MSG_VIDEO_FRAME = 5;
+    private static final int CAMERA_MSG_POSTVIEW_FRAME = 6;
+    private static final int CAMERA_MSG_RAW_IMAGE = 7;
+    private static final int CAMERA_MSG_COMPRESSED_IMAGE = 8;
 
     private int mNativeContext; // accessed by native methods
     private EventHandler mEventHandler;
@@ -152,7 +155,11 @@
      * @throws IOException if the method fails.
      */
     public final void setPreviewDisplay(SurfaceHolder holder) throws IOException {
-        setPreviewDisplay(holder.getSurface());
+        if (holder != null) {
+            setPreviewDisplay(holder.getSurface());
+        } else {
+            setPreviewDisplay((Surface)null);
+        }
     }
 
     private native final void setPreviewDisplay(Surface surface);
@@ -231,22 +238,23 @@
         @Override
         public void handleMessage(Message msg) {
             switch(msg.what) {
-            case SHUTTER_CALLBACK:
+            case CAMERA_MSG_SHUTTER:
                 if (mShutterCallback != null) {
                     mShutterCallback.onShutter();
                 }
                 return;
-            case RAW_PICTURE_CALLBACK:
+
+            case CAMERA_MSG_RAW_IMAGE:
                 if (mRawImageCallback != null)
                     mRawImageCallback.onPictureTaken((byte[])msg.obj, mCamera);
                 return;
 
-            case JPEG_PICTURE_CALLBACK:
+            case CAMERA_MSG_COMPRESSED_IMAGE:
                 if (mJpegCallback != null)
                     mJpegCallback.onPictureTaken((byte[])msg.obj, mCamera);
                 return;
             
-            case PREVIEW_CALLBACK:
+            case CAMERA_MSG_PREVIEW_FRAME:
                 if (mPreviewCallback != null) {
                     mPreviewCallback.onPreviewFrame((byte[])msg.obj, mCamera);
                     if (mOneShot) {
@@ -255,12 +263,12 @@
                 }
                 return;
 
-            case AUTOFOCUS_CALLBACK:
+            case CAMERA_MSG_FOCUS:
                 if (mAutoFocusCallback != null)
                     mAutoFocusCallback.onAutoFocus(msg.arg1 == 0 ? false : true, mCamera);
                 return;
 
-            case ERROR_CALLBACK:
+            case CAMERA_MSG_ERROR :
                 Log.e(TAG, "Error " + msg.arg1);
                 if (mErrorCallback != null)
                     mErrorCallback.onError(msg.arg1, mCamera);
diff --git a/core/java/android/net/http/RequestHandle.java b/core/java/android/net/http/RequestHandle.java
index c4ee5b0..6a97951 100644
--- a/core/java/android/net/http/RequestHandle.java
+++ b/core/java/android/net/http/RequestHandle.java
@@ -159,11 +159,11 @@
             e.printStackTrace();
         }
 
-        // update the "cookie" header based on the redirected url
-        mHeaders.remove("cookie");
+        // update the "Cookie" header based on the redirected url
+        mHeaders.remove("Cookie");
         String cookie = CookieManager.getInstance().getCookie(mUri);
         if (cookie != null && cookie.length() > 0) {
-            mHeaders.put("cookie", cookie);
+            mHeaders.put("Cookie", cookie);
         }
 
         if ((statusCode == 302 || statusCode == 303) && mMethod.equals("POST")) {
diff --git a/core/java/android/os/AsyncTask.java b/core/java/android/os/AsyncTask.java
index 6c13582..abfb274 100644
--- a/core/java/android/os/AsyncTask.java
+++ b/core/java/android/os/AsyncTask.java
@@ -127,12 +127,12 @@
 public abstract class AsyncTask<Params, Progress, Result> {
     private static final String LOG_TAG = "AsyncTask";
 
-    private static final int CORE_POOL_SIZE = 1;
-    private static final int MAXIMUM_POOL_SIZE = 10;
+    private static final int CORE_POOL_SIZE = 5;
+    private static final int MAXIMUM_POOL_SIZE = 128;
     private static final int KEEP_ALIVE = 10;
 
     private static final BlockingQueue<Runnable> sWorkQueue =
-            new LinkedBlockingQueue<Runnable>(MAXIMUM_POOL_SIZE);
+            new LinkedBlockingQueue<Runnable>(10);
 
     private static final ThreadFactory sThreadFactory = new ThreadFactory() {
         private final AtomicInteger mCount = new AtomicInteger(1);
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 333c7cb..1214abc 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -573,7 +573,21 @@
      * directly to a gid.
      */
     public static final native int getGidForName(String name);
-    
+
+    /**
+     * Returns a uid for a currently running process.
+     * @param pid the process id
+     * @return the uid of the process, or -1 if the process is not running.
+     * @hide pending API council review
+     */
+    public static final int getUidForPid(int pid) {
+        String[] procStatusLabels = { "Uid:" };
+        long[] procStatusValues = new long[1];
+        procStatusValues[0] = -1;
+        Process.readProcLines("/proc/" + pid + "/status", procStatusLabels, procStatusValues);
+        return (int) procStatusValues[0];
+    }
+
     /**
      * Set the priority of a thread, based on Linux priorities.
      * 
diff --git a/core/java/android/preference/PreferenceScreen.java b/core/java/android/preference/PreferenceScreen.java
index 6ea2528..95e54324 100644
--- a/core/java/android/preference/PreferenceScreen.java
+++ b/core/java/android/preference/PreferenceScreen.java
@@ -150,7 +150,7 @@
 
         // Set the title bar if title is available, else no title bar
         final CharSequence title = getTitle();
-        Dialog dialog = mDialog = new Dialog(context, !TextUtils.isEmpty(title)
+        Dialog dialog = mDialog = new Dialog(context, TextUtils.isEmpty(title)
                 ? com.android.internal.R.style.Theme_NoTitleBar
                 : com.android.internal.R.style.Theme);
         dialog.setContentView(listView);
diff --git a/core/java/android/provider/Browser.java b/core/java/android/provider/Browser.java
index 789fdff..1ba5e25e 100644
--- a/core/java/android/provider/Browser.java
+++ b/core/java/android/provider/Browser.java
@@ -91,6 +91,17 @@
      */
     public static final String EXTRA_APPEND_LOCATION = "com.android.browser.append_location";
 
+    /**
+     * The name of the extra data in the VIEW intent. The data is in the format of
+     * a byte array.
+     * <p>
+     * Any value sent here will be passed in the http request to the provided url as post data.
+     * <p>
+     * pending api approval
+     * @hide
+     */
+    public static final String EXTRA_POST_DATA = "com.android.browser.post_data";
+
     /* if you change column order you must also change indices
        below */
     public static final String[] HISTORY_PROJECTION = new String[] {
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 51d1951..bc7b5be 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -344,7 +344,10 @@
                 // Check if file exists with a FileInputStream
                 FileInputStream stream = new FileInputStream(imagePath);
                 try {
-                    return insertImage(cr, BitmapFactory.decodeFile(imagePath), name, description);
+                    Bitmap bm = BitmapFactory.decodeFile(imagePath);
+                    String ret = insertImage(cr, bm, name, description);
+                    bm.recycle();
+                    return ret;
                 } finally {
                     try {
                         stream.close();
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 5dc03eb..7356326 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1274,6 +1274,50 @@
         public static final String DTMF_TONE_WHEN_DIALING = "dtmf_tone";
 
         /**
+         * CDMA only settings
+         * DTMF tone type played by the dialer when dialing.
+         *                 0 = Normal
+         *                 1 = Long
+         * @hide
+         */
+        public static final String DTMF_TONE_TYPE_WHEN_DIALING = "dtmf_tone_type";
+
+        /**
+         * CDMA only settings
+         * Emergency Tone  0 = Off
+         *                 1 = Alert
+         *                 2 = Vibrate
+         * @hide
+         */
+        public static final String EMERGENCY_TONE = "emergency_tone";
+
+        /**
+         * CDMA only settings
+         * Whether the auto retry is enabled. The value is
+         * boolean (1 or 0).
+         * @hide
+         */
+        public static final String CALL_AUTO_RETRY = "call_auto_retry";
+
+        /**
+         * Whether the hearing aid is enabled. The value is
+         * boolean (1 or 0).
+         * @hide
+         */
+        public static final String HEARING_AID = "hearing_aid";
+
+        /**
+         * CDMA only settings
+         * TTY Mode
+         * 0 = OFF
+         * 1 = FULL
+         * 2 = VCO
+         * 3 = HCO
+         * @hide
+         */
+        public static final String TTY_MODE = "tty_mode";
+
+        /**
          * Whether the sounds effects (key clicks, lid open ...) are enabled. The value is
          * boolean (1 or 0).
          */
diff --git a/core/java/android/speech/tts/ITts.aidl b/core/java/android/speech/tts/ITts.aidl
index 75c3b30..15f3876 100755
--- a/core/java/android/speech/tts/ITts.aidl
+++ b/core/java/android/speech/tts/ITts.aidl
@@ -33,6 +33,8 @@
 

     void speak(in String text, in int queueMode, in String[] params);

 

+    void speakIpa(in String ipaText, in int queueMode, in String[] params);

+

     boolean isSpeaking();

 

     void stop();

@@ -41,9 +43,15 @@
 

     void addSpeechFile(in String text, in String filename);

 

+    String[] getLanguage();

+

+    int isLanguageAvailable(in String language, in String country, in String variant);

+

     void setLanguage(in String language, in String country, in String variant);

 

-    boolean synthesizeToFile(in String text, in String[] params, in String outputDirectory);

+    boolean synthesizeToFile(in String text, in String[] params, in String outputDirectory);
+

+    boolean synthesizeIpaToFile(in String ipaText, in String[] params, in String outputDirectory);

 

     void playEarcon(in String earcon, in int queueMode, in String[] params);

 

diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index c064284..b245713 100755
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -46,10 +46,6 @@
      * Denotes a generic operation failure.
      */
     public static final int TTS_ERROR                  = -1;
-    /**
-     * Denotes a failure due to a missing resource.
-     */
-    public static final int TTS_ERROR_MISSING_RESOURCE = -2;
 
     /**
      * Queue mode where all entries in the playback queue (media to be played
@@ -61,6 +57,37 @@
      */
     public static final int TTS_QUEUE_ADD = 1;
 
+
+    /**
+     * Denotes the language is available exactly as specified by the locale
+     */
+    public static final int TTS_LANG_COUNTRY_VAR_AVAILABLE = 2;
+
+
+    /**
+     * Denotes the language is available for the language and country specified 
+     * by the locale, but not the variant.
+     */
+    public static final int TTS_LANG_COUNTRY_AVAILABLE = 1;
+
+
+    /**
+     * Denotes the language is available for the language by the locale, 
+     * but not the country and variant.
+     */
+    public static final int TTS_LANG_AVAILABLE = 0;
+
+    /**
+     * Denotes the language data is missing.
+     */
+    public static final int TTS_LANG_MISSING_DATA = -1;
+
+    /**
+     * Denotes the language is not supported by the current TTS engine.
+     */
+    public static final int TTS_LANG_NOT_SUPPORTED = -2;
+
+
     /**
      * Called when the TTS has initialized.
      *
@@ -72,15 +99,6 @@
     }
 
     /**
-     * Called when the TTS has finished speaking by itself (speaking
-     * finished without being canceled).
-     *
-     */
-    public interface OnSpeechCompletedListener {
-        public void onSpeechCompleted();
-    }
-
-    /**
      * Internal constants for the TTS functionality
      *
      * {@hide}
@@ -100,6 +118,16 @@
         public static final int CHECK_VOICE_DATA_BAD_DATA = -1;
         public static final int CHECK_VOICE_DATA_MISSING_DATA = -2;
         public static final int CHECK_VOICE_DATA_MISSING_DATA_NO_SDCARD = -3;
+        
+        // keys for the parameters passed with speak commands
+        public static final String TTS_KEY_PARAM_RATE = "rate";
+        public static final String TTS_KEY_PARAM_LANGUAGE = "language";
+        public static final String TTS_KEY_PARAM_COUNTRY = "country";
+        public static final String TTS_KEY_PARAM_VARIANT = "variant";
+        public static final int TTS_PARAM_POSITION_RATE = 0;
+        public static final int TTS_PARAM_POSITION_LANGUAGE = 2;
+        public static final int TTS_PARAM_POSITION_COUNTRY = 4;
+        public static final int TTS_PARAM_POSITION_VARIANT = 6;
     }
 
     /**
@@ -112,11 +140,11 @@
     private OnInitListener mInitListener = null;
     private boolean mStarted = false;
     private final Object mStartLock = new Object();
-    private ITtsCallback mITtsCallback;
-    private OnSpeechCompletedListener mSpeechCompListener = null;
-    private final Object mSpeechCompListenerLock = new Object();
-
-
+    private int mCachedRate = Engine.FALLBACK_TTS_DEFAULT_RATE;
+    private String mCachedLang = Engine.FALLBACK_TTS_DEFAULT_LANG;
+    private String mCachedCountry = Engine.FALLBACK_TTS_DEFAULT_COUNTRY;
+    private String mCachedVariant = Engine.FALLBACK_TTS_DEFAULT_VARIANT;
+    private String[] mCachedParams;
 
     /**
      * The constructor for the TTS.
@@ -130,24 +158,23 @@
     public TextToSpeech(Context context, OnInitListener listener) {
         mContext = context;
         mInitListener = listener;
+
+        mCachedParams = new String[2*4]; //4 parameters, store key and value
+        mCachedParams[Engine.TTS_PARAM_POSITION_RATE] = Engine.TTS_KEY_PARAM_RATE;
+        mCachedParams[Engine.TTS_PARAM_POSITION_LANGUAGE] = Engine.TTS_KEY_PARAM_LANGUAGE;
+        mCachedParams[Engine.TTS_PARAM_POSITION_COUNTRY] = Engine.TTS_KEY_PARAM_COUNTRY;
+        mCachedParams[Engine.TTS_PARAM_POSITION_VARIANT] = Engine.TTS_KEY_PARAM_VARIANT;
+        updateCachedParamArray();
+
         initTts();
     }
 
 
-    public void setOnSpeechCompletedListener(final OnSpeechCompletedListener listener) {
-        synchronized(mSpeechCompListenerLock) {
-            mSpeechCompListener = listener;
-        }
-    }
-
-
-    private boolean dataFilesCheck() {
-        // TODO #TTS# config manager will be in settings
-        Log.i("TTS_FIXME", "FIXME in Tts: config manager will be in settings");
-        // TODO #TTS# implement checking of the correct installation of
-        //             the data files.
-
-        return true;
+    private void updateCachedParamArray() {
+        mCachedParams[Engine.TTS_PARAM_POSITION_RATE+1] = String.valueOf(mCachedRate);
+        mCachedParams[Engine.TTS_PARAM_POSITION_LANGUAGE+1] = mCachedLang;
+        mCachedParams[Engine.TTS_PARAM_POSITION_COUNTRY+1] = mCachedCountry;
+        mCachedParams[Engine.TTS_PARAM_POSITION_VARIANT+1] = mCachedVariant;
     }
 
 
@@ -159,34 +186,7 @@
             public void onServiceConnected(ComponentName name, IBinder service) {
                 synchronized(mStartLock) {
                     mITts = ITts.Stub.asInterface(service);
-                    try {
-                        mITtsCallback = new ITtsCallback.Stub() {
-                            public void markReached(String mark)
-                            throws RemoteException {
-                                // call the listener of that event, but not
-                                // while locked.
-                                OnSpeechCompletedListener listener = null;
-                                synchronized(mSpeechCompListenerLock) {
-                                    listener = mSpeechCompListener;
-                                }
-                                if (listener != null) {
-                                    listener.onSpeechCompleted();
-                                }
-                            }
-                        };
-                        mITts.registerCallback(mITtsCallback);
-
-                    } catch (RemoteException e) {
-                        initTts();
-                        return;
-                    }
-
                     mStarted = true;
-                    // The callback can become null if the Android OS decides to
-                    // restart the TTS process as well as whatever is using it.
-                    // In such cases, do nothing - the error handling from the
-                    // speaking calls will kick in and force a proper restart of
-                    // the TTS.
                     if (mInitListener != null) {
                         // TODO manage failures and missing resources
                         mInitListener.onInit(TTS_SUCCESS);
@@ -251,14 +251,17 @@
      *
      * @param resourceId
      *            Example: <b><code>R.raw.south_south_east</code></b>
+     *
+     * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
      */
-    public void addSpeech(String text, String packagename, int resourceId) {
+    public int addSpeech(String text, String packagename, int resourceId) {
         synchronized(mStartLock) {
             if (!mStarted) {
-                return;
+                return TTS_ERROR;
             }
             try {
                 mITts.addSpeech(text, packagename, resourceId);
+                return TTS_SUCCESS;
             } catch (RemoteException e) {
                 // TTS died; restart it.
                 mStarted = false;
@@ -272,6 +275,7 @@
                 mStarted = false;
                 initTts();
             }
+            return TTS_ERROR;
         }
     }
 
@@ -285,14 +289,17 @@
      * @param filename
      *            The full path to the sound file (for example:
      *            "/sdcard/mysounds/hello.wav")
+     *
+     * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
      */
-    public void addSpeech(String text, String filename) {
+    public int addSpeech(String text, String filename) {
         synchronized (mStartLock) {
             if (!mStarted) {
-                return;
+                return TTS_ERROR;
             }
             try {
                 mITts.addSpeechFile(text, filename);
+                return TTS_SUCCESS;
             } catch (RemoteException e) {
                 // TTS died; restart it.
                 mStarted = false;
@@ -306,6 +313,7 @@
                 mStarted = false;
                 initTts();
             }
+            return TTS_ERROR;
         }
     }
 
@@ -324,17 +332,20 @@
      *            See TTS_QUEUE_ADD and TTS_QUEUE_FLUSH.
      * @param params
      *            The hashmap of speech parameters to be used.
+     *
+     * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
      */
-    public void speak(String text, int queueMode, HashMap<String,String> params)
+    public int speak(String text, int queueMode, HashMap<String,String> params)
     {
         synchronized (mStartLock) {
             Log.i("TTS received: ", text);
             if (!mStarted) {
-                return;
+                return TTS_ERROR;
             }
             try {
-                // TODO support extra parameters, passing null for the moment
-                mITts.speak(text, queueMode, null);
+                // TODO support extra parameters, passing cache of current parameters for the moment
+                mITts.speak(text, queueMode, mCachedParams);
+                return TTS_SUCCESS;
             } catch (RemoteException e) {
                 // TTS died; restart it.
                 mStarted = false;
@@ -348,6 +359,55 @@
                 mStarted = false;
                 initTts();
             }
+            return TTS_ERROR;
+        }
+    }
+
+
+    /**
+     * Speaks the IPA string using the specified queuing strategy and speech
+     * parameters. Note that the speech parameters are not universally supported
+     * by all engines and will be treated as a hint. The TTS library will try to
+     * fulfill these parameters as much as possible, but there is no guarantee
+     * that the voice used will have the properties specified.
+     *
+     * @param ipaText
+     *            The string of IPA text to be spoken.
+     * @param queueMode
+     *            The queuing strategy to use.
+     *            See TTS_QUEUE_ADD and TTS_QUEUE_FLUSH.
+     * @param params
+     *            The hashmap of speech parameters to be used.
+     *
+     * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
+     *
+     * {@hide}
+     */
+    public int speakIpa(String ipaText, int queueMode, HashMap<String,String> params)
+    {
+        synchronized (mStartLock) {
+            Log.i("TTS received: ", ipaText);
+            if (!mStarted) {
+                return TTS_ERROR;
+            }
+            try {
+                // TODO support extra parameters, passing cache of current parameters for the moment
+                mITts.speakIpa(ipaText, queueMode, mCachedParams);
+                return TTS_SUCCESS;
+            } catch (RemoteException e) {
+                // TTS died; restart it.
+                mStarted = false;
+                initTts();
+            } catch (NullPointerException e) {
+                // TTS died; restart it.
+                mStarted = false;
+                initTts();
+            } catch (IllegalStateException e) {
+                // TTS died; restart it.
+                mStarted = false;
+                initTts();
+            }
+            return TTS_ERROR;
         }
     }
 
@@ -361,16 +421,19 @@
      *            See TTS_QUEUE_ADD and TTS_QUEUE_FLUSH.
      * @param params
      *            The hashmap of parameters to be used.
+     *
+     * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
      */
-    public void playEarcon(String earcon, int queueMode,
+    public int playEarcon(String earcon, int queueMode,
             HashMap<String,String> params) {
         synchronized (mStartLock) {
             if (!mStarted) {
-                return;
+                return TTS_ERROR;
             }
             try {
                 // TODO support extra parameters, passing null for the moment
                 mITts.playEarcon(earcon, queueMode, null);
+                return TTS_SUCCESS;
             } catch (RemoteException e) {
                 // TTS died; restart it.
                 mStarted = false;
@@ -384,12 +447,45 @@
                 mStarted = false;
                 initTts();
             }
+            return TTS_ERROR;
         }
     }
 
-
-    public void playSilence(long durationInMs, int queueMode) {
-        // TODO implement, already present in TTS service
+    /**
+     * Plays silence for the specified amount of time using the specified
+     * queue mode.
+     *
+     * @param durationInMs
+     *            A long that indicates how long the silence should last.
+     * @param queueMode
+     *            See TTS_QUEUE_ADD and TTS_QUEUE_FLUSH.
+     *
+     * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
+     */
+    public int playSilence(long durationInMs, int queueMode) {
+        synchronized (mStartLock) {
+            if (!mStarted) {
+                return TTS_ERROR;
+            }
+            try {
+                // TODO support extra parameters, passing cache of current parameters for the moment
+                mITts.playSilence(durationInMs, queueMode, mCachedParams);
+                return TTS_SUCCESS;
+            } catch (RemoteException e) {
+                // TTS died; restart it.
+                mStarted = false;
+                initTts();
+            } catch (NullPointerException e) {
+                // TTS died; restart it.
+                mStarted = false;
+                initTts();
+            } catch (IllegalStateException e) {
+                // TTS died; restart it.
+                mStarted = false;
+                initTts();
+            }
+            return TTS_ERROR;
+        }
     }
 
 
@@ -425,14 +521,17 @@
 
     /**
      * Stops speech from the TTS.
+     *
+     * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
      */
-    public void stop() {
+    public int stop() {
         synchronized (mStartLock) {
             if (!mStarted) {
-                return;
+                return TTS_ERROR;
             }
             try {
                 mITts.stop();
+                return TTS_SUCCESS;
             } catch (RemoteException e) {
                 // TTS died; restart it.
                 mStarted = false;
@@ -446,6 +545,7 @@
                 mStarted = false;
                 initTts();
             }
+            return TTS_ERROR;
         }
     }
 
@@ -462,21 +562,27 @@
      *            The speech rate for the TTS engine. 1 is the normal speed,
      *            lower values slow down the speech (0.5 is half the normal speech rate),
      *            greater values accelerate it (2 is twice the normal speech rate).
+     *
+     * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
      */
-    public void setSpeechRate(float speechRate) {
+    public int setSpeechRate(float speechRate) {
         synchronized (mStartLock) {
             if (!mStarted) {
-                return;
+                return TTS_SUCCESS;
             }
             try {
                 if (speechRate > 0) {
-                    mITts.setSpeechRate((int)(speechRate*100));
+                    mCachedRate = (int)(speechRate*100);
+                    updateCachedParamArray();
+                    mITts.setSpeechRate(mCachedRate);
+                    return TTS_SUCCESS;
                 }
             } catch (RemoteException e) {
                 // TTS died; restart it.
                 mStarted = false;
                 initTts();
             }
+            return TTS_ERROR;
         }
     }
 
@@ -493,21 +599,25 @@
      *            The pitch for the TTS engine. 1 is the normal pitch,
      *            lower values lower the tone of the synthesized voice,
      *            greater values increase it.
+     *
+     * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
      */
-    public void setPitch(float pitch) {
+    public int setPitch(float pitch) {
         synchronized (mStartLock) {
             if (!mStarted) {
-                return;
+                return TTS_ERROR;
             }
             try {
                 if (pitch > 0) {
                     mITts.setPitch((int)(pitch*100));
+                    return TTS_SUCCESS;
                 }
             } catch (RemoteException e) {
                 // TTS died; restart it.
                 mStarted = false;
                 initTts();
             }
+            return TTS_ERROR;
         }
     }
 
@@ -521,25 +631,86 @@
      *
      * @param loc
      *            The locale describing the language to be used.
+     *
+     * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
      */
-    public void setLanguage(Locale loc) {
+    public int setLanguage(Locale loc) {
         synchronized (mStartLock) {
             if (!mStarted) {
-                return;
+                return TTS_ERROR;
             }
             try {
-                mITts.setLanguage(loc.getISO3Language(), loc.getISO3Country(), loc.getVariant());
+                mCachedLang = loc.getISO3Language();
+                mCachedCountry = loc.getISO3Country();
+                mCachedVariant = loc.getVariant();
+                updateCachedParamArray();
+                mITts.setLanguage(mCachedLang, mCachedCountry, mCachedVariant);
+                return TTS_SUCCESS;
             } catch (RemoteException e) {
                 // TTS died; restart it.
                 mStarted = false;
                 initTts();
             }
+            return TTS_ERROR;
         }
     }
 
 
     /**
-     * Speaks the given text using the specified queueing mode and parameters.
+     * Returns a Locale instance describing the language currently being used by the TTS engine.
+     * @return language, country (if any) and variant (if any) used by the engine stored in a Locale
+     *     instance, or null is the TTS engine has failed.
+     */
+    public Locale getLanguage() {
+        synchronized (mStartLock) {
+            if (!mStarted) {
+                return null;
+            }
+            try {
+                String[] locStrings =  mITts.getLanguage();
+                if (locStrings.length == 3) {
+                    return new Locale(locStrings[0], locStrings[1], locStrings[2]);
+                } else {
+                    return null;
+                }
+            } catch (RemoteException e) {
+                // TTS died; restart it.
+                mStarted = false;
+                initTts();
+            }
+            return null;
+        }
+    }
+
+    /**
+     * Checks if the specified language as represented by the Locale is available.
+     *
+     * @param loc
+     *            The Locale describing the language to be used.
+     *
+     * @return one of TTS_LANG_NOT_SUPPORTED, TTS_LANG_MISSING_DATA, TTS_LANG_AVAILABLE,
+     *         TTS_LANG_COUNTRY_AVAILABLE, TTS_LANG_COUNTRY_VAR_AVAILABLE.
+     */
+    public int isLanguageAvailable(Locale loc) {
+        synchronized (mStartLock) {
+            if (!mStarted) {
+                return TTS_LANG_NOT_SUPPORTED;
+            }
+            try {
+                return mITts.isLanguageAvailable(loc.getISO3Language(), loc.getISO3Country(),
+                        loc.getVariant());
+            } catch (RemoteException e) {
+                // TTS died; restart it.
+                mStarted = false;
+                initTts();
+            }
+            return TTS_LANG_NOT_SUPPORTED;
+        }
+    }
+
+
+    /**
+     * Synthesizes the given text to a file using the specified parameters.
      *
      * @param text
      *            The String of text that should be synthesized
@@ -548,17 +719,20 @@
      * @param filename
      *            The string that gives the full output filename; it should be
      *            something like "/sdcard/myappsounds/mysound.wav".
-     * @return A boolean that indicates if the synthesis succeeded
+     *
+     * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
      */
-    public boolean synthesizeToFile(String text, HashMap<String,String> params,
+    public int synthesizeToFile(String text, HashMap<String,String> params,
             String filename) {
         synchronized (mStartLock) {
             if (!mStarted) {
-                return false;
+                return TTS_ERROR;
             }
             try {
                 // TODO support extra parameters, passing null for the moment
-                return mITts.synthesizeToFile(text, null, filename);
+                if (mITts.synthesizeToFile(text, null, filename)){
+                    return TTS_SUCCESS;
+                }
             } catch (RemoteException e) {
                 // TTS died; restart it.
                 mStarted = false;
@@ -572,9 +746,52 @@
                 mStarted = false;
                 initTts();
             }
-            return false;
+            return TTS_ERROR;
         }
     }
 
 
+    /**
+     * Synthesizes the given IPA text to a file using the specified parameters.
+     *
+     * @param text
+     *            The String of text that should be synthesized
+     * @param params
+     *            A hashmap of parameters.
+     * @param filename
+     *            The string that gives the full output filename; it should be
+     *            something like "/sdcard/myappsounds/mysound.wav".
+     *
+     * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
+     *
+     * {@hide}
+     */
+    public int synthesizeIpaToFile(String ipaText,
+            HashMap<String,String> params, String filename) {
+        synchronized (mStartLock) {
+            if (!mStarted) {
+                return TTS_ERROR;
+            }
+            try {
+                // TODO support extra parameters, passing null for the moment
+                if (mITts.synthesizeIpaToFile(ipaText, null, filename)){
+                    return TTS_SUCCESS;
+                }
+            } catch (RemoteException e) {
+                // TTS died; restart it.
+                mStarted = false;
+                initTts();
+            } catch (NullPointerException e) {
+                // TTS died; restart it.
+                mStarted = false;
+                initTts();
+            } catch (IllegalStateException e) {
+                // TTS died; restart it.
+                mStarted = false;
+                initTts();
+            }
+            return TTS_ERROR;
+        }
+    }
+
 }
diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java
index d89ada0..4179edb 100644
--- a/core/java/android/util/DisplayMetrics.java
+++ b/core/java/android/util/DisplayMetrics.java
@@ -103,40 +103,43 @@
 
     /**
      * Update the display metrics based on the compatibility info and orientation
+     * NOTE: DO NOT EXPOSE THIS API!  It is introducing a circular dependency
+     * with the higher-level android.res package.
      * {@hide}
      */
-    public void updateMetrics(CompatibilityInfo compatibilityInfo, int orientation) {
+    public void updateMetrics(CompatibilityInfo compatibilityInfo, int orientation,
+            int screenLayout) {
         int xOffset = 0;
         if (!compatibilityInfo.isConfiguredExpandable()) {
             // Note: this assume that configuration is updated before calling
             // updateMetrics method.
-            int defaultWidth;
-            int defaultHeight;
-            switch (orientation) {
-                case Configuration.ORIENTATION_LANDSCAPE: {
-                    defaultWidth = (int)(CompatibilityInfo.DEFAULT_PORTRAIT_HEIGHT * density);
-                    defaultHeight = (int)(CompatibilityInfo.DEFAULT_PORTRAIT_WIDTH * density);
-                    break;
-                }
-                case Configuration.ORIENTATION_PORTRAIT:
-                case Configuration.ORIENTATION_SQUARE:
-                default: {
-                    defaultWidth = (int)(CompatibilityInfo.DEFAULT_PORTRAIT_WIDTH * density);
-                    defaultHeight = (int)(CompatibilityInfo.DEFAULT_PORTRAIT_HEIGHT * density);
-                    break;
-                }
-                case Configuration.ORIENTATION_UNDEFINED: {
-                    // don't change
-                    return;
-                }
-            }
-            
-            if (defaultWidth == widthPixels && defaultHeight == heightPixels) {
-                // the screen size is same as expected size. make it expandable
-                compatibilityInfo.setExpandable(true);
-            } else {
+            if (screenLayout == Configuration.SCREENLAYOUT_LARGE) {
+                // This is a large screen device and the app is not 
+                // compatible with large screens, to diddle it.
+                
                 compatibilityInfo.setExpandable(false);
-                // adjust the size only when the device's screen is bigger.
+                // Figure out the compatibility width and height of the screen.
+                int defaultWidth;
+                int defaultHeight;
+                switch (orientation) {
+                    case Configuration.ORIENTATION_LANDSCAPE: {
+                        defaultWidth = (int)(CompatibilityInfo.DEFAULT_PORTRAIT_HEIGHT * density);
+                        defaultHeight = (int)(CompatibilityInfo.DEFAULT_PORTRAIT_WIDTH * density);
+                        break;
+                    }
+                    case Configuration.ORIENTATION_PORTRAIT:
+                    case Configuration.ORIENTATION_SQUARE:
+                    default: {
+                        defaultWidth = (int)(CompatibilityInfo.DEFAULT_PORTRAIT_WIDTH * density);
+                        defaultHeight = (int)(CompatibilityInfo.DEFAULT_PORTRAIT_HEIGHT * density);
+                        break;
+                    }
+                    case Configuration.ORIENTATION_UNDEFINED: {
+                        // don't change
+                        return;
+                    }
+                }
+                
                 if (defaultWidth < widthPixels) {
                     // content/window's x offset in original pixels
                     xOffset = ((widthPixels - defaultWidth) / 2);
@@ -145,6 +148,10 @@
                 if (defaultHeight < heightPixels) {
                     heightPixels = defaultHeight;
                 }
+                
+            } else {
+                // the screen size is same as expected size. make it expandable
+                compatibilityInfo.setExpandable(true);
             }
         }
         compatibilityInfo.setVisibleRect(xOffset, widthPixels, heightPixels);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 3bfdde8..b3180ca 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1690,6 +1690,7 @@
     private int[] mDrawableState = null;
 
     private SoftReference<Bitmap> mDrawingCache;
+    private SoftReference<Bitmap> mUnscaledDrawingCache;
 
     /**
      * When this view has focus and the next focus is {@link #FOCUS_LEFT},
@@ -5783,28 +5784,52 @@
     }
 
     /**
+     * <p>Calling this method is equivalent to calling <code>getDrawingCache(false)</code>.</p>
+     * 
+     * @return A non-scaled bitmap representing this view or null if cache is disabled.
+     * 
+     * @see #getDrawingCache(boolean)
+     */
+    public Bitmap getDrawingCache() {
+        return getDrawingCache(false);
+    }
+
+    /**
      * <p>Returns the bitmap in which this view drawing is cached. The returned bitmap
      * is null when caching is disabled. If caching is enabled and the cache is not ready,
      * this method will create it. Calling {@link #draw(android.graphics.Canvas)} will not
      * draw from the cache when the cache is enabled. To benefit from the cache, you must
      * request the drawing cache by calling this method and draw it on screen if the
      * returned bitmap is not null.</p>
+     * 
+     * <p>Note about auto scaling in compatibility mode: When auto scaling is not enabled,
+     * this method will create a bitmap of the same size as this view. Because this bitmap
+     * will be drawn scaled by the parent ViewGroup, the result on screen might show
+     * scaling artifacts. To avoid such artifacts, you should call this method by setting
+     * the auto scaling to true. Doing so, however, will generate a bitmap of a different
+     * size than the view. This implies that your application must be able to handle this
+     * size.</p>
+     * 
+     * @param autoScale Indicates whether the generated bitmap should be scaled based on
+     *        the current density of the screen when the application is in compatibility
+     *        mode.
      *
-     * @return a bitmap representing this view or null if cache is disabled
-     *
+     * @return A bitmap representing this view or null if cache is disabled.
+     * 
      * @see #setDrawingCacheEnabled(boolean)
      * @see #isDrawingCacheEnabled()
-     * @see #buildDrawingCache()
+     * @see #buildDrawingCache(boolean)
      * @see #destroyDrawingCache()
      */
-    public Bitmap getDrawingCache() {
+    public Bitmap getDrawingCache(boolean autoScale) {
         if ((mViewFlags & WILL_NOT_CACHE_DRAWING) == WILL_NOT_CACHE_DRAWING) {
             return null;
         }
         if ((mViewFlags & DRAWING_CACHE_ENABLED) == DRAWING_CACHE_ENABLED) {
-            buildDrawingCache();
+            buildDrawingCache(autoScale);
         }
-        return mDrawingCache == null ? null : mDrawingCache.get();
+        return autoScale ? (mDrawingCache == null ? null : mDrawingCache.get()) :
+                (mUnscaledDrawingCache == null ? null : mUnscaledDrawingCache.get());
     }
 
     /**
@@ -5823,6 +5848,11 @@
             if (bitmap != null) bitmap.recycle();
             mDrawingCache = null;
         }
+        if (mUnscaledDrawingCache != null) {
+            final Bitmap bitmap = mUnscaledDrawingCache.get();
+            if (bitmap != null) bitmap.recycle();
+            mUnscaledDrawingCache = null;
+        }
     }
 
     /**
@@ -5850,18 +5880,36 @@
     }
 
     /**
+     * <p>Calling this method is equivalent to calling <code>buildDrawingCache(false)</code>.</p>
+     * 
+     * @see #buildDrawingCache(boolean)
+     */
+    public void buildDrawingCache() {
+        buildDrawingCache(false);
+    }
+
+    /**
      * <p>Forces the drawing cache to be built if the drawing cache is invalid.</p>
      *
      * <p>If you call {@link #buildDrawingCache()} manually without calling
      * {@link #setDrawingCacheEnabled(boolean) setDrawingCacheEnabled(true)}, you
      * should cleanup the cache by calling {@link #destroyDrawingCache()} afterwards.</p>
+     * 
+     * <p>Note about auto scaling in compatibility mode: When auto scaling is not enabled,
+     * this method will create a bitmap of the same size as this view. Because this bitmap
+     * will be drawn scaled by the parent ViewGroup, the result on screen might show
+     * scaling artifacts. To avoid such artifacts, you should call this method by setting
+     * the auto scaling to true. Doing so, however, will generate a bitmap of a different
+     * size than the view. This implies that your application must be able to handle this
+     * size.</p>
      *
      * @see #getDrawingCache()
      * @see #destroyDrawingCache()
      */
-    public void buildDrawingCache() {
-        if ((mPrivateFlags & DRAWING_CACHE_VALID) == 0 || mDrawingCache == null ||
-                mDrawingCache.get() == null) {
+    public void buildDrawingCache(boolean autoScale) {
+        if ((mPrivateFlags & DRAWING_CACHE_VALID) == 0 || (autoScale ?
+                (mDrawingCache == null || mDrawingCache.get() == null) :
+                (mUnscaledDrawingCache == null || mUnscaledDrawingCache.get() == null))) {
 
             if (ViewDebug.TRACE_HIERARCHY) {
                 ViewDebug.trace(this, ViewDebug.HierarchyTraceType.BUILD_CACHE);
@@ -5874,12 +5922,11 @@
             int height = mBottom - mTop;
 
             final AttachInfo attachInfo = mAttachInfo;
-            if (attachInfo != null) {
-                final boolean scalingRequired = attachInfo.mScalingRequired;
-                if (scalingRequired) {
-                    width = (int) ((width * attachInfo.mApplicationScale) + 0.5f);
-                    height = (int) ((height * attachInfo.mApplicationScale) + 0.5f);
-                }
+            final boolean scalingRequired = attachInfo != null && attachInfo.mScalingRequired;
+
+            if (autoScale && scalingRequired) {
+                width = (int) ((width * attachInfo.mApplicationScale) + 0.5f);
+                height = (int) ((height * attachInfo.mApplicationScale) + 0.5f);
             }
 
             final int drawingCacheBackgroundColor = mDrawingCacheBackgroundColor;
@@ -5894,7 +5941,8 @@
             }
 
             boolean clear = true;
-            Bitmap bitmap = mDrawingCache == null ? null : mDrawingCache.get();
+            Bitmap bitmap = autoScale ? (mDrawingCache == null ? null : mDrawingCache.get()) :
+                    (mUnscaledDrawingCache == null ? null : mUnscaledDrawingCache.get());
 
             if (bitmap == null || bitmap.getWidth() != width || bitmap.getHeight() != height) {
 
@@ -5923,12 +5971,20 @@
 
                 try {
                     bitmap = Bitmap.createBitmap(width, height, quality);
-                    mDrawingCache = new SoftReference<Bitmap>(bitmap);
+                    if (autoScale) {
+                        mDrawingCache = new SoftReference<Bitmap>(bitmap);
+                    } else {
+                        mUnscaledDrawingCache = new SoftReference<Bitmap>(bitmap);
+                    }
                 } catch (OutOfMemoryError e) {
                     // If there is not enough memory to create the bitmap cache, just
                     // ignore the issue as bitmap caches are not required to draw the
                     // view hierarchy
-                    mDrawingCache = null;
+                    if (autoScale) {
+                        mDrawingCache = null;
+                    } else {
+                        mUnscaledDrawingCache = null;
+                    }
                     return;
                 }
 
@@ -5940,13 +5996,6 @@
                 canvas = attachInfo.mCanvas;
                 if (canvas == null) {
                     canvas = new Canvas();
-
-                    // NOTE: This should have to happen only once since compatibility
-                    //       mode should not change at runtime
-                    if (attachInfo.mScalingRequired) {
-                        final float scale = attachInfo.mApplicationScale;
-                        canvas.scale(scale, scale);
-                    }
                 }
                 canvas.setBitmap(bitmap);
                 // Temporarily clobber the cached Canvas in case one of our children
@@ -5965,6 +6014,12 @@
 
             computeScroll();
             final int restoreCount = canvas.save();
+            
+            if (autoScale && scalingRequired) {
+                final float scale = attachInfo.mApplicationScale;
+                canvas.scale(scale, scale);
+            }
+            
             canvas.translate(-mScrollX, -mScrollY);
 
             mPrivateFlags |= DRAWN;
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index f803b5a..f7b7f02 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -1166,7 +1166,7 @@
                 final View child = children[i];
                 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
                     child.setDrawingCacheEnabled(true);
-                    child.buildDrawingCache();
+                    child.buildDrawingCache(true);
                 }
             }
 
@@ -1208,7 +1208,7 @@
                     bindLayoutAnimation(child);
                     if (cache) {
                         child.setDrawingCacheEnabled(true);
-                        child.buildDrawingCache();
+                        child.buildDrawingCache(true);
                     }
                 }
             }
@@ -1448,7 +1448,7 @@
         Bitmap cache = null;
         if ((flags & FLAG_CHILDREN_DRAWN_WITH_CACHE) == FLAG_CHILDREN_DRAWN_WITH_CACHE ||
                 (flags & FLAG_ALWAYS_DRAWN_WITH_CACHE) == FLAG_ALWAYS_DRAWN_WITH_CACHE) {
-            cache = child.getDrawingCache();
+            cache = child.getDrawingCache(true);
             if (mAttachInfo != null) scalingRequired = mAttachInfo.mScalingRequired;
         }
 
diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index 9a0f467..7393737 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -340,10 +340,19 @@
     }
 
     /**
-     * The default implementation does nothing.
+     * The default implementation turns this into the enter key.
      */
     public boolean performEditorAction(int actionCode) {
-        return false;
+        long eventTime = SystemClock.uptimeMillis();
+        sendKeyEvent(new KeyEvent(eventTime, eventTime,
+                KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER, 0, 0, 0, 0,
+                KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE
+                | KeyEvent.FLAG_EDITOR_ACTION));
+        sendKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), eventTime,
+                KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER, 0, 0, 0, 0,
+                KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE
+                | KeyEvent.FLAG_EDITOR_ACTION));
+        return true;
     }
 
     /**
diff --git a/core/java/android/webkit/FrameLoader.java b/core/java/android/webkit/FrameLoader.java
index 6f1b160..66ab021 100644
--- a/core/java/android/webkit/FrameLoader.java
+++ b/core/java/android/webkit/FrameLoader.java
@@ -364,7 +364,7 @@
         String cookie = CookieManager.getInstance().getCookie(
                 mListener.getWebAddress());
         if (cookie != null && cookie.length() > 0) {
-            mHeaders.put("cookie", cookie);
+            mHeaders.put("Cookie", cookie);
         }
     }
 }
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index dcba943..ec671d5 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -69,7 +69,24 @@
         }
         int value;
     }
-    
+
+    /**
+     * Enum for specifying the WebView's desired density.
+     * FAR makes 100% looking like in 240dpi
+     * MEDIUM makes 100% looking like in 160dpi
+     * CLOSE makes 100% looking like in 120dpi
+     * @hide Pending API council approval
+     */
+    public enum ZoomDensity {
+        FAR(150),      // 240dpi
+        MEDIUM(100),    // 160dpi
+        CLOSE(75);     // 120dpi
+        ZoomDensity(int size) {
+            value = size;
+        }
+        int value;
+    }
+
     /**
      * Default cache usage pattern  Use with {@link #setCacheMode}.
      */
@@ -105,6 +122,8 @@
         LOW
     }
 
+    // WebView associated with this WebSettings.
+    private WebView mWebView;
     // BrowserFrame used to access the native frame pointer.
     private BrowserFrame mBrowserFrame;
     // Flag to prevent multiple SYNC messages at one time.
@@ -145,6 +164,7 @@
     // Don't need to synchronize the get/set methods as they
     // are basic types, also none of these values are used in
     // native WebCore code.
+    private ZoomDensity     mDefaultZoom = ZoomDensity.MEDIUM;
     private RenderPriority  mRenderPriority = RenderPriority.NORMAL;
     private int             mOverrideCacheMode = LOAD_DEFAULT;
     private boolean         mSaveFormData = true;
@@ -237,9 +257,10 @@
      * Package constructor to prevent clients from creating a new settings
      * instance.
      */
-    WebSettings(Context context) {   
+    WebSettings(Context context, WebView webview) {
         mEventHandler = new EventHandler();
         mContext = context;
+        mWebView = webview;
         mDefaultTextEncoding = context.getString(com.android.internal.
                                                  R.string.default_text_encoding);
 
@@ -447,6 +468,31 @@
     }
 
     /**
+     * Set the default zoom density of the page. This should be called from UI
+     * thread.
+     * @param zoom A ZoomDensity value
+     * @see WebSettings.ZoomDensity
+     * @hide Pending API council approval
+     */
+    public void setDefaultZoom(ZoomDensity zoom) {
+        if (mDefaultZoom != zoom) {
+            mDefaultZoom = zoom;
+            mWebView.updateDefaultZoomDensity(zoom.value);
+        }
+    }
+
+    /**
+     * Get the default zoom density of the page. This should be called from UI
+     * thread.
+     * @return A ZoomDensity value
+     * @see WebSettings.ZoomDensity
+     * @hide Pending API council approval
+     */
+    public ZoomDensity getDefaultZoom() {
+        return mDefaultZoom;
+    }
+
+    /**
      * Enables using light touches to make a selection and activate mouseovers.
      */
     public void setLightTouchEnabled(boolean enabled) {
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 1179c1c..429f0f9 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -408,7 +408,7 @@
 
     // default scale. Depending on the display density.
     static int DEFAULT_SCALE_PERCENT;
-    private float DEFAULT_SCALE;
+    private float mDefaultScale;
 
     // set to true temporarily while the zoom control is being dragged
     private boolean mPreviewZoomOnly = false;
@@ -640,7 +640,7 @@
         mZoomFitPageButton.setOnClickListener(
             new View.OnClickListener() {
                 public void onClick(View v) {
-                    zoomWithPreview(DEFAULT_SCALE);
+                    zoomWithPreview(mDefaultScale);
                     updateZoomButtonsEnabled();
                 }
             });
@@ -663,7 +663,7 @@
             // or out.
             mZoomButtonsController.setZoomInEnabled(canZoomIn);
             mZoomButtonsController.setZoomOutEnabled(canZoomOut);
-            mZoomFitPageButton.setEnabled(mActualScale != DEFAULT_SCALE);
+            mZoomFitPageButton.setEnabled(mActualScale != mDefaultScale);
         }
         mZoomOverviewButton.setVisibility(canZoomScrollOut() ? View.VISIBLE:
                 View.GONE);
@@ -685,7 +685,7 @@
         mNavSlop = (int) (16 * density);
         // density adjusted scale factors
         DEFAULT_SCALE_PERCENT = (int) (100 * density);
-        DEFAULT_SCALE = density;
+        mDefaultScale = density;
         mActualScale = density;
         mInvActualScale = 1 / density;
         DEFAULT_MAX_ZOOM_SCALE = 4.0f * density;
@@ -694,6 +694,23 @@
         mMinZoomScale = DEFAULT_MIN_ZOOM_SCALE;
     }
 
+    /* package */void updateDefaultZoomDensity(int zoomDensity) {
+        final float density = getContext().getResources().getDisplayMetrics().density
+                * 100 / zoomDensity;
+        if (Math.abs(density - mDefaultScale) > 0.01) {
+            float scaleFactor = density / mDefaultScale;
+            // adjust the limits
+            mNavSlop = (int) (16 * density);
+            DEFAULT_SCALE_PERCENT = (int) (100 * density);
+            DEFAULT_MAX_ZOOM_SCALE = 4.0f * density;
+            DEFAULT_MIN_ZOOM_SCALE = 0.25f * density;
+            mDefaultScale = density;
+            mMaxZoomScale *= scaleFactor;
+            mMinZoomScale *= scaleFactor;
+            setNewZoomScale(mActualScale * scaleFactor, false);
+        }
+    }
+
     /* package */ boolean onSavePassword(String schemePlusHost, String username,
             String password, final Message resumeMsg) {
        boolean rVal = false;
@@ -4172,8 +4189,8 @@
         float oldScale = mActualScale;
 
         // snap to DEFAULT_SCALE if it is close
-        if (scale > (DEFAULT_SCALE - 0.05) && scale < (DEFAULT_SCALE + 0.05)) {
-            scale = DEFAULT_SCALE;
+        if (scale > (mDefaultScale - 0.05) && scale < (mDefaultScale + 0.05)) {
+            scale = mDefaultScale;
         }
 
         setNewZoomScale(scale, false);
@@ -4554,9 +4571,11 @@
                     break;
                 }
                 case SWITCH_TO_LONGPRESS: {
-                    mTouchMode = TOUCH_DONE_MODE;
-                    performLongClick();
-                    updateTextEntry();
+                    if (!mPreventDrag) {
+                        mTouchMode = TOUCH_DONE_MODE;
+                        performLongClick();
+                        updateTextEntry();
+                    }
                     break;
                 }
                 case SWITCH_TO_ENTER:
@@ -4689,7 +4708,7 @@
                     int initialScale = msg.arg1;
                     int viewportWidth = msg.arg2;
                     // start a new page with DEFAULT_SCALE zoom scale.
-                    float scale = DEFAULT_SCALE;
+                    float scale = mDefaultScale;
                     if (mInitialScale > 0) {
                         scale = mInitialScale / 100.0f;
                     } else  {
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index e2efb43..a5fa41e 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -139,7 +139,7 @@
         // ready.
         mEventHub = new EventHub();
         // Create a WebSettings object for maintaining all settings
-        mSettings = new WebSettings(mContext);
+        mSettings = new WebSettings(mContext, mWebView);
         // The WebIconDatabase needs to be initialized within the UI thread so
         // just request the instance here.
         WebIconDatabase.getInstance();
@@ -1560,6 +1560,20 @@
         // set the viewport settings from WebKit
         setViewportSettingsFromNative();
 
+        // adjust the default scale to match the density
+        if (WebView.DEFAULT_SCALE_PERCENT != 100) {
+            float adjust = (float) WebView.DEFAULT_SCALE_PERCENT / 100.0f;
+            if (mViewportInitialScale > 0) {
+                mViewportInitialScale *= adjust;
+            }
+            if (mViewportMinimumScale > 0) {
+                mViewportMinimumScale *= adjust;
+            }
+            if (mViewportMaximumScale > 0) {
+                mViewportMaximumScale *= adjust;
+            }
+        }
+
         // infer the values if they are not defined.
         if (mViewportWidth == 0) {
             if (mViewportInitialScale == 0) {
@@ -1586,7 +1600,7 @@
                 mViewportMaximumScale = mViewportInitialScale;
             } else if (mViewportInitialScale == 0) {
                 mViewportInitialScale = mViewportMaximumScale;
-            }            
+            }
         }
         if (mViewportWidth < 0
                 && mViewportInitialScale == WebView.DEFAULT_SCALE_PERCENT) {
diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java
index 70749d1..e84e5b0 100644
--- a/core/java/android/widget/AutoCompleteTextView.java
+++ b/core/java/android/widget/AutoCompleteTextView.java
@@ -1202,7 +1202,11 @@
                         int position, long id) {
 
                     if (position != -1) {
-                        mDropDownList.mListSelectionHidden = false;
+                        DropDownListView dropDownList = mDropDownList;
+
+                        if (dropDownList != null) {
+                            dropDownList.mListSelectionHidden = false;
+                        }
                     }
                 }
 
diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java
index 955475e4..e62dda5 100644
--- a/core/java/android/widget/RelativeLayout.java
+++ b/core/java/android/widget/RelativeLayout.java
@@ -41,6 +41,7 @@
 import java.util.TreeSet;
 import java.util.LinkedList;
 import java.util.HashSet;
+import java.util.ArrayList;
 
 /**
  * A Layout where the positions of the children can be described in relation to each other or to the
@@ -339,11 +340,17 @@
         int right = Integer.MIN_VALUE;
         int bottom = Integer.MIN_VALUE;
 
+        boolean offsetHorizontalAxis = false;
+        boolean offsetVerticalAxis = false;
+
         if ((horizontalGravity || verticalGravity) && mIgnoreGravity != View.NO_ID) {
             ignore = findViewById(mIgnoreGravity);
         }
 
-        View[] views = mSortedVerticalChildren;
+        final boolean isWrapContentWidth = widthMode != MeasureSpec.EXACTLY;
+        final boolean isWrapContentHeight = heightMode != MeasureSpec.EXACTLY;
+
+        View[] views = mSortedHorizontalChildren;
         int count = views.length;
         for (int i = 0; i < count; i++) {
             View child = views[i];
@@ -351,13 +358,16 @@
                 LayoutParams params = (LayoutParams) child.getLayoutParams();
 
                 applyHorizontalSizeRules(params, myWidth);
-                measureChildHorizontal(child, params, myWidth);
-                positionChildHorizontal(child, params, myWidth);
+                measureChildHorizontal(child, params, myWidth, myHeight);
+                if (positionChildHorizontal(child, params, myWidth, isWrapContentWidth)) {
+                    offsetHorizontalAxis = true;
+                }
             }
         }
 
-        views = mSortedHorizontalChildren;
+        views = mSortedVerticalChildren;
         count = views.length;
+
         for (int i = 0; i < count; i++) {
             View child = views[i];
             if (child.getVisibility() != GONE) {
@@ -365,12 +375,15 @@
                 
                 applyVerticalSizeRules(params, myHeight);
                 measureChild(child, params, myWidth, myHeight);
-                positionChildVertical(child, params, myHeight);
+                if (positionChildVertical(child, params, myHeight, isWrapContentHeight)) {
+                    offsetVerticalAxis = true;
+                }
 
-                if (widthMode != MeasureSpec.EXACTLY) {
+                if (isWrapContentWidth) {
                     width = Math.max(width, params.mRight);
                 }
-                if (heightMode != MeasureSpec.EXACTLY) {
+
+                if (isWrapContentHeight) {
                     height = Math.max(height, params.mBottom);
                 }
 
@@ -406,7 +419,7 @@
             }
         }
 
-        if (widthMode != MeasureSpec.EXACTLY) {
+        if (isWrapContentWidth) {
             // Width already has left padding in it since it was calculated by looking at
             // the right of each child view
             width += mPaddingRight;
@@ -417,8 +430,22 @@
 
             width = Math.max(width, getSuggestedMinimumWidth());
             width = resolveSize(width, widthMeasureSpec);
+
+            if (offsetHorizontalAxis) {
+                    for (int i = 0; i < count; i++) {
+                    View child = getChildAt(i);
+                    if (child.getVisibility() != GONE) {
+                        LayoutParams params = (LayoutParams) child.getLayoutParams();
+                        final int[] rules = params.getRules();
+                        if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_HORIZONTAL] != 0) {
+                            centerHorizontal(child, params, width);
+                        }
+                    }
+                }
+            }
         }
-        if (heightMode != MeasureSpec.EXACTLY) {
+
+        if (isWrapContentHeight) {
             // Height already has top padding in it since it was calculated by looking at
             // the bottom of each child view
             height += mPaddingBottom;
@@ -429,6 +456,19 @@
 
             height = Math.max(height, getSuggestedMinimumHeight());
             height = resolveSize(height, heightMeasureSpec);
+
+            if (offsetVerticalAxis) {
+                for (int i = 0; i < count; i++) {
+                    View child = getChildAt(i);
+                    if (child.getVisibility() != GONE) {
+                        LayoutParams params = (LayoutParams) child.getLayoutParams();
+                        final int[] rules = params.getRules();
+                        if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_VERTICAL] != 0) {
+                            centerVertical(child, params, height);
+                        }
+                    }
+                }
+            }
         }
 
         if (horizontalGravity || verticalGravity) {
@@ -510,13 +550,18 @@
         child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
     }
 
-    private void measureChildHorizontal(View child, LayoutParams params, int myWidth) {
+    private void measureChildHorizontal(View child, LayoutParams params, int myWidth, int myHeight) {
         int childWidthMeasureSpec = getChildMeasureSpec(params.mLeft,
                 params.mRight, params.width,
                 params.leftMargin, params.rightMargin,
                 mPaddingLeft, mPaddingRight,
                 myWidth);
-        int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+        int childHeightMeasureSpec;
+        if (params.width == LayoutParams.FILL_PARENT) {
+            childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(myHeight, MeasureSpec.EXACTLY);
+        } else {
+            childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(myHeight, MeasureSpec.AT_MOST);
+        }
         child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
     }
 
@@ -599,7 +644,9 @@
         return MeasureSpec.makeMeasureSpec(childSpecSize, childSpecMode);
     }
 
-    private void positionChildHorizontal(View child, LayoutParams params, int myWidth) {
+    private boolean positionChildHorizontal(View child, LayoutParams params, int myWidth,
+            boolean wrapContent) {
+
         int[] rules = params.getRules();
 
         if (params.mLeft < 0 && params.mRight >= 0) {
@@ -610,16 +657,25 @@
             params.mRight = params.mLeft + child.getMeasuredWidth();
         } else if (params.mLeft < 0 && params.mRight < 0) {
             // Both left and right vary
-            if (0 != rules[CENTER_IN_PARENT] || 0 != rules[CENTER_HORIZONTAL]) {
-                centerHorizontal(child, params, myWidth);
+            if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_HORIZONTAL] != 0) {
+                if (!wrapContent) {
+                    centerHorizontal(child, params, myWidth);
+                } else {
+                    params.mLeft = mPaddingLeft + params.leftMargin;
+                    params.mRight = params.mLeft + child.getMeasuredWidth();
+                }
+                return true;
             } else {
                 params.mLeft = mPaddingLeft + params.leftMargin;
                 params.mRight = params.mLeft + child.getMeasuredWidth();
             }
         }
+        return false;
     }
 
-    private void positionChildVertical(View child, LayoutParams params, int myHeight) {
+    private boolean positionChildVertical(View child, LayoutParams params, int myHeight,
+            boolean wrapContent) {
+
         int[] rules = params.getRules();
 
         if (params.mTop < 0 && params.mBottom >= 0) {
@@ -630,13 +686,20 @@
             params.mBottom = params.mTop + child.getMeasuredHeight();
         } else if (params.mTop < 0 && params.mBottom < 0) {
             // Both top and bottom vary
-            if (0 != rules[CENTER_IN_PARENT] || 0 != rules[CENTER_VERTICAL]) {
-                centerVertical(child, params, myHeight);
+            if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_VERTICAL] != 0) {
+                if (!wrapContent) {
+                    centerVertical(child, params, myHeight);
+                } else {
+                    params.mTop = mPaddingTop + params.topMargin;
+                    params.mBottom = params.mTop + child.getMeasuredHeight();
+                }
+                return true;
             } else {
                 params.mTop = mPaddingTop + params.topMargin;
                 params.mBottom = params.mTop + child.getMeasuredHeight();
             }
         }
+        return false;
     }
 
     private void applyHorizontalSizeRules(LayoutParams childParams, int myWidth) {
@@ -766,14 +829,14 @@
     private View getRelatedView(int[] rules, int relation) {
         int id = rules[relation];
         if (id != 0) {
-            DependencyGraph.Node node = mGraph.mNodes.get(id);
+            DependencyGraph.Node node = mGraph.mKeyNodes.get(id);
             if (node == null) return null;
             View v = node.view;
 
             // Find the first non-GONE view up the chain
             while (v.getVisibility() == View.GONE) {
                 rules = ((LayoutParams) v.getLayoutParams()).getRules();
-                node = mGraph.mNodes.get((rules[relation]));
+                node = mGraph.mKeyNodes.get((rules[relation]));
                 if (node == null) return null;
                 v = node.view;
             }
@@ -1109,10 +1172,15 @@
 
     private static class DependencyGraph {
         /**
+         * List of all views in the graph.
+         */
+        private ArrayList<Node> mNodes = new ArrayList<Node>();
+
+        /**
          * List of nodes in the graph. Each node is identified by its
          * view id (see View#getId()).
          */
-        private SparseArray<Node> mNodes = new SparseArray<Node>();
+        private SparseArray<Node> mKeyNodes = new SparseArray<Node>();
 
         /**
          * Temporary data structure used to build the list of roots
@@ -1124,14 +1192,15 @@
          * Clears the graph.
          */
         void clear() {
-            final SparseArray<Node> nodes = mNodes;
+            final ArrayList<Node> nodes = mNodes;
             final int count = nodes.size();
 
             for (int i = 0; i < count; i++) {
-                nodes.valueAt(i).release();
+                nodes.get(i).release();
             }
             nodes.clear();
 
+            mKeyNodes.clear();
             mRoots.clear();
         }
 
@@ -1141,7 +1210,14 @@
          * @param view The view to be added as a node to the graph.
          */
         void add(View view) {
-            mNodes.put(view.getId(), Node.acquire(view));
+            final int id = view.getId();
+            final Node node = Node.acquire(view);
+
+            if (id != View.NO_ID) {
+                mKeyNodes.put(id, node);
+            }
+
+            mNodes.add(node);
         }
 
         /**
@@ -1192,20 +1268,21 @@
          * @return A list of node, each being a root of the graph
          */
         private LinkedList<Node> findRoots(int[] rulesFilter) {
-            final SparseArray<Node> nodes = mNodes;
+            final SparseArray<Node> keyNodes = mKeyNodes;
+            final ArrayList<Node> nodes = mNodes;
             final int count = nodes.size();
 
             // Find roots can be invoked several times, so make sure to clear
             // all dependents and dependencies before running the algorithm
             for (int i = 0; i < count; i++) {
-                final Node node = nodes.valueAt(i);
+                final Node node = nodes.get(i);
                 node.dependents.clear();
                 node.dependencies.clear();
             }
 
             // Builds up the dependents and dependencies for each node of the graph
             for (int i = 0; i < count; i++) {
-                final Node node = nodes.valueAt(i);
+                final Node node = nodes.get(i);
 
                 final LayoutParams layoutParams = (LayoutParams) node.view.getLayoutParams();
                 final int[] rules = layoutParams.mRules;
@@ -1217,11 +1294,14 @@
                     final int rule = rules[rulesFilter[j]];
                     if (rule > 0) {
                         // The node this node depends on
-                        final Node dependency = nodes.get(rule);
+                        final Node dependency = keyNodes.get(rule);
                         if (dependency == node) {
                             throw new IllegalStateException("A view cannot have a dependency" +
                                     " on itself");
                         }
+                        if (dependency == null) {
+                            continue;
+                        }
                         // Add the current node as a dependent
                         dependency.dependents.add(node);
                         // Add a dependency to the current node
@@ -1235,7 +1315,7 @@
 
             // Finds all the roots in the graph: all nodes with no dependencies
             for (int i = 0; i < count; i++) {
-                final Node node = nodes.valueAt(i);
+                final Node node = nodes.get(i);
                 if (node.dependencies.size() == 0) roots.add(node);
             }
 
@@ -1323,7 +1403,9 @@
             /*
              * START POOL IMPLEMENTATION
              */
-            private static final int POOL_LIMIT = 12;
+            // The pool is static, so all nodes instances are shared across
+            // activities, that's why we give it a rather high limit
+            private static final int POOL_LIMIT = 100;
             private static final Pool<Node> sPool = Pools.synchronizedPool(
                     Pools.finitePool(new PoolableManager<Node>() {
                         public Node newInstance() {
diff --git a/core/java/com/android/internal/backup/IBackupTransport.aidl b/core/java/com/android/internal/backup/IBackupTransport.aidl
index ec63528..4bef265 100644
--- a/core/java/com/android/internal/backup/IBackupTransport.aidl
+++ b/core/java/com/android/internal/backup/IBackupTransport.aidl
@@ -41,6 +41,20 @@
         - adb: close the file
 */
     /**
+     * Ask the transport where, on local device storage, to keep backup state blobs.
+     * This is per-transport so that mock transports used for testing can coexist with
+     * "live" backup services without interfering with the live bookkeeping.  The
+     * returned string should be a name that is expected to be unambiguous among all
+     * available backup transports; the name of the class implementing the transport
+     * is a good choice.
+     *
+     * @return A unique name, suitable for use as a file or directory name, that the
+     *         Backup Manager could use to disambiguate state files associated with
+     *         different backup transports.
+     */
+    String transportDirName();
+
+    /**
      * Verify that this is a suitable time for a backup pass.  This should return zero
      * if a backup is reasonable right now, some positive value otherwise.  This method
      * will be called outside of the {@link #startSession}/{@link #endSession} pair.
diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java
index 0fbbb3f..c5d9d403 100644
--- a/core/java/com/android/internal/backup/LocalTransport.java
+++ b/core/java/com/android/internal/backup/LocalTransport.java
@@ -30,6 +30,9 @@
     private static final String TAG = "LocalTransport";
     private static final boolean DEBUG = true;
 
+    private static final String TRANSPORT_DIR_NAME
+            = "com.android.internal.backup.LocalTransport";
+
     private Context mContext;
     private PackageManager mPackageManager;
     private File mDataDir = new File(Environment.getDownloadCacheDirectory(), "backup");
@@ -43,6 +46,11 @@
         mPackageManager = context.getPackageManager();
     }
 
+
+    public String transportDirName() throws RemoteException {
+        return TRANSPORT_DIR_NAME;
+    }
+
     public long requestBackupTime() throws RemoteException {
         // any time is a good time for local backup
         return 0;
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index fc4a9c4..a03802d 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -2836,14 +2836,12 @@
      * @param name process name
      * @return the statistics object for the process
      */
-    public Uid.Proc getProcessStatsLocked(String name) {
+    public Uid.Proc getProcessStatsLocked(String name, int pid) {
         int uid;
         if (mUidCache.containsKey(name)) {
             uid = mUidCache.get(name);
         } else {
-            // TODO: Find the actual uid from /proc/pid/status. For now use the hashcode of the
-            // process name
-            uid = name.hashCode();
+            uid = Process.getUidForPid(pid);
             mUidCache.put(name, uid);
         }
         Uid u = getUidStatsLocked(uid);
diff --git a/core/java/com/android/internal/util/BitwiseInputStream.java b/core/java/com/android/internal/util/BitwiseInputStream.java
index 4757919..86f74f3 100644
--- a/core/java/com/android/internal/util/BitwiseInputStream.java
+++ b/core/java/com/android/internal/util/BitwiseInputStream.java
@@ -65,30 +65,31 @@
     /**
      * Read some data and increment the current position.
      *
-     * @param bits the amount of data to read (gte 0, lte 8)
+     * The 8-bit limit on access to bitwise streams is intentional to
+     * avoid endianness issues.
      *
+     * @param bits the amount of data to read (gte 0, lte 8)
      * @return byte of read data (possibly partially filled, from lsb)
      */
-    public byte read(int bits) throws AccessException {
+    public int read(int bits) throws AccessException {
         int index = mPos >>> 3;
         int offset = 16 - (mPos & 0x07) - bits;  // &7==%8
         if ((bits < 0) || (bits > 8) || ((mPos + bits) > mEnd)) {
             throw new AccessException("illegal read " +
                 "(pos " + mPos + ", end " + mEnd + ", bits " + bits + ")");
         }
-        int data = (mBuf[index] & 0x00FF) << 8;
-        if (offset < 8) data |= (mBuf[index + 1] & 0xFF);
+        int data = (mBuf[index] & 0xFF) << 8;
+        if (offset < 8) data |= mBuf[index + 1] & 0xFF;
         data >>>= offset;
         data &= (-1 >>> (32 - bits));
         mPos += bits;
-        return (byte)data;
+        return data;
     }
 
     /**
      * Read data in bulk into a byte array and increment the current position.
      *
      * @param bits the amount of data to read
-     *
      * @return newly allocated byte array of read data
      */
     public byte[] readByteArray(int bits) throws AccessException {
diff --git a/core/java/com/android/internal/util/BitwiseOutputStream.java b/core/java/com/android/internal/util/BitwiseOutputStream.java
index 1b974ce..70c0be8 100644
--- a/core/java/com/android/internal/util/BitwiseOutputStream.java
+++ b/core/java/com/android/internal/util/BitwiseOutputStream.java
@@ -82,6 +82,9 @@
     /**
      * Write some data and increment the current position.
      *
+     * The 8-bit limit on access to bitwise streams is intentional to
+     * avoid endianness issues.
+     *
      * @param bits the amount of data to write (gte 0, lte 8)
      * @param data to write, will be masked to expose only bits param from lsb
      */
@@ -95,8 +98,8 @@
         int offset = 16 - (mPos & 0x07) - bits;  // &7==%8
         data <<= offset;
         mPos += bits;
-        mBuf[index] |= (data >>> 8);
-        if (offset < 8) mBuf[index + 1] |= (data & 0x00FF);
+        mBuf[index] |= data >>> 8;
+        if (offset < 8) mBuf[index + 1] |= data & 0xFF;
     }
 
     /**
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 80c2489..888cb11 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -19,6 +19,7 @@
 	ActivityManager.cpp \
 	AndroidRuntime.cpp \
 	CursorWindow.cpp \
+	Time.cpp \
 	com_google_android_gles_jni_EGLImpl.cpp \
 	com_google_android_gles_jni_GLImpl.cpp.arm \
 	android_opengl_GLES10.cpp \
@@ -119,7 +120,8 @@
 	com_android_internal_graphics_NativeUtils.cpp \
 	android_backup_BackupDataInput.cpp \
 	android_backup_BackupDataOutput.cpp \
-	android_backup_FileBackupHelperBase.cpp
+	android_backup_FileBackupHelperBase.cpp \
+	android_backup_BackupHelperDispatcher.cpp
 
 LOCAL_C_INCLUDES += \
 	$(JNI_H_INCLUDE) \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 7350348..c815301 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -158,6 +158,7 @@
 extern int register_android_backup_BackupDataInput(JNIEnv *env);
 extern int register_android_backup_BackupDataOutput(JNIEnv *env);
 extern int register_android_backup_FileBackupHelperBase(JNIEnv *env);
+extern int register_android_backup_BackupHelperDispatcher(JNIEnv *env);
 
 static AndroidRuntime* gCurRuntime = NULL;
 
@@ -1131,6 +1132,7 @@
     REG_JNI(register_android_backup_BackupDataInput),
     REG_JNI(register_android_backup_BackupDataOutput),
     REG_JNI(register_android_backup_FileBackupHelperBase),
+    REG_JNI(register_android_backup_BackupHelperDispatcher),
 };
 
 /*
diff --git a/libs/ui/Time.cpp b/core/jni/Time.cpp
similarity index 98%
rename from libs/ui/Time.cpp
rename to core/jni/Time.cpp
index b5539135..f3037f3 100644
--- a/libs/ui/Time.cpp
+++ b/core/jni/Time.cpp
@@ -1,4 +1,4 @@
-#include <utils/TimeUtils.h>
+#include "TimeUtils.h"
 #include <stdio.h>
 #include <cutils/tztime.h>
 
diff --git a/include/utils/TimeUtils.h b/core/jni/TimeUtils.h
similarity index 100%
rename from include/utils/TimeUtils.h
rename to core/jni/TimeUtils.h
diff --git a/core/jni/android_backup_BackupHelperDispatcher.cpp b/core/jni/android_backup_BackupHelperDispatcher.cpp
new file mode 100644
index 0000000..2e3f0b9
--- /dev/null
+++ b/core/jni/android_backup_BackupHelperDispatcher.cpp
@@ -0,0 +1,259 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "BackupHelperDispatcher_native"
+#include <utils/Log.h>
+
+#include "JNIHelp.h"
+#include <android_runtime/AndroidRuntime.h>
+
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+
+#define VERSION_1_HEADER 0x01706c48  // 'Hlp'1 little endian
+
+namespace android
+{
+
+struct chunk_header_v1 {
+    int headerSize;
+    int version;
+    int dataSize; // corresponds to Header.chunkSize
+    int nameLength; // not including the NULL terminator, which is not written to the file
+};
+
+// java.io.FileDescriptor
+static jfieldID s_descriptorField = 0;
+static jfieldID s_chunkSizeField = 0;
+static jfieldID s_keyPrefixField = 0;
+
+static int
+readHeader_native(JNIEnv* env, jobject clazz, jobject headerObj, jobject fdObj)
+{
+    chunk_header_v1 flattenedHeader;
+    int fd;
+    ssize_t amt;
+    String8 keyPrefix;
+    char* buf;
+
+    fd = env->GetIntField(fdObj, s_descriptorField);
+
+    amt = read(fd, &flattenedHeader.headerSize, sizeof(flattenedHeader.headerSize));
+    if (amt != sizeof(flattenedHeader.headerSize)) {
+        return -1;
+    }
+
+    int remainingHeader = flattenedHeader.headerSize - sizeof(flattenedHeader.headerSize);
+
+    if (flattenedHeader.headerSize < (int)sizeof(chunk_header_v1)) {
+        LOGW("Skipping unknown header: %d bytes", flattenedHeader.headerSize);
+        if (remainingHeader > 0) {
+            lseek(fd, remainingHeader, SEEK_CUR);
+            // >0 means skip this chunk
+            return 1;
+        }
+    }
+
+    amt = read(fd, &flattenedHeader.version,
+            sizeof(chunk_header_v1)-sizeof(flattenedHeader.headerSize));
+    if (amt <= 0) {
+        LOGW("Failed reading chunk header");
+        return -1;
+    }
+    remainingHeader -= sizeof(chunk_header_v1)-sizeof(flattenedHeader.headerSize);
+
+    if (flattenedHeader.version != VERSION_1_HEADER) {
+        LOGW("Skipping unknown header version: 0x%08x, %d bytes", flattenedHeader.version,
+                flattenedHeader.headerSize);
+        if (remainingHeader > 0) {
+            lseek(fd, remainingHeader, SEEK_CUR);
+            // >0 means skip this chunk
+            return 1;
+        }
+    }
+
+#if 0
+    LOGD("chunk header:");
+    LOGD("  headerSize=%d", flattenedHeader.headerSize);
+    LOGD("  version=0x%08x", flattenedHeader.version);
+    LOGD("  dataSize=%d", flattenedHeader.dataSize);
+    LOGD("  nameLength=%d", flattenedHeader.nameLength);
+#endif
+
+    if (flattenedHeader.dataSize < 0 || flattenedHeader.nameLength < 0 ||
+            remainingHeader < flattenedHeader.nameLength) {
+        LOGW("Malformed V1 header remainingHeader=%d dataSize=%d nameLength=%d", remainingHeader,
+                flattenedHeader.dataSize, flattenedHeader.nameLength);
+        return -1;
+    }
+
+    buf = keyPrefix.lockBuffer(flattenedHeader.nameLength);
+    if (buf == NULL) {
+        LOGW("unable to allocate %d bytes", flattenedHeader.nameLength);
+        return -1;
+    }
+
+    amt = read(fd, buf, flattenedHeader.nameLength);
+    buf[flattenedHeader.nameLength] = 0;
+
+    keyPrefix.unlockBuffer(flattenedHeader.nameLength);
+
+    remainingHeader -= flattenedHeader.nameLength;
+
+    if (remainingHeader > 0) {
+        lseek(fd, remainingHeader, SEEK_CUR);
+    }
+
+    env->SetIntField(headerObj, s_chunkSizeField, flattenedHeader.dataSize);
+    env->SetObjectField(headerObj, s_keyPrefixField, env->NewStringUTF(keyPrefix.string()));
+
+    return 0;
+}
+
+static int
+skipChunk_native(JNIEnv* env, jobject clazz, jobject fdObj, jint bytesToSkip)
+{
+    int fd;
+
+    fd = env->GetIntField(fdObj, s_descriptorField);
+
+    lseek(fd, bytesToSkip, SEEK_CUR);
+
+    return 0;
+}
+
+static int
+padding_len(int len)
+{
+    len = len % 4;
+    return len == 0 ? len : 4 - len;
+}
+
+static int
+allocateHeader_native(JNIEnv* env, jobject clazz, jobject headerObj, jobject fdObj)
+{
+    int pos;
+    jstring nameObj;
+    int nameLength;
+    int namePadding;
+    int headerSize;
+    int fd;
+
+    fd = env->GetIntField(fdObj, s_descriptorField);
+
+    nameObj = (jstring)env->GetObjectField(headerObj, s_keyPrefixField);
+
+    nameLength = env->GetStringUTFLength(nameObj);
+    namePadding = padding_len(nameLength);
+
+    headerSize = sizeof(chunk_header_v1) + nameLength + namePadding;
+
+    pos = lseek(fd, 0, SEEK_CUR);
+
+    lseek(fd, headerSize, SEEK_CUR);
+    
+    return pos;
+}
+
+static int
+writeHeader_native(JNIEnv* env, jobject clazz, jobject headerObj, jobject fdObj, jint pos)
+{
+    int err;
+    chunk_header_v1 header;
+    int fd;
+    int namePadding;
+    int prevPos;
+    jstring nameObj;
+    const char* buf;
+
+    fd = env->GetIntField(fdObj, s_descriptorField);
+    prevPos = lseek(fd, 0, SEEK_CUR);
+
+    nameObj = (jstring)env->GetObjectField(headerObj, s_keyPrefixField);
+    header.nameLength = env->GetStringUTFLength(nameObj);
+    namePadding = padding_len(header.nameLength);
+
+    header.headerSize = sizeof(chunk_header_v1) + header.nameLength + namePadding;
+    header.version = VERSION_1_HEADER;
+    header.dataSize = prevPos - (pos + header.headerSize);
+
+    lseek(fd, pos, SEEK_SET);
+    err = write(fd, &header, sizeof(chunk_header_v1));
+    if (err != sizeof(chunk_header_v1)) {
+        return errno;
+    }
+
+    buf = env->GetStringUTFChars(nameObj, NULL);
+    err = write(fd, buf, header.nameLength);
+    env->ReleaseStringUTFChars(nameObj, buf);
+    if (err != header.nameLength) {
+        return errno;
+    }
+
+    if (namePadding != 0) {
+        int zero = 0;
+        err = write(fd, &zero, namePadding);
+        if (err != namePadding) {
+            return errno;
+        }
+    }
+
+    lseek(fd, prevPos, SEEK_SET);
+    return 0;
+}
+
+static const JNINativeMethod g_methods[] = {
+    { "readHeader_native",
+       "(Landroid/backup/BackupHelperDispatcher$Header;Ljava/io/FileDescriptor;)I",
+       (void*)readHeader_native },
+    { "skipChunk_native",
+        "(Ljava/io/FileDescriptor;I)I",
+        (void*)skipChunk_native },
+    { "allocateHeader_native",
+        "(Landroid/backup/BackupHelperDispatcher$Header;Ljava/io/FileDescriptor;)I",
+        (void*)allocateHeader_native },
+    { "writeHeader_native",
+       "(Landroid/backup/BackupHelperDispatcher$Header;Ljava/io/FileDescriptor;I)I",
+       (void*)writeHeader_native },
+};
+
+int register_android_backup_BackupHelperDispatcher(JNIEnv* env)
+{
+    jclass clazz;
+
+    clazz = env->FindClass("java/io/FileDescriptor");
+    LOG_FATAL_IF(clazz == NULL, "Unable to find class java.io.FileDescriptor");
+    s_descriptorField = env->GetFieldID(clazz, "descriptor", "I");
+    LOG_FATAL_IF(s_descriptorField == NULL,
+            "Unable to find descriptor field in java.io.FileDescriptor");
+    
+    clazz = env->FindClass("android/backup/BackupHelperDispatcher$Header");
+    LOG_FATAL_IF(clazz == NULL,
+            "Unable to find class android.backup.BackupHelperDispatcher.Header");
+    s_chunkSizeField = env->GetFieldID(clazz, "chunkSize", "I");
+    LOG_FATAL_IF(s_chunkSizeField == NULL,
+            "Unable to find chunkSize field in android.backup.BackupHelperDispatcher.Header");
+    s_keyPrefixField = env->GetFieldID(clazz, "keyPrefix", "Ljava/lang/String;");
+    LOG_FATAL_IF(s_keyPrefixField == NULL,
+            "Unable to find keyPrefix field in android.backup.BackupHelperDispatcher.Header");
+    
+    return AndroidRuntime::registerNativeMethods(env, "android/backup/BackupHelperDispatcher",
+            g_methods, NELEM(g_methods));
+}
+
+}
diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp
index b07ba7d..3550716 100644
--- a/core/jni/android_hardware_Camera.cpp
+++ b/core/jni/android_hardware_Camera.cpp
@@ -125,37 +125,8 @@
         return;
     }
     JNIEnv *env = AndroidRuntime::getJNIEnv();
-
-    // parse message
-    switch (msgType) {
-    case CAMERA_MSG_ERROR:
-        LOGV("errorCallback");
-        int error;
-        switch (ext1) {
-            case DEAD_OBJECT:
-                error = kCameraErrorMediaServer;
-                break;
-            default:
-                error = kCameraErrorUnknown;
-                break;
-        }
-        env->CallStaticVoidMethod(mCameraJClass, fields.post_event,
-                mCameraJObjectWeak, kErrorCallback, error, 0, NULL);
-        break;
-    case CAMERA_MSG_FOCUS:
-        LOGV("autoFocusCallback");
-        env->CallStaticVoidMethod(mCameraJClass, fields.post_event,
-                mCameraJObjectWeak, kAutoFocusCallback, ext1, 0, NULL);
-        break;
-    case CAMERA_MSG_SHUTTER:
-        LOGV("shutterCallback");
-        env->CallStaticVoidMethod(mCameraJClass, fields.post_event,
-                mCameraJObjectWeak, kShutterCallback, 0, 0, NULL);
-        break;
-    default:
-        LOGV("notifyCallback(%d, %d, %d)", msgType, ext1, ext2);
-        break;
-    }
+    env->CallStaticVoidMethod(mCameraJClass, fields.post_event,
+            mCameraJObjectWeak, msgType, ext1, ext2);
 }
 
 void JNICameraContext::copyAndPost(JNIEnv* env, const sp<IMemory>& dataPtr, int msgType)
@@ -203,27 +174,20 @@
 
     // return data based on callback type
     switch(msgType) {
-    case CAMERA_MSG_PREVIEW_FRAME:
-        LOGV("previewCallback");
-        copyAndPost(env, dataPtr, kPreviewCallback);
-        break;
     case CAMERA_MSG_VIDEO_FRAME:
-        LOGV("recordingCallback");
+        // should never happen
         break;
+    // don't return raw data to Java
     case CAMERA_MSG_RAW_IMAGE:
         LOGV("rawCallback");
         env->CallStaticVoidMethod(mCameraJClass, fields.post_event,
-                mCameraJObjectWeak, kRawCallback, 0, 0, NULL);
-        break;
-    case CAMERA_MSG_COMPRESSED_IMAGE:
-        LOGV("jpegCallback");
-        copyAndPost(env, dataPtr, kJpegCallback);
+                mCameraJObjectWeak, msgType, 0, 0, NULL);
         break;
     default:
         LOGV("dataCallback(%d, %p)", msgType, dataPtr.get());
+        copyAndPost(env, dataPtr, msgType);
         break;
     }
-
 }
 
 // connect to camera service
@@ -298,7 +262,10 @@
     sp<Camera> camera = get_native_camera(env, thiz, NULL);
     if (camera == 0) return;
 
-    sp<Surface> surface = reinterpret_cast<Surface*>(env->GetIntField(jSurface, fields.surface));
+    sp<Surface> surface = NULL;
+    if (jSurface != NULL) {
+        surface = reinterpret_cast<Surface*>(env->GetIntField(jSurface, fields.surface));
+    }
     if (camera->setPreviewDisplay(surface) != NO_ERROR) {
         jniThrowException(env, "java/io/IOException", "setPreviewDisplay failed");
     }
diff --git a/core/jni/android_text_format_Time.cpp b/core/jni/android_text_format_Time.cpp
index 7c208e9..98f4e03 100644
--- a/core/jni/android_text_format_Time.cpp
+++ b/core/jni/android_text_format_Time.cpp
@@ -23,7 +23,7 @@
 #include "jni.h"
 #include "utils/misc.h"
 #include "android_runtime/AndroidRuntime.h"
-#include <utils/TimeUtils.h>
+#include "TimeUtils.h"
 #include <nativehelper/JNIHelp.h>
 #include <cutils/tztime.h>
 
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index d147bcc..2d90ba4 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -535,7 +535,7 @@
                                                           jint keyboard, jint keyboardHidden,
                                                           jint navigation,
                                                           jint screenWidth, jint screenHeight,
-                                                          jint sdkVersion)
+                                                          jint screenLayout, jint sdkVersion)
 {
     AssetManager* am = assetManagerForJavaObject(env, clazz);
     if (am == NULL) {
@@ -557,6 +557,7 @@
     config.navigation = (uint8_t)navigation;
     config.screenWidth = (uint16_t)screenWidth;
     config.screenHeight = (uint16_t)screenHeight;
+    config.screenLayout = (uint8_t)screenLayout;
     config.sdkVersion = (uint16_t)sdkVersion;
     config.minorVersion = 0;
     am->setConfiguration(config, locale8);
@@ -1567,7 +1568,7 @@
         (void*) android_content_AssetManager_setLocale },
     { "getLocales",      "()[Ljava/lang/String;",
         (void*) android_content_AssetManager_getLocales },
-    { "setConfiguration", "(IILjava/lang/String;IIIIIIIII)V",
+    { "setConfiguration", "(IILjava/lang/String;IIIIIIIIII)V",
         (void*) android_content_AssetManager_setConfiguration },
     { "getResourceIdentifier","(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
         (void*) android_content_AssetManager_getResourceIdentifier },
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 945a325..aee0ed7 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -269,9 +269,9 @@
 void android_os_Process_setThreadPriority(JNIEnv* env, jobject clazz,
                                               jint pid, jint pri)
 {
-    if (pri == ANDROID_PRIORITY_BACKGROUND) {
+    if (pri >= ANDROID_PRIORITY_BACKGROUND) {
         add_pid_to_cgroup(pid, ANDROID_TGROUP_BG_NONINTERACT);
-    } else if (getpriority(PRIO_PROCESS, pid) == ANDROID_PRIORITY_BACKGROUND) {
+    } else if (getpriority(PRIO_PROCESS, pid) >= ANDROID_PRIORITY_BACKGROUND) {
         add_pid_to_cgroup(pid, ANDROID_TGROUP_DEFAULT);
     }
 
@@ -496,7 +496,7 @@
                 const String8& field = fields[i];
                 if (strncmp(p, field.string(), field.length()) == 0) {
                     p += field.length();
-                    while (*p == ' ') p++;
+                    while (*p == ' ' || *p == '\t') p++;
                     char* num = p;
                     while (*p >= '0' && *p <= '9') p++;
                     skipToEol = *p != '\n';
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 7f24dcc..599360f 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -973,7 +973,7 @@
     <permission android:name="android.permission.BACKUP"
         android:label="@string/permlab_backup"
         android:description="@string/permdesc_backup"
-        android:protectionLevel="signature" />
+        android:protectionLevel="signatureOrSystem" />
 
     <!-- Allows an application to tell the AppWidget service which application
          can access AppWidget's data.  The normal user flow is that a user
diff --git a/core/res/res/drawable/progress.xml b/core/res/res/drawable/progress.xml
deleted file mode 100644
index d270520..0000000
--- a/core/res/res/drawable/progress.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/* //device/apps/common/res/drawable/progress.xml
-**
-** Copyright 2007, 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.
-*/
--->
-
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:drawable="@android:drawable/progress_circular_background" />
-    <item>
-        <shape android:shape="ring"
-               android:innerRadiusRatio="3.4"
-               android:thicknessRatio="6.0">
-            <gradient
-                   android:useLevel="true"
-                   android:type="sweep"
-                   android:startColor="#ff000000"
-                   android:endColor="#ffffffff" />
-        </shape>
-    </item>
-    <item>
-        <rotate
-            android:pivotX="50%" android:pivotY="50%"
-            android:fromDegrees="0" android:toDegrees="360"
-            android:drawable="@android:drawable/progress_particle" />
-    </item>
-</layer-list>
diff --git a/core/res/res/drawable/progress_circular_background.png b/core/res/res/drawable/progress_circular_background.png
deleted file mode 100644
index 7c637fd..0000000
--- a/core/res/res/drawable/progress_circular_background.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/progress_circular_background_small.png b/core/res/res/drawable/progress_circular_background_small.png
deleted file mode 100644
index 6b8ba9b..0000000
--- a/core/res/res/drawable/progress_circular_background_small.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/progress_circular_indeterminate.png b/core/res/res/drawable/progress_circular_indeterminate.png
deleted file mode 100644
index 125a264..0000000
--- a/core/res/res/drawable/progress_circular_indeterminate.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/progress_indeterminate.xml b/core/res/res/drawable/progress_indeterminate.xml
deleted file mode 100644
index 1bf715e5..0000000
--- a/core/res/res/drawable/progress_indeterminate.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/* //device/apps/common/res/drawable/progress.xml
-**
-** Copyright 2007, 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.
-*/
--->
-
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
-    <item
-        android:drawable="@android:drawable/progress_circular_background" />
-
-    <item><rotate
-        android:pivotX="50%"
-        android:pivotY="50%"
-        android:fromDegrees="0"
-        android:toDegrees="360"
-        android:drawable="@android:drawable/progress_circular_indeterminate" />
-    </item>
-</layer-list>
diff --git a/core/res/res/drawable/progress_particle.png b/core/res/res/drawable/progress_particle.png
deleted file mode 100644
index 9160108..0000000
--- a/core/res/res/drawable/progress_particle.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/search_spinner.xml b/core/res/res/drawable/search_spinner.xml
index 34c163d..31a77c3 100644
--- a/core/res/res/drawable/search_spinner.xml
+++ b/core/res/res/drawable/search_spinner.xml
@@ -2,7 +2,7 @@
 <!--
 /*
 **
-** Copyright 2008, The Android Open Source Project
+** 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. 
@@ -17,20 +17,9 @@
 ** limitations under the License.
 */
 -->
-<animation-list
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        android:oneshot="false">
-    <item android:drawable="@drawable/search_spinner_anim1" android:duration="150" />
-    <item android:drawable="@drawable/search_spinner_anim2" android:duration="150" />
-    <item android:drawable="@drawable/search_spinner_anim3" android:duration="150" />
-    <item android:drawable="@drawable/search_spinner_anim4" android:duration="150" />
-    <item android:drawable="@drawable/search_spinner_anim5" android:duration="150" />
-    <item android:drawable="@drawable/search_spinner_anim6" android:duration="150" />
-    <item android:drawable="@drawable/search_spinner_anim7" android:duration="150" />
-    <item android:drawable="@drawable/search_spinner_anim8" android:duration="150" />
-    <item android:drawable="@drawable/search_spinner_anim9" android:duration="150" />
-    <item android:drawable="@drawable/search_spinner_anim10" android:duration="150" />
-    <item android:drawable="@drawable/search_spinner_anim11" android:duration="150" />
-    <item android:drawable="@drawable/search_spinner_anim12" android:duration="150" />
-</animation-list>
-
+<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android"
+    android:drawable="@drawable/spinner_black_20"
+    android:pivotX="50%"
+    android:pivotY="50%"
+    android:framesCount="12"
+    android:frameDuration="100" />
diff --git a/core/res/res/drawable/search_spinner_anim10.png b/core/res/res/drawable/search_spinner_anim10.png
deleted file mode 100755
index 9611d97..0000000
--- a/core/res/res/drawable/search_spinner_anim10.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/search_spinner_anim11.png b/core/res/res/drawable/search_spinner_anim11.png
deleted file mode 100755
index 4261704..0000000
--- a/core/res/res/drawable/search_spinner_anim11.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/search_spinner_anim12.png b/core/res/res/drawable/search_spinner_anim12.png
deleted file mode 100755
index 0602314..0000000
--- a/core/res/res/drawable/search_spinner_anim12.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/search_spinner_anim2.png b/core/res/res/drawable/search_spinner_anim2.png
deleted file mode 100755
index 05d58e0..0000000
--- a/core/res/res/drawable/search_spinner_anim2.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/search_spinner_anim3.png b/core/res/res/drawable/search_spinner_anim3.png
deleted file mode 100755
index 69fa9c1..0000000
--- a/core/res/res/drawable/search_spinner_anim3.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/search_spinner_anim4.png b/core/res/res/drawable/search_spinner_anim4.png
deleted file mode 100755
index 9201bac..0000000
--- a/core/res/res/drawable/search_spinner_anim4.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/search_spinner_anim5.png b/core/res/res/drawable/search_spinner_anim5.png
deleted file mode 100755
index f0c7101..0000000
--- a/core/res/res/drawable/search_spinner_anim5.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/search_spinner_anim6.png b/core/res/res/drawable/search_spinner_anim6.png
deleted file mode 100755
index 99d1d4e..0000000
--- a/core/res/res/drawable/search_spinner_anim6.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/search_spinner_anim7.png b/core/res/res/drawable/search_spinner_anim7.png
deleted file mode 100755
index 8ca3358..0000000
--- a/core/res/res/drawable/search_spinner_anim7.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/search_spinner_anim8.png b/core/res/res/drawable/search_spinner_anim8.png
deleted file mode 100755
index 408d723..0000000
--- a/core/res/res/drawable/search_spinner_anim8.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/search_spinner_anim9.png b/core/res/res/drawable/search_spinner_anim9.png
deleted file mode 100755
index 42a2c65..0000000
--- a/core/res/res/drawable/search_spinner_anim9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/search_spinner_anim1.png b/core/res/res/drawable/spinner_black_20.png
similarity index 100%
rename from core/res/res/drawable/search_spinner_anim1.png
rename to core/res/res/drawable/spinner_black_20.png
Binary files differ
diff --git a/core/res/res/layout/character_picker.xml b/core/res/res/layout/character_picker.xml
index bb4955a..0344849 100644
--- a/core/res/res/layout/character_picker.xml
+++ b/core/res/res/layout/character_picker.xml
@@ -23,8 +23,8 @@
         android:id="@+id/characterPicker"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:padding="12dp"
-        android:verticalSpacing="8dp"
+        android:padding="4dp"
+        android:verticalSpacing="4dp"
         android:horizontalSpacing="8dp"
         android:stretchMode="spacingWidth"
         android:gravity="left"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index e978ef5..d070107 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2327,6 +2327,15 @@
         <attr name="drawable" />
     </declare-styleable>
 
+    <declare-styleable name="AnimatedRotateDrawable">
+        <attr name="visible" />
+        <attr name="frameDuration" format="integer" />
+        <attr name="framesCount" format="integer" />
+        <attr name="pivotX" />
+        <attr name="pivotY" />
+        <attr name="drawable" />
+    </declare-styleable>
+    
     <declare-styleable name="InsetDrawable">
         <attr name="visible" />
         <attr name="drawable" />
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 91cd9fd..7571e24 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -512,6 +512,9 @@
         <!-- The screen orientation has changed, that is the user has
              rotated the device. -->
         <flag name="orientation" value="0x0080" />
+        <!-- The screen orientation has changed, that is the user has
+             rotated the device. -->
+        <flag name="screenLayout" value="0x0100" />
         <!-- The font scaling factor has changed, that is the user has
              selected a new global font size. -->
         <flag name="fontScale" value="0x40000000" />
@@ -829,8 +832,59 @@
          <p>This appears as a child tag of the
          {@link #AndroidManifest manifest} tag. -->
     <declare-styleable name="AndroidManifestSupportsDensity" parent="AndroidManifest">
-        <!-- Required value of the density in dip (device independent pixel). -->
-        <attr name="density" format="integer" />
+        <!-- Required value of the density in dip (device independent pixel).
+             You should use one of the pre-defined constants for the standard
+             screen densities defined here.
+        -->
+        <attr name="density" format="integer">
+            <!-- A low density screen, such as a QVGA or WQVGA screen in a
+                 typical hand-held phone.  The constant for this is 120. -->
+            <enum name="low" value="120" />
+            <!-- A medium density screen, such as an HVGA screen in a
+                 typical hand-held phone.  The constant for this is 160. -->
+            <enum name="medium" value="160" />
+            <!-- A high density screen, such as a VGA or WVGA screen in a
+                 typical hand-held phone.  The constant for this is 240. -->
+            <enum name="high" value="240" />
+        </attr>
+    </declare-styleable>
+
+    <!-- The <code>supports-screens</code> specifies the screen dimensions an
+         application supports.  By default a modern application supports all
+         screen sizes and must explicitly disable certain screen sizes here;
+         older applications are assumed to only support the traditional normal
+         (HVGA) screen size.  Note that screen size is a separate axis from
+         density, and is determined as the available pixels to an application
+         after density scaling has been applied.
+         
+         <p>This appears as a child tag of the
+         {@link #AndroidManifest manifest} tag. -->
+    <declare-styleable name="AndroidManifestSupportsScreens" parent="AndroidManifest">
+        <!-- Indicates whether the application supports smaller screen form-factors.
+             A small screen is defined as one with a smaller aspect ratio than
+             the traditional HVGA screen; that is, for a portrait screen, less
+             tall than an HVGA screen.  In practice, this means a QVGA low
+             density or VGA high density screen.  An application that does
+             not support small screens <em>will not be available</em> for
+             small screen devices, since there is little the platform can do
+             to make such an application work on a smaller screen. -->
+        <attr name="smallScreens" format="boolean" />
+        <!-- Indicates whether an application supports the normal screen
+             form-factors.  Traditionally this is an HVGA normal density
+             screen, but WQVGA low density and WVGA high density are also
+             considered to be normal.  This attribute is true by default,
+             and applications currently should leave it that way. -->
+        <attr name="normalScreens" format="boolean" />
+        <!-- Indicates whether the application supports larger screen form-factors.
+             A large screen is defined as a screen that is significantly larger
+             than a normal phone screen, and thus may require some special care
+             on the application's part to make good use of it.  An example would
+             be a VGA <em>normal density</em> screen, though even larger screens
+             are certainly possible.  An application that does not support
+             large screens will be placed as a postage stamp on such a
+             screen, so that it retains the dimensions it was originally
+             designed for. -->
+        <attr name="largeScreens" format="boolean" />
     </declare-styleable>
 
     <!-- The <code>expandable</code> specifies if this package supports screen metrics
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 4634b50..14d8dbe 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1117,6 +1117,9 @@
   <public type="attr" name="glEsVersion" />
   <public type="attr" name="queryAfterZeroResults" />
   <public type="attr" name="dropDownHeight" />
+  <public type="attr" name="smallScreens" />
+  <public type="attr" name="normalScreens" />
+  <public type="attr" name="largeScreens" />
 
   <public-padding type="attr" name="donut_resource_pad" end="0x0101029f" />
 
diff --git a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
new file mode 100644
index 0000000..08d295d
--- /dev/null
+++ b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
@@ -0,0 +1,315 @@
+/*
+ * 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 android.graphics.drawable;
+
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.ColorFilter;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.util.Log;
+import android.os.SystemClock;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+import com.android.internal.R;
+
+/**
+ * @hide
+ */
+public class AnimatedRotateDrawable extends Drawable implements Drawable.Callback, Runnable {
+    private AnimatedRotateState mState;
+    private boolean mMutated;
+    private float mCurrentDegrees;
+    private float mIncrement;
+
+    public AnimatedRotateDrawable() {
+        this(null);
+    }
+
+    private AnimatedRotateDrawable(AnimatedRotateState rotateState) {
+        mState = new AnimatedRotateState(rotateState, this);
+        init();
+    }
+
+    private void init() {
+        final AnimatedRotateState state = mState;
+        mIncrement = 360.0f / (float) state.mFramesCount;
+        final Drawable drawable = state.mDrawable;
+        if (drawable != null) {
+            drawable.setFilterBitmap(true);
+            if (drawable instanceof BitmapDrawable) {
+                ((BitmapDrawable) drawable).setAntiAlias(true);
+            }
+        }
+    }
+
+    public void draw(Canvas canvas) {
+        int saveCount = canvas.save();
+
+        final AnimatedRotateState st = mState;
+        final Drawable drawable = st.mDrawable;
+        final Rect bounds = drawable.getBounds();
+
+        int w = bounds.right - bounds.left;
+        int h = bounds.bottom - bounds.top;
+
+        float px = st.mPivotXRel ? (w * st.mPivotX) : st.mPivotX;
+        float py = st.mPivotYRel ? (h * st.mPivotY) : st.mPivotY;
+
+        canvas.rotate(mCurrentDegrees, px, py);
+
+        drawable.draw(canvas);
+
+        canvas.restoreToCount(saveCount);
+        
+        nextFrame();
+    }
+    
+    private void nextFrame() {
+        unscheduleSelf(this);
+        scheduleSelf(this, SystemClock.uptimeMillis() + mState.mFrameDuration);
+    }
+    
+    public void run() {
+        // TODO: This should be computed in draw(Canvas), based on the amount
+        // of time since the last frame drawn 
+        mCurrentDegrees += mIncrement;
+        if (mCurrentDegrees > (360.0f - mIncrement)) {
+            mCurrentDegrees = 0.0f;
+        }
+        nextFrame();
+        invalidateSelf();
+    }
+    
+    @Override
+    public boolean setVisible(boolean visible, boolean restart) {
+        mState.mDrawable.setVisible(visible, restart);
+        boolean changed = super.setVisible(visible, restart);
+        if (visible) {
+            if (changed || restart) {
+                mCurrentDegrees = 0.0f;
+                nextFrame();
+            }
+        } else {
+            unscheduleSelf(this);
+        }
+        return changed;
+    }    
+    
+    /**
+     * Returns the drawable rotated by this RotateDrawable.
+     */
+    public Drawable getDrawable() {
+        return mState.mDrawable;
+    }
+
+    @Override
+    public int getChangingConfigurations() {
+        return super.getChangingConfigurations()
+                | mState.mChangingConfigurations
+                | mState.mDrawable.getChangingConfigurations();
+    }
+    
+    public void setAlpha(int alpha) {
+        mState.mDrawable.setAlpha(alpha);
+    }
+
+    public void setColorFilter(ColorFilter cf) {
+        mState.mDrawable.setColorFilter(cf);
+    }
+
+    public int getOpacity() {
+        return mState.mDrawable.getOpacity();
+    }
+
+    public void invalidateDrawable(Drawable who) {
+        if (mCallback != null) {
+            mCallback.invalidateDrawable(this);
+        }
+    }
+
+    public void scheduleDrawable(Drawable who, Runnable what, long when) {
+        if (mCallback != null) {
+            mCallback.scheduleDrawable(this, what, when);
+        }
+    }
+
+    public void unscheduleDrawable(Drawable who, Runnable what) {
+        if (mCallback != null) {
+            mCallback.unscheduleDrawable(this, what);
+        }
+    }
+
+    @Override
+    public boolean getPadding(Rect padding) {
+        return mState.mDrawable.getPadding(padding);
+    }
+    
+    @Override
+    public boolean isStateful() {
+        return mState.mDrawable.isStateful();
+    }
+
+    @Override
+    protected void onBoundsChange(Rect bounds) {
+        mState.mDrawable.setBounds(bounds.left, bounds.top, bounds.right, bounds.bottom);
+    }
+
+    @Override
+    public int getIntrinsicWidth() {
+        return mState.mDrawable.getIntrinsicWidth();
+    }
+
+    @Override
+    public int getIntrinsicHeight() {
+        return mState.mDrawable.getIntrinsicHeight();
+    }
+
+    @Override
+    public ConstantState getConstantState() {
+        if (mState.canConstantState()) {
+            mState.mChangingConfigurations = super.getChangingConfigurations();
+            return mState;
+        }
+        return null;
+    }
+
+    @Override
+    public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs)
+            throws XmlPullParserException, IOException {
+
+        final TypedArray a = r.obtainAttributes(attrs, R.styleable.AnimatedRotateDrawable);
+
+        super.inflateWithAttributes(r, parser, a, R.styleable.AnimatedRotateDrawable_visible);
+        
+        TypedValue tv = a.peekValue(R.styleable.AnimatedRotateDrawable_pivotX);
+        final boolean pivotXRel = tv.type == TypedValue.TYPE_FRACTION;
+        final float pivotX = pivotXRel ? tv.getFraction(1.0f, 1.0f) : tv.getFloat();
+        
+        tv = a.peekValue(R.styleable.AnimatedRotateDrawable_pivotY);
+        final boolean pivotYRel = tv.type == TypedValue.TYPE_FRACTION;
+        final float pivotY = pivotYRel ? tv.getFraction(1.0f, 1.0f) : tv.getFloat();
+        
+        final int framesCount = a.getInt(R.styleable.AnimatedRotateDrawable_framesCount, 12);
+        final int frameDuration = a.getInt(R.styleable.AnimatedRotateDrawable_frameDuration, 150);
+        
+        final int res = a.getResourceId(R.styleable.AnimatedRotateDrawable_drawable, 0);
+        Drawable drawable = null;
+        if (res > 0) {
+            drawable = r.getDrawable(res);
+        }
+
+        a.recycle();
+        
+        int outerDepth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT &&
+               (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+
+            if (type != XmlPullParser.START_TAG) {
+                continue;
+            }
+
+            if ((drawable = Drawable.createFromXmlInner(r, parser, attrs)) == null) {
+                Log.w("drawable", "Bad element under <animated-rotate>: "
+                        + parser .getName());
+            }
+        }
+
+        if (drawable == null) {
+            Log.w("drawable", "No drawable specified for <animated-rotate>");
+        }
+
+        final AnimatedRotateState rotateState = mState;
+        rotateState.mDrawable = drawable;
+        rotateState.mPivotXRel = pivotXRel;
+        rotateState.mPivotX = pivotX;
+        rotateState.mPivotYRel = pivotYRel;
+        rotateState.mPivotY = pivotY;
+        rotateState.mFramesCount = framesCount;
+        rotateState.mFrameDuration = frameDuration;
+
+        init();
+
+        if (drawable != null) {
+            drawable.setCallback(this);
+        }
+    }
+
+    @Override
+    public Drawable mutate() {
+        if (!mMutated && super.mutate() == this) {
+            mState.mDrawable.mutate();
+            mMutated = true;
+        }
+        return this;
+    }
+
+    final static class AnimatedRotateState extends Drawable.ConstantState {
+        Drawable mDrawable;
+
+        int mChangingConfigurations;
+        
+        boolean mPivotXRel;
+        float mPivotX;
+        boolean mPivotYRel;
+        float mPivotY;
+        int mFrameDuration;
+        int mFramesCount;
+
+        private boolean mCanConstantState;
+        private boolean mCheckedConstantState;        
+
+        public AnimatedRotateState(AnimatedRotateState source, AnimatedRotateDrawable owner) {
+            if (source != null) {
+                mDrawable = source.mDrawable.getConstantState().newDrawable();
+                mDrawable.setCallback(owner);
+                mPivotXRel = source.mPivotXRel;
+                mPivotX = source.mPivotX;
+                mPivotYRel = source.mPivotYRel;
+                mPivotY = source.mPivotY;
+                mFramesCount = source.mFramesCount;
+                mFrameDuration = source.mFrameDuration;
+                mCanConstantState = mCheckedConstantState = true;
+            }
+        }
+
+        @Override
+        public Drawable newDrawable() {
+            return new AnimatedRotateDrawable(this);
+        }
+        
+        @Override
+        public int getChangingConfigurations() {
+            return mChangingConfigurations;
+        }
+
+        public boolean canConstantState() {
+            if (!mCheckedConstantState) {
+                mCanConstantState = mDrawable.getConstantState() != null;
+                mCheckedConstantState = true;
+            }
+
+            return mCanConstantState;
+        }
+    }
+}
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index f0d49f5..910e111 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -745,6 +745,8 @@
             drawable = new ClipDrawable();
         } else if (name.equals("rotate")) {
             drawable = new RotateDrawable();
+        } else if (name.equals("animated-rotate")) {
+            drawable = new AnimatedRotateDrawable();            
         } else if (name.equals("animation-list")) {
             drawable = new AnimationDrawable();
         } else if (name.equals("inset")) {
diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java
index f8b88d0..376b1df 100644
--- a/graphics/java/android/graphics/drawable/DrawableContainer.java
+++ b/graphics/java/android/graphics/drawable/DrawableContainer.java
@@ -234,8 +234,10 @@
     @Override
     public Drawable mutate() {
         if (!mMutated && super.mutate() == this) {
-            for (Drawable child : mDrawableContainerState.mDrawables) {
-                child.mutate();
+            final int N = mDrawableContainerState.getChildCount();
+            final Drawable[] drawables = mDrawableContainerState.getChildren();
+            for (int i = 0; i < N; i++) {
+                drawables[i].mutate();
             }
             mMutated = true;
         }
diff --git a/include/tts/TtsEngine.h b/include/tts/TtsEngine.h
index 8486532..21cb73b 100644
--- a/include/tts/TtsEngine.h
+++ b/include/tts/TtsEngine.h
@@ -69,6 +69,14 @@
     TTS_MISSING_RESOURCES       = -6
 };
 
+enum tts_support_result {
+    TTS_LANG_COUNTRY_VAR_AVAILABLE = 2,
+    TTS_LANG_COUNTRY_AVAILABLE = 1,
+    TTS_LANG_AVAILABLE = 0,
+    TTS_LANG_MISSING_DATA = -1,
+    TTS_LANG_NOT_SUPPORTED = -2
+};
+
 class TtsEngine
 {
 public:
@@ -86,19 +94,32 @@
     // @return TTS_SUCCESS, or TTS_FAILURE
     virtual tts_result stop();
 
+    // Returns the level of support for the language, country and variant.
+    // @return TTS_LANG_COUNTRY_VAR_AVAILABLE if the language, country and variant are supported,
+    //            and the corresponding resources are correctly installed
+    //         TTS_LANG_COUNTRY_AVAILABLE if the language and country are supported and the
+    //             corresponding resources are correctly installed, but there is no match for
+    //             the specified variant
+    //         TTS_LANG_AVAILABLE if the language is supported and the
+    //             corresponding resources are correctly installed, but there is no match for
+    //             the specified country and variant
+    //         TTS_LANG_MISSING_DATA if the required resources to provide any level of support
+    //             for the language are not correctly installed
+    //         TTS_LANG_NOT_SUPPORTED if the language is not supported by the TTS engine.
+    virtual tts_support_result isLanguageAvailable(const char *lang, const char *country,
+            const char *variant);
+
     // Load the resources associated with the specified language. The loaded
     // language will only be used once a call to setLanguage() with the same
-    // language value is issued. Language values are based on the Android
-    // conventions for localization as described in the Android platform
-    // documentation on internationalization. This implies that language
-    // data is specified in the format xx-rYY, where xx is a two letter
-    // ISO 639-1 language code in lowercase and rYY is a two letter
-    // ISO 3166-1-alpha-2 language code in uppercase preceded by a
-    // lowercase "r".
-    // @param value pointer to the language value
-    // @param size  length of the language value
+    // language value is issued. Language and country values are coded according to the ISO three
+    // letter codes for languages and countries, as can be retrieved from a java.util.Locale
+    // instance. The variant value is encoded as the variant string retrieved from a
+    // java.util.Locale instance built with that variant data.
+    // @param lang pointer to the ISO three letter code for the language
+    // @param country pointer to the ISO three letter code for the country
+    // @param variant pointer to the variant code
     // @return TTS_SUCCESS, or TTS_FAILURE
-    virtual tts_result loadLanguage(const char *value, const size_t size);
+    virtual tts_result loadLanguage(const char *lang, const char *country, const char *variant);
     
     // Load the resources associated with the specified language, country and Locale variant.
     // The loaded language will only be used once a call to setLanguageFromLocale() with the same
@@ -112,16 +133,26 @@
     // @return TTS_SUCCESS, or TTS_FAILURE
     virtual tts_result setLanguage(const char *lang, const char *country, const char *variant);
 
-    // Retrieve the currently set language, or an empty "value" if no language
-    // has been set.
-    // @param[out]   value pointer to the retrieved language value
-    // @param[inout] iosize in: stores the size available to store the language
-    //                         value in *value
-    //                      out: stores the size required to hold the language
-    //                         value if  getLanguage() returned
-    //                         TTS_PROPERTY_SIZE_TOO_SMALL, unchanged otherwise.
-    // @return TTS_SUCCESS, or TTS_PROPERTY_SIZE_TOO_SMALL, or TTS_FAILURE
-    virtual tts_result getLanguage(char *value, size_t *iosize);
+    // Retrieve the currently set language, country and variant, or empty strings if none of
+    // parameters have been set. Language and country are represented by their 3-letter ISO code
+    // @param[out]   pointer to the retrieved 3-letter code language value
+    // @param[out]   pointer to the retrieved 3-letter code country value
+    // @param[out]   pointer to the retrieved variant value
+    // @return TTS_SUCCESS, or TTS_FAILURE
+    virtual tts_result getLanguage(char *language, char *country, char *variant);
+
+    // Notifies the engine what audio parameters should be used for the synthesis.
+    // This is meant to be used as a hint, the engine implementation will set the output values
+    // to those of the synthesis format, based on a given hint.
+    // @param[inout] encoding in: the desired audio sample format
+    //                         out: the format used by the TTS engine
+    // @param[inout] rate in: the desired audio sample rate
+    //                         out: the sample rate used by the TTS engine
+    // @param[inout] channels in: the desired number of audio channels
+    //                         out: the number of channels used by the TTS engine
+    // @return TTS_SUCCESS, or TTS_FAILURE
+    virtual tts_result setAudioFormat(AudioSystem::audio_format& encoding, uint32_t& rate,
+            int& channels);
 
     // Set a property for the the TTS engine
     // "size" is the maximum size of "value" for properties "property"
diff --git a/include/ui/Camera.h b/include/ui/Camera.h
index bbc21c4..97e0e90 100644
--- a/include/ui/Camera.h
+++ b/include/ui/Camera.h
@@ -63,16 +63,12 @@
 #define FRAME_CALLBACK_FLAG_CAMERA                   0x05
 #define FRAME_CALLBACK_FLAG_BARCODE_SCANNER          0x07
 
-// msgType in notifyCallback function
+// msgType in notifyCallback and dataCallback functions
 enum {
-    CAMERA_MSG_ERROR,
+    CAMERA_MSG_ERROR = 0,
     CAMERA_MSG_SHUTTER,
     CAMERA_MSG_FOCUS,
-    CAMERA_MSG_ZOOM
-};
-
-// msgType in dataCallback function
-enum {
+    CAMERA_MSG_ZOOM,
     CAMERA_MSG_PREVIEW_FRAME,
     CAMERA_MSG_VIDEO_FRAME,
     CAMERA_MSG_POSTVIEW_FRAME,
diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h
index f1029b7..5c41ead 100644
--- a/include/utils/ResourceTypes.h
+++ b/include/utils/ResourceTypes.h
@@ -866,7 +866,7 @@
             uint8_t keyboard;
             uint8_t navigation;
             uint8_t inputFlags;
-            uint8_t pad0;
+            uint8_t inputPad0;
         };
         uint32_t input;
     };
@@ -905,6 +905,23 @@
         uint32_t version;
     };
     
+    enum {
+        SCREENLAYOUT_ANY  = 0x0000,
+        SCREENLAYOUT_SMALL = 0x0001,
+        SCREENLAYOUT_NORMAL = 0x0002,
+        SCREENLAYOUT_LARGE = 0x0003,
+    };
+    
+    union {
+        struct {
+            uint8_t screenLayout;
+            uint8_t screenConfigPad0;
+            uint8_t screenConfigPad1;
+            uint8_t screenConfigPad2;
+        };
+        uint32_t screenConfig;
+    };
+    
     inline void copyFromDeviceNoSwap(const ResTable_config& o) {
         const size_t size = dtohl(o.size);
         if (size >= sizeof(ResTable_config)) {
@@ -950,6 +967,8 @@
         diff = (int32_t)(screenSize - o.screenSize);
         if (diff != 0) return diff;
         diff = (int32_t)(version - o.version);
+        if (diff != 0) return diff;
+        diff = (int32_t)(screenLayout - o.screenLayout);
         return (int)diff;
     }
     
@@ -967,7 +986,8 @@
         CONFIG_ORIENTATION = 0x0080,
         CONFIG_DENSITY = 0x0100,
         CONFIG_SCREEN_SIZE = 0x0200,
-        CONFIG_VERSION = 0x0400
+        CONFIG_VERSION = 0x0400,
+        CONFIG_SCREEN_LAYOUT = 0x0800
     };
     
     // Compare two configuration, returning CONFIG_* flags set for each value
@@ -985,6 +1005,7 @@
         if (navigation != o.navigation) diffs |= CONFIG_NAVIGATION;
         if (screenSize != o.screenSize) diffs |= CONFIG_SCREEN_SIZE;
         if (version != o.version) diffs |= CONFIG_VERSION;
+        if (screenLayout != o.screenLayout) diffs |= CONFIG_SCREEN_LAYOUT;
         return diffs;
     }
     
@@ -1062,6 +1083,13 @@
             }
         }
 
+        if (screenConfig || o.screenConfig) {
+            if (screenLayout != o.screenLayout) {
+                if (!screenLayout) return false;
+                if (!o.screenLayout) return true;
+            }
+        }
+
         if (version || o.version) {
             if (sdkVersion != o.sdkVersion) {
                 if (!sdkVersion) return false;
@@ -1191,6 +1219,12 @@
                 }
             }
 
+            if (screenConfig || o.screenConfig) {
+                if ((screenLayout != o.screenLayout) && requested->screenLayout) {
+                    return (screenLayout);
+                }
+            }
+
             if (version || o.version) {
                 if ((sdkVersion != o.sdkVersion) && requested->sdkVersion) {
                     return (sdkVersion);
@@ -1282,6 +1316,12 @@
                 return false;
             }
         }
+        if (screenConfig != 0) {
+            if (settings.screenLayout != 0 && screenLayout != 0
+                && screenLayout != settings.screenLayout) {
+                return false;
+            }
+        }
         if (version != 0) {
             if (settings.sdkVersion != 0 && sdkVersion != 0
                 && sdkVersion != settings.sdkVersion) {
@@ -1310,13 +1350,13 @@
 
     String8 toString() const {
         char buf[200];
-        sprintf(buf, "imsi=%d/%d lang=%c%c reg=%c%c orient=0x%02x touch=0x%02x dens=0x%02x "
-                "kbd=0x%02x nav=0x%02x input=0x%02x screenW=0x%04x screenH=0x%04x vers=%d.%d",
+        sprintf(buf, "imsi=%d/%d lang=%c%c reg=%c%c orient=%d touch=%d dens=%d "
+                "kbd=%d nav=%d input=%d scrnW=%d scrnH=%d layout=%d vers=%d.%d",
                 mcc, mnc,
                 language[0] ? language[0] : '-', language[1] ? language[1] : '-',
                 country[0] ? country[0] : '-', country[1] ? country[1] : '-',
                 orientation, touchscreen, density, keyboard, navigation, inputFlags,
-                screenWidth, screenHeight, sdkVersion, minorVersion);
+                screenWidth, screenHeight, screenLayout, sdkVersion, minorVersion);
         return String8(buf);
     }
 };
diff --git a/libs/surfaceflinger/SurfaceFlinger.cpp b/libs/surfaceflinger/SurfaceFlinger.cpp
index 0e998bf..ef4a8ea 100644
--- a/libs/surfaceflinger/SurfaceFlinger.cpp
+++ b/libs/surfaceflinger/SurfaceFlinger.cpp
@@ -771,10 +771,11 @@
             dirty.orSelf(layer->visibleRegionScreen);
             layer->contentDirty = false;
         } else {
-            // compute the exposed region
-            // dirty = what's visible now - what's wasn't covered before
-            //       = what's visible now & what's was covered before
-            dirty = visibleRegion.intersect(layer->coveredRegionScreen);            
+            /* compute the exposed region:
+             *    exposed = what's VISIBLE and NOT COVERED now 
+             *    but was COVERED before
+             */
+            dirty = (visibleRegion - coveredRegion) & layer->coveredRegionScreen;
         }
         dirty.subtractSelf(aboveOpaqueLayers);
 
@@ -783,7 +784,7 @@
 
         // updade aboveOpaqueLayers/aboveCoveredLayers for next (lower) layer
         aboveOpaqueLayers.orSelf(opaqueRegion);
-        aboveCoveredLayers.orSelf(bounds);
+        aboveCoveredLayers.orSelf(visibleRegion);
         
         // Store the visible region is screen space
         layer->setVisibleRegion(visibleRegion);
diff --git a/libs/ui/Android.mk b/libs/ui/Android.mk
index b3b2104b..7bbe38b 100644
--- a/libs/ui/Android.mk
+++ b/libs/ui/Android.mk
@@ -24,8 +24,7 @@
 	Region.cpp \
 	Surface.cpp \
 	SurfaceComposerClient.cpp \
-	SurfaceFlingerSynchro.cpp \
-	Time.cpp
+	SurfaceFlingerSynchro.cpp 
 
 LOCAL_SHARED_LIBRARIES := \
 	libcorecg \
diff --git a/libs/ui/Camera.cpp b/libs/ui/Camera.cpp
index bb22dab..a481ce7 100644
--- a/libs/ui/Camera.cpp
+++ b/libs/ui/Camera.cpp
@@ -149,21 +149,21 @@
 status_t Camera::setPreviewDisplay(const sp<Surface>& surface)
 {
     LOGV("setPreviewDisplay");
-    if (surface == 0) {
-        LOGE("app passed NULL surface");
-        return NO_INIT;
-    }
     sp <ICamera> c = mCamera;
     if (c == 0) return NO_INIT;
-    return c->setPreviewDisplay(surface->getISurface());
+    if (surface != 0) {
+        return c->setPreviewDisplay(surface->getISurface());
+    } else {
+        LOGD("app passed NULL surface");
+        return c->setPreviewDisplay(0);
+    }
 }
 
 status_t Camera::setPreviewDisplay(const sp<ISurface>& surface)
 {
     LOGV("setPreviewDisplay");
     if (surface == 0) {
-        LOGE("app passed NULL surface");
-        return NO_INIT;
+        LOGD("app passed NULL surface");
     }
     sp <ICamera> c = mCamera;
     if (c == 0) return NO_INIT;
@@ -171,7 +171,7 @@
 }
 
 
-// start preview mode, must call setPreviewDisplay first
+// start preview mode
 status_t Camera::startPreview()
 {
     LOGV("startPreview");
diff --git a/libs/utils/BackupData.cpp b/libs/utils/BackupData.cpp
index 0868cff..cce754a 100644
--- a/libs/utils/BackupData.cpp
+++ b/libs/utils/BackupData.cpp
@@ -107,7 +107,10 @@
     } else {
         k = key;
     }
-    LOGD("m_keyPrefix=%s key=%s k=%s", m_keyPrefix.string(), key.string(), k.string());
+    if (true) {
+        LOGD("Writing entity: prefix='%s' key='%s' dataSize=%d", m_keyPrefix.string(), key.string(),
+                dataSize);
+    }
 
     entity_header_v1 header;
     ssize_t keyLen;
diff --git a/libs/utils/BackupHelpers.cpp b/libs/utils/BackupHelpers.cpp
index 99a4abc..4ad9b51 100644
--- a/libs/utils/BackupHelpers.cpp
+++ b/libs/utils/BackupHelpers.cpp
@@ -68,11 +68,15 @@
 
 const static int CURRENT_METADATA_VERSION = 1;
 
-#if 1 // TEST_BACKUP_HELPERS
+#if 1
+#define LOGP(f, x...)
+#else
+#if TEST_BACKUP_HELPERS
 #define LOGP(f, x...) printf(f "\n", x)
 #else
 #define LOGP(x...) LOGD(x)
 #endif
+#endif
 
 const static int ROUND_UP[4] = { 0, 3, 2, 1 };
 
@@ -349,7 +353,6 @@
 
         err = stat(file, &st);
         if (err != 0) {
-            LOGW("Error stating file %s", file);
             r.deleted = true;
         } else {
             r.deleted = false;
diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp
index e4f9f0f..7a33220 100644
--- a/libs/utils/ResourceTypes.cpp
+++ b/libs/utils/ResourceTypes.cpp
@@ -3919,7 +3919,7 @@
                         printf("      NON-INTEGER ResTable_type ADDRESS: %p\n", type);
                         continue;
                     }
-                    printf("      config %d lang=%c%c cnt=%c%c orien=%d touch=%d density=%d key=%d infl=%d nav=%d w=%d h=%d\n",
+                    printf("      config %d lang=%c%c cnt=%c%c orien=%d touch=%d density=%d key=%d infl=%d nav=%d w=%d h=%d lyt=%d\n",
                            (int)configIndex,
                            type->config.language[0] ? type->config.language[0] : '-',
                            type->config.language[1] ? type->config.language[1] : '-',
@@ -3932,7 +3932,8 @@
                            type->config.inputFlags,
                            type->config.navigation,
                            dtohs(type->config.screenWidth),
-                           dtohs(type->config.screenHeight));
+                           dtohs(type->config.screenHeight),
+                           type->config.screenLayout);
                     size_t entryCount = dtohl(type->entryCount);
                     uint32_t entriesStart = dtohl(type->entriesStart);
                     if ((entriesStart&0x3) != 0) {
diff --git a/media/libmediaplayerservice/VorbisPlayer.cpp b/media/libmediaplayerservice/VorbisPlayer.cpp
index 14fd6ce..7f0ef21 100644
--- a/media/libmediaplayerservice/VorbisPlayer.cpp
+++ b/media/libmediaplayerservice/VorbisPlayer.cpp
@@ -345,9 +345,6 @@
 {
     LOGV("reset\n");
     Mutex::Autolock l(mMutex);
-    if (mState != STATE_OPEN) {
-        return NO_ERROR;
-    }
     return reset_nosync();
 }
 
@@ -355,10 +352,13 @@
 status_t VorbisPlayer::reset_nosync()
 {
     // close file
-    ov_clear(&mVorbisFile); // this also closes the FILE
     if (mFile != NULL) {
-        LOGV("OOPS! Vorbis didn't close the file");
-        fclose(mFile);
+        ov_clear(&mVorbisFile); // this also closes the FILE
+        if (mFile != NULL) {
+            LOGV("OOPS! Vorbis didn't close the file");
+            fclose(mFile);
+            mFile = NULL;
+        }
     }
     mState = STATE_ERROR;
 
diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp
index 25e31ee..de323b3 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -1052,23 +1052,25 @@
     if (!validate_display_context(dpy, ctx))
         return EGL_FALSE;    
     
+    EGLSurface impl_draw = EGL_NO_SURFACE;
+    EGLSurface impl_read = EGL_NO_SURFACE;
     egl_context_t * const c = get_context(ctx);
     if (draw != EGL_NO_SURFACE) {
         egl_surface_t const * d = get_surface(draw);
         if (!d) return setError(EGL_BAD_SURFACE, EGL_FALSE);
         if (d->impl != c->impl)
             return setError(EGL_BAD_MATCH, EGL_FALSE);
-        draw = d->surface;
+        impl_draw = d->surface;
     }
     if (read != EGL_NO_SURFACE) {
         egl_surface_t const * r = get_surface(read);
         if (!r) return setError(EGL_BAD_SURFACE, EGL_FALSE);
         if (r->impl != c->impl)
             return setError(EGL_BAD_MATCH, EGL_FALSE);
-        read = r->surface;
+        impl_read = r->surface;
     }
     EGLBoolean result = c->cnx->hooks->egl.eglMakeCurrent(
-            dp->dpys[c->impl], draw, read, c->context);
+            dp->dpys[c->impl], impl_draw, impl_read, c->context);
 
     if (result == EGL_TRUE) {
         setGlThreadSpecific(c->cnx->hooks);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index b2f3557..660b469 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -592,6 +592,21 @@
         loadIntegerSetting(stmt, Settings.System.SCREEN_OFF_TIMEOUT,
                 R.integer.def_screen_off_timeout);
 
+        // Set default cdma emergency tone
+        loadSetting(stmt, Settings.System.EMERGENCY_TONE, 0);
+
+        // Set default cdma call auto retry
+        loadSetting(stmt, Settings.System.CALL_AUTO_RETRY, 0);
+
+        // Set default cdma DTMF type
+        loadSetting(stmt, Settings.System.DTMF_TONE_TYPE_WHEN_DIALING, 0);
+
+        // Set default hearing aid
+        loadSetting(stmt, Settings.System.HEARING_AID, 0);
+
+        // Set default tty mode
+        loadSetting(stmt, Settings.System.TTY_MODE, 0);
+
         loadBooleanSetting(stmt, Settings.System.AIRPLANE_MODE_ON,
                 R.bool.def_airplane_mode_on);
 
diff --git a/packages/TtsService/AndroidManifest.xml b/packages/TtsService/AndroidManifest.xml
index 1dc25c6..fab2534 100755
--- a/packages/TtsService/AndroidManifest.xml
+++ b/packages/TtsService/AndroidManifest.xml
@@ -12,4 +12,5 @@
         </service>
     </application>
     <uses-permission android:name="android.permission.INTERNET"/>
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
 </manifest>
diff --git a/packages/TtsService/jni/android_tts_SynthProxy.cpp b/packages/TtsService/jni/android_tts_SynthProxy.cpp
index 0dafcc1..a55b704 100644
--- a/packages/TtsService/jni/android_tts_SynthProxy.cpp
+++ b/packages/TtsService/jni/android_tts_SynthProxy.cpp
@@ -199,16 +199,29 @@
         if (wav == NULL) {
             delete pForAfter;
             LOGV("Null: speech has completed");
+            return TTS_CALLBACK_HALT;
         }
         if (bufferSize > 0){
             fwrite(wav, 1, bufferSize, pForAfter->outputFile);
         }
     }
-    // TODO update to call back into the SynthProxy class through the
+    // Future update:
+    //      For sync points in the speech, call back into the SynthProxy class through the
     //      javaTTSFields.synthProxyMethodPost methode to notify
-    //      playback has completed if the synthesis is done, i.e.
-    //      if status == TTS_SYNTH_DONE
-    //delete pForAfter;
+    //      playback has completed if the synthesis is done or if a marker has been reached.
+
+    if (status == TTS_SYNTH_DONE) {
+        // this struct was allocated in the original android_tts_SynthProxy_speak call,
+        // all processing matching this call is now done.
+        LOGV("Speech synthesis done.");
+        if (pForAfter->usageMode == USAGEMODE_PLAY_IMMEDIATELY) {
+            // only delete for direct playback. When writing to a file, we still have work to do
+            // in android_tts_SynthProxy_synthesizeToFile. The struct will be deleted there.
+            delete pForAfter;
+            pForAfter = NULL;
+        }
+        return TTS_CALLBACK_HALT;
+    }
 
     // we don't update the wav (output) parameter as we'll let the next callback
     // write at the same location, we've consumed the data already, but we need
@@ -270,6 +283,33 @@
 }
 
 
+static int
+android_tts_SynthProxy_isLanguageAvailable(JNIEnv *env, jobject thiz, jint jniData,
+        jstring language, jstring country, jstring variant)
+{
+    int result = TTS_LANG_NOT_SUPPORTED;
+
+    if (jniData == 0) {
+        LOGE("android_tts_SynthProxy_isLanguageAvailable(): invalid JNI data");
+        return result;
+    }
+
+    SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
+    const char *langNativeString = env->GetStringUTFChars(language, 0);
+    const char *countryNativeString = env->GetStringUTFChars(country, 0);
+    const char *variantNativeString = env->GetStringUTFChars(variant, 0);
+    // TODO check return codes
+    if (pSynthData->mNativeSynthInterface) {
+        result = pSynthData->mNativeSynthInterface->isLanguageAvailable(langNativeString,
+                countryNativeString, variantNativeString);
+    }
+    env->ReleaseStringUTFChars(language, langNativeString);
+    env->ReleaseStringUTFChars(country, countryNativeString);
+    env->ReleaseStringUTFChars(variant, variantNativeString);
+    return result;
+}
+
+
 static void
 android_tts_SynthProxy_setLanguage(JNIEnv *env, jobject thiz, jint jniData,
         jstring language, jstring country, jstring variant)
@@ -289,8 +329,32 @@
                 variantNativeString);
     }
     env->ReleaseStringUTFChars(language, langNativeString);
-    env->ReleaseStringUTFChars(language, countryNativeString);
-    env->ReleaseStringUTFChars(language, variantNativeString);
+    env->ReleaseStringUTFChars(country, countryNativeString);
+    env->ReleaseStringUTFChars(variant, variantNativeString);
+}
+
+
+static void
+android_tts_SynthProxy_loadLanguage(JNIEnv *env, jobject thiz, jint jniData,
+        jstring language, jstring country, jstring variant)
+{
+    if (jniData == 0) {
+        LOGE("android_tts_SynthProxy_loadLanguage(): invalid JNI data");
+        return;
+    }
+
+    SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
+    const char *langNativeString = env->GetStringUTFChars(language, 0);
+    const char *countryNativeString = env->GetStringUTFChars(country, 0);
+    const char *variantNativeString = env->GetStringUTFChars(variant, 0);
+    // TODO check return codes
+    if (pSynthData->mNativeSynthInterface) {
+        pSynthData->mNativeSynthInterface->loadLanguage(langNativeString, countryNativeString,
+                variantNativeString);
+    }
+    env->ReleaseStringUTFChars(language, langNativeString);
+    env->ReleaseStringUTFChars(country, countryNativeString);
+    env->ReleaseStringUTFChars(variant, variantNativeString);
 }
 
 
@@ -338,7 +402,6 @@
 }
 
 
-// TODO: Refactor this to get rid of any assumptions about sample rate, etc.
 static void
 android_tts_SynthProxy_synthesizeToFile(JNIEnv *env, jobject thiz, jint jniData,
         jstring textJavaString, jstring filenameJavaString)
@@ -349,6 +412,21 @@
     }
 
     SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
+    if (!pSynthData->mNativeSynthInterface) {
+        LOGE("android_tts_SynthProxy_synthesizeToFile(): invalid engine handle");
+        return;
+    }
+
+    // Retrieve audio parameters before writing the file header
+    AudioSystem::audio_format encoding = DEFAULT_TTS_FORMAT;
+    uint32_t rate = DEFAULT_TTS_RATE;
+    int channels = DEFAULT_TTS_NB_CHANNELS;
+    pSynthData->mNativeSynthInterface->setAudioFormat(encoding, rate, channels);
+
+    if ((encoding != AudioSystem::PCM_16_BIT) && (encoding != AudioSystem::PCM_8_BIT)) {
+        LOGE("android_tts_SynthProxy_synthesizeToFile(): engine uses invalid format");
+        return;
+    }
 
     const char *filenameNativeString =
             env->GetStringUTFChars(filenameJavaString, 0);
@@ -360,6 +438,12 @@
 
     pForAfter->outputFile = fopen(filenameNativeString, "wb");
 
+    if (pForAfter->outputFile == NULL) {
+        LOGE("android_tts_SynthProxy_synthesizeToFile(): error creating output file");
+        delete pForAfter;
+        return;
+    }
+
     // Write 44 blank bytes for WAV header, then come back and fill them in
     // after we've written the audio data
     char header[44];
@@ -368,10 +452,8 @@
     unsigned int unique_identifier;
 
     // TODO check return codes
-    if (pSynthData->mNativeSynthInterface) {
-        pSynthData->mNativeSynthInterface->synthesizeText(textNativeString, pSynthData->mBuffer, pSynthData->mBufferSize,
-                (void *)pForAfter);
-    }
+    pSynthData->mNativeSynthInterface->synthesizeText(textNativeString, pSynthData->mBuffer,
+            pSynthData->mBufferSize, (void *)pForAfter);
 
     long filelen = ftell(pForAfter->outputFile);
 
@@ -393,12 +475,14 @@
 
     ((uint32_t *)(&header[16]))[0] = 16;  // size of fmt
 
+    int sampleSizeInByte = (encoding == AudioSystem::PCM_16_BIT ? 2 : 1);
+
     ((unsigned short *)(&header[20]))[0] = 1;  // format
-    ((unsigned short *)(&header[22]))[0] = 1;  // channels
-    ((uint32_t *)(&header[24]))[0] = 22050;  // samplerate
-    ((uint32_t *)(&header[28]))[0] = 44100;  // byterate
-    ((unsigned short *)(&header[32]))[0] = 2;  // block align
-    ((unsigned short *)(&header[34]))[0] = 16;  // bits per sample
+    ((unsigned short *)(&header[22]))[0] = channels;  // channels
+    ((uint32_t *)(&header[24]))[0] = rate;  // samplerate
+    ((uint32_t *)(&header[28]))[0] = rate * sampleSizeInByte * channels;// byterate
+    ((unsigned short *)(&header[32]))[0] = sampleSizeInByte * channels;  // block align
+    ((unsigned short *)(&header[34]))[0] = sampleSizeInByte * 8;  // bits per sample
 
     header[36] = 'd';
     header[37] = 'a';
@@ -414,6 +498,9 @@
     fflush(pForAfter->outputFile);
     fclose(pForAfter->outputFile);
 
+    delete pForAfter;
+    pForAfter = NULL;
+
     env->ReleaseStringUTFChars(textJavaString, textNativeString);
     env->ReleaseStringUTFChars(filenameJavaString, filenameNativeString);
 }
@@ -441,8 +528,8 @@
 
     if (pSynthData->mNativeSynthInterface) {
         const char *textNativeString = env->GetStringUTFChars(textJavaString, 0);
-        pSynthData->mNativeSynthInterface->synthesizeText(textNativeString, pSynthData->mBuffer, pSynthData->mBufferSize,
-                (void *)pForAfter);
+        pSynthData->mNativeSynthInterface->synthesizeText(textNativeString, pSynthData->mBuffer,
+                pSynthData->mBufferSize, (void *)pForAfter);
         env->ReleaseStringUTFChars(textJavaString, textNativeString);
     }
 }
@@ -501,23 +588,34 @@
 }
 
 
-JNIEXPORT jstring JNICALL
+static jobjectArray
 android_tts_SynthProxy_getLanguage(JNIEnv *env, jobject thiz, jint jniData)
 {
     if (jniData == 0) {
         LOGE("android_tts_SynthProxy_getLanguage(): invalid JNI data");
-        return env->NewStringUTF("");
+        return NULL;
     }
 
     SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
-    size_t bufSize = 100;
-    char buf[bufSize];
-    memset(buf, 0, bufSize);
-    // TODO check return codes
+
     if (pSynthData->mNativeSynthInterface) {
-        pSynthData->mNativeSynthInterface->getLanguage(buf, &bufSize);
+        size_t bufSize = 100;
+        char lang[bufSize];
+        char country[bufSize];
+        char variant[bufSize];
+        memset(lang, 0, bufSize);
+        memset(country, 0, bufSize);
+        memset(variant, 0, bufSize);
+        jobjectArray retLocale = (jobjectArray)env->NewObjectArray(3,
+                env->FindClass("java/lang/String"), env->NewStringUTF(""));
+        pSynthData->mNativeSynthInterface->getLanguage(lang, country, variant);
+        env->SetObjectArrayElement(retLocale, 0, env->NewStringUTF(lang));
+        env->SetObjectArrayElement(retLocale, 1, env->NewStringUTF(country));
+        env->SetObjectArrayElement(retLocale, 2, env->NewStringUTF(variant));
+        return retLocale;
+    } else {
+        return NULL;
     }
-    return env->NewStringUTF(buf);
 }
 
 
@@ -555,10 +653,18 @@
         "(ILjava/lang/String;Ljava/lang/String;)V",
         (void*)android_tts_SynthProxy_synthesizeToFile
     },
+    {   "native_isLanguageAvailable",
+        "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
+        (void*)android_tts_SynthProxy_isLanguageAvailable
+    },
     {   "native_setLanguage",
         "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V",
         (void*)android_tts_SynthProxy_setLanguage
     },
+    {   "native_loadLanguage",
+        "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V",
+        (void*)android_tts_SynthProxy_loadLanguage
+    },
     {   "native_setSpeechRate",
         "(II)V",
         (void*)android_tts_SynthProxy_setSpeechRate
@@ -572,7 +678,7 @@
         (void*)android_tts_SynthProxy_playAudioBuffer
     },
     {   "native_getLanguage",
-        "(I)Ljava/lang/String;",
+        "(I)[Ljava/lang/String;",
         (void*)android_tts_SynthProxy_getLanguage
     },
     {   "native_getRate",
diff --git a/packages/TtsService/src/android/tts/SynthProxy.java b/packages/TtsService/src/android/tts/SynthProxy.java
index 3bdff37..91fe3b7 100755
--- a/packages/TtsService/src/android/tts/SynthProxy.java
+++ b/packages/TtsService/src/android/tts/SynthProxy.java
@@ -68,49 +68,64 @@
     // TODO add IPA methods
 
     /**
-     * Sets the language
+     * Queries for language support.
+     * Return codes are defined in android.speech.tts.TextToSpeech
+     */
+    public int isLanguageAvailable(String language, String country, String variant) {
+        return native_isLanguageAvailable(mJniData, language, country, variant);
+    }
+
+    /**
+     * Sets the language.
      */
     public void setLanguage(String language, String country, String variant) {
         native_setLanguage(mJniData, language, country, variant);
     }
 
     /**
-     * Sets the speech rate
+     * Loads the language: it's not set, but prepared for use later.
+     */
+    public void loadLanguage(String language, String country, String variant) {
+        native_loadLanguage(mJniData, language, country, variant);
+    }
+
+    /**
+     * Sets the speech rate.
      */
     public final void setSpeechRate(int speechRate) {
         native_setSpeechRate(mJniData, speechRate);
     }
 
     /**
-     * Sets the pitch of the synthesized voice
+     * Sets the pitch of the synthesized voice.
      */
     public final void setPitch(int pitch) {
         native_setPitch(mJniData, pitch);
     }
 
     /**
-     * Plays the given audio buffer
+     * Plays the given audio buffer.
      */
     public void playAudioBuffer(int bufferPointer, int bufferSize) {
         native_playAudioBuffer(mJniData, bufferPointer, bufferSize);
     }
 
     /**
-     * Gets the currently set language
+     * Returns the currently set language, country and variant information.
      */
-    public String getLanguage() {
+    public String[] getLanguage() {
         return native_getLanguage(mJniData);
     }
 
     /**
-     * Gets the currently set rate
+     * Gets the currently set rate.
      */
     public int getRate() {
         return native_getRate(mJniData);
     }
 
     /**
-     * Shuts down the native synthesizer
+     * Shuts down the native synthesizer.
      */
     public void shutdown()  {
         native_shutdown(mJniData);
@@ -147,17 +162,23 @@
 
     private native final void native_synthesizeToFile(int jniData, String text, String filename);
 
+    private native final int  native_isLanguageAvailable(int jniData, String language,
+            String country, String variant);
+
     private native final void native_setLanguage(int jniData, String language, String country,
             String variant);
 
+    private native final void native_loadLanguage(int jniData, String language, String country,
+            String variant);
+
     private native final void native_setSpeechRate(int jniData, int speechRate);
-    
+
     private native final void native_setPitch(int jniData, int speechRate);
 
     // TODO add buffer format
     private native final void native_playAudioBuffer(int jniData, int bufferPointer, int bufferSize);
 
-    private native final String native_getLanguage(int jniData);
+    private native final String[] native_getLanguage(int jniData);
 
     private native final int native_getRate(int jniData);
 
diff --git a/packages/TtsService/src/android/tts/TtsService.java b/packages/TtsService/src/android/tts/TtsService.java
index 6fa3141..b1e6425 100755
--- a/packages/TtsService/src/android/tts/TtsService.java
+++ b/packages/TtsService/src/android/tts/TtsService.java
@@ -47,12 +47,13 @@
 public class TtsService extends Service implements OnCompletionListener {
 
     private static class SpeechItem {
-        public static final int SPEECH = 0;
-        public static final int EARCON = 1;
-        public static final int SILENCE = 2;
+        public static final int TEXT = 0;
+        public static final int IPA = 1;
+        public static final int EARCON = 2;
+        public static final int SILENCE = 3;
         public String mText = null;
         public ArrayList<String> mParams = null;
-        public int mType = SPEECH;
+        public int mType = TEXT;
         public long mDuration = 0;
 
         public SpeechItem(String text, ArrayList<String> params, int itemType) {
@@ -89,6 +90,9 @@
         }
     }
 
+    private static final int MAX_SPEECH_ITEM_CHAR_LENGTH = 4000;
+    private static final int MAX_FILENAME_LENGTH = 250;
+
     private static final String ACTION = "android.intent.action.USE_TTS";
     private static final String CATEGORY = "android.intent.category.TTS";
     private static final String PKGNAME = "android.tts";
@@ -108,7 +112,6 @@
     private final ReentrantLock synthesizerLock = new ReentrantLock();
 
     private SynthProxy nativeSynth;
-
     @Override
     public void onCreate() {
         super.onCreate();
@@ -145,13 +148,10 @@
 
 
     private void setDefaultSettings() {
-
-        // TODO handle default language
-        setLanguage("eng", "USA", "");
+        setLanguage(this.getDefaultLanguage(), getDefaultCountry(), getDefaultLocVariant());
 
         // speech rate
         setSpeechRate(getDefaultRate());
-
     }
 
 
@@ -217,6 +217,17 @@
     }
 
 
+    private int isLanguageAvailable(String lang, String country, String variant) {
+        Log.v("TTS", "TtsService.isLanguageAvailable(" + lang + ", " + country + ", " +variant+")");
+        return nativeSynth.isLanguageAvailable(lang, country, variant);
+    }
+
+
+    private String[] getLanguage() {
+        return nativeSynth.getLanguage();
+    }
+
+
     private void setLanguage(String lang, String country, String variant) {
         Log.v("TTS", "TtsService.setLanguage(" + lang + ", " + country + ", " + variant + ")");
         if (isDefaultEnforced()) {
@@ -298,7 +309,29 @@
         if (queueMode == 0) {
             stop();
         }
-        mSpeechQueue.add(new SpeechItem(text, params, SpeechItem.SPEECH));
+        mSpeechQueue.add(new SpeechItem(text, params, SpeechItem.TEXT));
+        if (!mIsSpeaking) {
+            processSpeechQueue();
+        }
+    }
+
+    /**
+     * Speaks the given IPA text using the specified queueing mode and parameters.
+     *
+     * @param ipaText
+     *            The IPA text that should be spoken
+     * @param queueMode
+     *            0 for no queue (interrupts all previous utterances), 1 for
+     *            queued
+     * @param params
+     *            An ArrayList of parameters. This is not implemented for all
+     *            engines.
+     */
+    private void speakIpa(String ipaText, int queueMode, ArrayList<String> params) {
+        if (queueMode == 0) {
+            stop();
+        }
+        mSpeechQueue.add(new SpeechItem(ipaText, params, SpeechItem.IPA));
         if (!mIsSpeaking) {
             processSpeechQueue();
         }
@@ -392,6 +425,26 @@
                         synth.start();
                         return;
                     }
+                    if (params != null){
+                        String language = "";
+                        String country = "";
+                        String variant = "";
+                        for (int i = 0; i < params.size() - 1; i = i + 2){
+                            String param = params.get(i);
+                            if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_RATE)){
+                                setSpeechRate(Integer.parseInt(params.get(i+1)));
+                            } else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_LANGUAGE)){
+                                language = params.get(i+1);
+                            } else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_COUNTRY)){
+                                country = params.get(i+1);
+                            } else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_VARIANT)){
+                                variant = params.get(i+1);
+                            }
+                        }
+                        if (language.length() > 0){
+                            setLanguage(language, country, variant);
+                        }
+                    }
                     nativeSynth.speak(text);
                 } catch (InterruptedException e) {
                     e.printStackTrace();
@@ -424,6 +477,11 @@
         return sr;
     }
 
+    private void broadcastTtsQueueProcessingCompleted(){
+        Intent i = new Intent(Intent.ACTION_TTS_QUEUE_PROCESSING_COMPLETED);
+        sendBroadcast(i);
+    }
+
     private void dispatchSpeechCompletedCallbacks(String mark) {
         Log.i("TTS callback", "dispatch started");
         // Broadcast to all clients the new value.
@@ -440,6 +498,33 @@
         Log.i("TTS callback", "dispatch completed to " + N);
     }
 
+    private SpeechItem splitCurrentTextIfNeeded(SpeechItem currentSpeechItem){
+        if (currentSpeechItem.mText.length() < MAX_SPEECH_ITEM_CHAR_LENGTH){
+            return currentSpeechItem;
+        } else {
+            ArrayList<SpeechItem> splitItems = new ArrayList<SpeechItem>();
+            int start = 0;
+            int end = start + MAX_SPEECH_ITEM_CHAR_LENGTH - 1;
+            String splitText;
+            SpeechItem splitItem;
+            while (end < currentSpeechItem.mText.length()){
+                splitText = currentSpeechItem.mText.substring(start, end);
+                splitItem = new SpeechItem(splitText, null, SpeechItem.TEXT);
+                splitItems.add(splitItem);
+                start = end;
+                end = start + MAX_SPEECH_ITEM_CHAR_LENGTH - 1;
+            }
+            splitText = currentSpeechItem.mText.substring(start);
+            splitItem = new SpeechItem(splitText, null, SpeechItem.TEXT);
+            splitItems.add(splitItem);
+            mSpeechQueue.remove(0);
+            for (int i = splitItems.size() - 1; i >= 0; i--){
+                mSpeechQueue.add(0, splitItems.get(i));
+            }
+            return mSpeechQueue.get(0);
+        }
+    }
+
     private void processSpeechQueue() {
         boolean speechQueueAvailable = false;
         try {
@@ -449,11 +534,7 @@
             }
             if (mSpeechQueue.size() < 1) {
                 mIsSpeaking = false;
-                // Dispatch a completion here as this is the
-                // only place where speech completes normally.
-                // Nothing left to say in the queue is a special case
-                // that is always a "mark" - associated text is null.
-                dispatchSpeechCompletedCallbacks("");
+                broadcastTtsQueueProcessingCompleted();
                 return;
             }
 
@@ -464,11 +545,12 @@
             // processSpeechQueue to continue running the queue
             Log.i("TTS processing: ", currentSpeechItem.mText);
             if (sr == null) {
-                if (currentSpeechItem.mType == SpeechItem.SPEECH) {
-                    // TODO: Split text up into smaller chunks before accepting
-                    // them for processing.
+                if (currentSpeechItem.mType == SpeechItem.TEXT) {
+                    currentSpeechItem = splitCurrentTextIfNeeded(currentSpeechItem);
                     speakInternalOnly(currentSpeechItem.mText,
                             currentSpeechItem.mParams);
+                } else if (currentSpeechItem.mType == SpeechItem.IPA) {
+                    // TODO Implement IPA support
                 } else {
                     // This is either silence or an earcon that was missing
                     silence(currentSpeechItem.mDuration);
@@ -534,8 +616,7 @@
     }
 
     /**
-     * Synthesizes the given text using the specified queuing mode and
-     * parameters.
+     * Synthesizes the given text to a file using the specified parameters.
      *
      * @param text
      *            The String of text that should be synthesized
@@ -564,8 +645,7 @@
                 return false;
             }
             // Don't allow a filename that is too long
-            // TODO use platform constant
-            if (filename.length() > 250) {
+            if (filename.length() > MAX_FILENAME_LENGTH) {
                 return false;
             }
             nativeSynth.synthesizeToFile(text, filename);
@@ -580,6 +660,51 @@
         return true;
     }
 
+    /**
+     * Synthesizes the given IPA text to a file using the specified parameters.
+     *
+     * @param ipaText
+     *            The String of IPA text that should be synthesized
+     * @param params
+     *            An ArrayList of parameters. The first element of this array
+     *            controls the type of voice to use.
+     * @param filename
+     *            The string that gives the full output filename; it should be
+     *            something like "/sdcard/myappsounds/mysound.wav".
+     * @return A boolean that indicates if the synthesis succeeded
+     */
+    private boolean synthesizeIpaToFile(String ipaText, ArrayList<String> params,
+            String filename, boolean calledFromApi) {
+        // Only stop everything if this is a call made by an outside app trying
+        // to
+        // use the API. Do NOT stop if this is a call from within the service as
+        // clearing the speech queue here would be a mistake.
+        if (calledFromApi) {
+            stop();
+        }
+        Log.i("TTS", "Synthesizing IPA to " + filename);
+        boolean synthAvailable = false;
+        try {
+            synthAvailable = synthesizerLock.tryLock();
+            if (!synthAvailable) {
+                return false;
+            }
+            // Don't allow a filename that is too long
+            if (filename.length() > MAX_FILENAME_LENGTH) {
+                return false;
+            }
+            // TODO: Add nativeSynth.synthesizeIpaToFile(text, filename);
+        } finally {
+            // This check is needed because finally will always run; even if the
+            // method returns somewhere in the try block.
+            if (synthAvailable) {
+                synthesizerLock.unlock();
+            }
+        }
+        Log.i("TTS", "Completed synthesis for " + filename);
+        return true;
+    }
+
     @Override
     public IBinder onBind(Intent intent) {
         if (ACTION.equals(intent.getAction())) {
@@ -626,6 +751,27 @@
         }
 
         /**
+         * Speaks the given IPA text using the specified queueing mode and
+         * parameters.
+         *
+         * @param ipaText
+         *            The IPA text that should be spoken
+         * @param queueMode
+         *            0 for no queue (interrupts all previous utterances), 1 for
+         *            queued
+         * @param params
+         *            An ArrayList of parameters. The first element of this
+         *            array controls the type of voice to use.
+         */
+        public void speakIpa(String ipaText, int queueMode, String[] params) {
+            ArrayList<String> speakingParams = new ArrayList<String>();
+            if (params != null) {
+                speakingParams = new ArrayList<String>(Arrays.asList(params));
+            }
+            mSelf.speakIpa(ipaText, queueMode, speakingParams);
+        }
+
+        /**
          * Plays the earcon using the specified queueing mode and parameters.
          *
          * @param earcon
@@ -757,6 +903,30 @@
         }
 
         /**
+         * Returns the level of support for the specified language.
+         *
+         * @param lang  the three letter ISO language code.
+         * @param country  the three letter ISO country code.
+         * @param variant  the variant code associated with the country and language pair.
+         * @return one of TTS_LANG_NOT_SUPPORTED, TTS_LANG_MISSING_DATA, TTS_LANG_AVAILABLE,
+         *      TTS_LANG_COUNTRY_AVAILABLE, TTS_LANG_COUNTRY_VAR_AVAILABLE as defined in
+         *      android.speech.tts.TextToSpeech.
+         */
+        public int isLanguageAvailable(String lang, String country, String variant) {
+            return mSelf.isLanguageAvailable(lang, country, variant);
+        }
+
+        /**
+         * Returns the currently set language / country / variant strings representing the
+         * language used by the TTS engine.
+         * @return null is no language is set, or an array of 3 string containing respectively
+         *      the language, country and variant.
+         */
+        public String[] getLanguage() {
+            return mSelf.getLanguage();
+        }
+
+        /**
          * Sets the speech rate for the TTS, which affects the synthesized voice.
          *
          * @param lang  the three letter ISO language code.
@@ -768,7 +938,7 @@
         }
 
         /**
-         * Speaks the given text using the specified queueing mode and
+         * Synthesizes the given text to a file using the specified
          * parameters.
          *
          * @param text
@@ -789,6 +959,29 @@
             }
             return mSelf.synthesizeToFile(text, speakingParams, filename, true);
         }
+
+        /**
+         * Synthesizes the given IPA text to a file using the specified
+         * parameters.
+         *
+         * @param ipaText
+         *            The String of IPA text that should be synthesized
+         * @param params
+         *            An ArrayList of parameters. The first element of this
+         *            array controls the type of voice to use.
+         * @param filename
+         *            The string that gives the full output filename; it should
+         *            be something like "/sdcard/myappsounds/mysound.wav".
+         * @return A boolean that indicates if the synthesis succeeded
+         */
+        public boolean synthesizeIpaToFile(String ipaText, String[] params,
+                String filename) {
+            ArrayList<String> speakingParams = new ArrayList<String>();
+            if (params != null) {
+                speakingParams = new ArrayList<String>(Arrays.asList(params));
+            }
+            return mSelf.synthesizeIpaToFile(ipaText, speakingParams, filename, true);
+        }
     };
 
 }
diff --git a/packages/VpnServices/src/com/android/server/vpn/AndroidServiceProxy.java b/packages/VpnServices/src/com/android/server/vpn/AndroidServiceProxy.java
index a12db8c..2ad218f 100644
--- a/packages/VpnServices/src/com/android/server/vpn/AndroidServiceProxy.java
+++ b/packages/VpnServices/src/com/android/server/vpn/AndroidServiceProxy.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007, The Android Open Source Project
+ * 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.
diff --git a/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecService.java b/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecService.java
index 8358c5c..877fa6b 100644
--- a/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecService.java
+++ b/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecService.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007, The Android Open Source Project
+ * 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.
@@ -25,8 +25,7 @@
  * The service that manages the L2TP-over-IPSec VPN connection.
  */
 class L2tpIpsecService extends VpnService<L2tpIpsecProfile> {
-    private static final String IPSEC_SERVICE = "racoon";
-    private static final String L2TP_SERVICE = "mtpd";
+    private static final String IPSEC_DAEMON = "racoon";
 
     @Override
     protected void connect(String serverIp, String username, String password)
@@ -34,7 +33,7 @@
         String hostIp = getHostIp();
 
         // IPSEC
-        AndroidServiceProxy ipsecService = startService(IPSEC_SERVICE);
+        AndroidServiceProxy ipsecService = startService(IPSEC_DAEMON);
         ipsecService.sendCommand(
                 String.format("SETKEY %s %s", hostIp, serverIp));
         ipsecService.sendCommand(String.format("SET_CERTS %s %s %s %s",
@@ -42,11 +41,11 @@
                 getUserkeyPath()));
 
         // L2TP
-        AndroidServiceProxy l2tpService = startService(L2TP_SERVICE);
-        l2tpService.sendCommand2("l2tp", serverIp, "1701", "",
-                    "file", getPppOptionFilePath(),
-                    "name", username,
-                    "password", password);
+        L2tpIpsecProfile p = getProfile();
+        MtpdHelper.sendCommand(this, L2tpService.L2TP_DAEMON, serverIp,
+                L2tpService.L2TP_PORT,
+                (p.isSecretEnabled() ? p.getSecretString() : null),
+                username, password);
     }
 
     private String getCaCertPath() {
diff --git a/packages/VpnServices/src/com/android/server/vpn/L2tpService.java b/packages/VpnServices/src/com/android/server/vpn/L2tpService.java
index 9aad7a1..9273f35 100644
--- a/packages/VpnServices/src/com/android/server/vpn/L2tpService.java
+++ b/packages/VpnServices/src/com/android/server/vpn/L2tpService.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007, The Android Open Source Project
+ * 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.
@@ -24,19 +24,15 @@
  * The service that manages the L2TP VPN connection.
  */
 class L2tpService extends VpnService<L2tpProfile> {
-    private static final String L2TP_SERVICE = "mtpd";
+    static final String L2TP_DAEMON = "l2tp";
+    static final String L2TP_PORT = "1701";
 
     @Override
     protected void connect(String serverIp, String username, String password)
             throws IOException {
-        String hostIp = getHostIp();
-
-        // L2TP
-        AndroidServiceProxy l2tpService = startService(L2TP_SERVICE);
-        l2tpService.sendCommand2("l2tp", serverIp, "1701", "",
-                    "file", getPppOptionFilePath(),
-                    "name", username,
-                    "password", password);
+        L2tpProfile p = getProfile();
+        MtpdHelper.sendCommand(this, L2TP_DAEMON, serverIp, L2TP_PORT,
+                (p.isSecretEnabled() ? p.getSecretString() : null),
+                username, password);
     }
-
 }
diff --git a/packages/VpnServices/src/com/android/server/vpn/MtpdHelper.java b/packages/VpnServices/src/com/android/server/vpn/MtpdHelper.java
new file mode 100644
index 0000000..6160900
--- /dev/null
+++ b/packages/VpnServices/src/com/android/server/vpn/MtpdHelper.java
@@ -0,0 +1,60 @@
+/*
+ * 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.android.server.vpn;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * A helper class for sending commands to the MTP daemon (mtpd).
+ */
+class MtpdHelper {
+    private static final String MTPD_SERVICE = "mtpd";
+    private static final String VPN_LINKNAME = "vpn";
+    private static final String PPP_ARGS_SEPARATOR = "";
+
+    static void sendCommand(VpnService<?> vpnService, String protocol,
+            String serverIp, String port, String secret, String username,
+            String password) throws IOException {
+        ArrayList<String> args = new ArrayList<String>();
+        args.addAll(Arrays.asList(protocol, serverIp, port));
+        if (secret != null) args.add(secret);
+        args.add(PPP_ARGS_SEPARATOR);
+        addPppArguments(vpnService, args, serverIp, username, password);
+
+        AndroidServiceProxy mtpd = vpnService.startService(MTPD_SERVICE);
+        mtpd.sendCommand2(args.toArray(new String[args.size()]));
+    }
+
+    private static void addPppArguments(VpnService<?> vpnService,
+            ArrayList<String> args, String serverIp, String username,
+            String password) throws IOException {
+        args.addAll(Arrays.asList(
+                "linkname", VPN_LINKNAME,
+                "name", username,
+                "password", password,
+                "ipparam", serverIp + "@" + vpnService.getGatewayIp(),
+                "refuse-eap", "nodefaultroute", "usepeerdns",
+                "idle", "1800",
+                "mtu", "1400",
+                "mru", "1400"));
+    }
+
+    private MtpdHelper() {
+    }
+}
diff --git a/packages/VpnServices/src/com/android/server/vpn/NormalProcessProxy.java b/packages/VpnServices/src/com/android/server/vpn/NormalProcessProxy.java
index f20d85f..f0bbc34 100644
--- a/packages/VpnServices/src/com/android/server/vpn/NormalProcessProxy.java
+++ b/packages/VpnServices/src/com/android/server/vpn/NormalProcessProxy.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007, The Android Open Source Project
+ * 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.
diff --git a/packages/VpnServices/src/com/android/server/vpn/PptpService.java b/packages/VpnServices/src/com/android/server/vpn/PptpService.java
new file mode 100644
index 0000000..01362a5
--- /dev/null
+++ b/packages/VpnServices/src/com/android/server/vpn/PptpService.java
@@ -0,0 +1,36 @@
+/*
+ * 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.android.server.vpn;
+
+import android.net.vpn.PptpProfile;
+
+import java.io.IOException;
+
+/**
+ * The service that manages the PPTP VPN connection.
+ */
+class PptpService extends VpnService<PptpProfile> {
+    static final String PPTP_DAEMON = "pptp";
+    static final String PPTP_PORT = "1723";
+    @Override
+    protected void connect(String serverIp, String username, String password)
+            throws IOException {
+        MtpdHelper.sendCommand(this, PPTP_DAEMON, serverIp, PPTP_PORT, null,
+                username, password);
+    }
+
+}
diff --git a/packages/VpnServices/src/com/android/server/vpn/ProcessProxy.java b/packages/VpnServices/src/com/android/server/vpn/ProcessProxy.java
index 14d7521..50fbf4b 100644
--- a/packages/VpnServices/src/com/android/server/vpn/ProcessProxy.java
+++ b/packages/VpnServices/src/com/android/server/vpn/ProcessProxy.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007, The Android Open Source Project
+ * 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.
diff --git a/packages/VpnServices/src/com/android/server/vpn/VpnService.java b/packages/VpnServices/src/com/android/server/vpn/VpnService.java
index fdefe9c..44127ff 100644
--- a/packages/VpnServices/src/com/android/server/vpn/VpnService.java
+++ b/packages/VpnServices/src/com/android/server/vpn/VpnService.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007, The Android Open Source Project
+ * 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.
@@ -20,6 +20,7 @@
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.content.Context;
+import android.net.NetworkUtils;
 import android.net.vpn.VpnManager;
 import android.net.vpn.VpnProfile;
 import android.net.vpn.VpnState;
@@ -31,8 +32,10 @@
 import java.io.IOException;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
+import java.net.NetworkInterface;
 import java.net.Socket;
 import java.util.ArrayList;
+import java.util.Enumeration;
 import java.util.List;
 
 /**
@@ -153,16 +156,25 @@
     }
 
     /**
-     * Returns the IP of the specified host name.
+     * Returns the IP address of the specified host name.
      */
     protected String getIp(String hostName) throws IOException {
-        InetAddress iaddr = InetAddress.getByName(hostName);
-        byte[] aa = iaddr.getAddress();
-        StringBuilder sb = new StringBuilder().append(byteToInt(aa[0]));
-        for (int i = 1; i < aa.length; i++) {
-            sb.append(".").append(byteToInt(aa[i]));
+        return InetAddress.getByName(hostName).getHostAddress();
+    }
+
+    /**
+     * Returns the IP address of the default gateway.
+     */
+    protected String getGatewayIp() throws IOException {
+        Enumeration<NetworkInterface> ifces =
+                NetworkInterface.getNetworkInterfaces();
+        for (; ifces.hasMoreElements(); ) {
+            NetworkInterface ni = ifces.nextElement();
+            int gateway = NetworkUtils.getDefaultRoute(ni.getName());
+            if (gateway == 0) continue;
+            return toInetAddress(gateway).getHostAddress();
         }
-        return sb.toString();
+        throw new IOException("Default gateway is not available");
     }
 
     /**
@@ -170,7 +182,7 @@
      * connection is established.
      */
     protected String getConnectMonitorFile() {
-        return "/etc/ppp/ip-up";
+        return "/etc/ppp/ip-up-vpn";
     }
 
     /**
@@ -461,12 +473,18 @@
     }
 
     private String reallyGetHostIp() throws IOException {
-        Socket s = new Socket();
-        s.connect(new InetSocketAddress("www.google.com", 80), DNS_TIMEOUT);
-        String ipAddress = s.getLocalAddress().getHostAddress();
-        Log.d(TAG, "Host IP: " + ipAddress);
-        s.close();
-        return ipAddress;
+        Enumeration<NetworkInterface> ifces =
+                NetworkInterface.getNetworkInterfaces();
+        for (; ifces.hasMoreElements(); ) {
+            NetworkInterface ni = ifces.nextElement();
+            int gateway = NetworkUtils.getDefaultRoute(ni.getName());
+            if (gateway == 0) continue;
+            Enumeration<InetAddress> addrs = ni.getInetAddresses();
+            for (; addrs.hasMoreElements(); ) {
+                return addrs.nextElement().getHostAddress();
+            }
+        }
+        throw new IOException("Host IP is not available");
     }
 
     private String getProfileSubpath(String subpath) throws IOException {
@@ -496,8 +514,13 @@
         return ((message == null) || (message.length() == 0));
     }
 
-    private static int byteToInt(byte b) {
-        return ((int) b) & 0x0FF;
+    private InetAddress toInetAddress(int addr) throws IOException {
+        byte[] aa = new byte[4];
+        for (int i= 0; i < aa.length; i++) {
+            aa[i] = (byte) (addr & 0x0FF);
+            addr >>= 8;
+        }
+        return InetAddress.getByAddress(aa);
     }
 
     private class ServiceHelper implements ProcessProxy.Callback {
diff --git a/packages/VpnServices/src/com/android/server/vpn/VpnServiceBinder.java b/packages/VpnServices/src/com/android/server/vpn/VpnServiceBinder.java
index 7195ea61..63fc858 100644
--- a/packages/VpnServices/src/com/android/server/vpn/VpnServiceBinder.java
+++ b/packages/VpnServices/src/com/android/server/vpn/VpnServiceBinder.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007, The Android Open Source Project
+ * 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.
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index efa6179..c67f0b5 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -34,7 +34,6 @@
 import android.content.pm.Signature;
 import android.net.Uri;
 import android.os.Binder;
-import android.os.Build;
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.Handler;
@@ -43,12 +42,13 @@
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
 import android.os.RemoteException;
+import android.os.SystemProperties;
 import android.util.Log;
 import android.util.SparseArray;
 
 import android.backup.IBackupManager;
+import android.backup.IRestoreObserver;
 import android.backup.IRestoreSession;
-import android.backup.BackupManager;
 import android.backup.RestoreSet;
 
 import com.android.internal.backup.LocalTransport;
@@ -60,7 +60,6 @@
 import java.io.EOFException;
 import java.io.File;
 import java.io.FileDescriptor;
-import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.io.RandomAccessFile;
@@ -68,13 +67,17 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 
 class BackupManagerService extends IBackupManager.Stub {
     private static final String TAG = "BackupManagerService";
     private static final boolean DEBUG = true;
 
+    // Persistent properties
+    private static final String BACKUP_TRANSPORT_PROPERTY = "persist.service.bkup.trans";
+    private static final String BACKUP_ENABLED_PROPERTY = "persist.service.bkup.enabled";
+
     // Default time to wait after data changes before we back up the data
     private static final long COLLECTION_INTERVAL = 3 * 60 * 1000;
 
@@ -87,10 +90,11 @@
 
     private Context mContext;
     private PackageManager mPackageManager;
-    private final IActivityManager mActivityManager;
+    private IActivityManager mActivityManager;
+    private boolean mEnabled;   // access to this is synchronized on 'this'
     private final BackupHandler mBackupHandler = new BackupHandler();
     // map UIDs to the set of backup client services within that UID's app set
-    private SparseArray<HashSet<ApplicationInfo>> mBackupParticipants
+    private final SparseArray<HashSet<ApplicationInfo>> mBackupParticipants
         = new SparseArray<HashSet<ApplicationInfo>>();
     // set of backup services that have pending changes
     private class BackupRequest {
@@ -109,8 +113,8 @@
     // Backups that we haven't started yet.
     private HashMap<ApplicationInfo,BackupRequest> mPendingBackups
             = new HashMap<ApplicationInfo,BackupRequest>();
-    // Do we need to back up the package manager metadata on the next pass?
-    private boolean mDoPackageManager;
+
+    // Pseudoname that we use for the Package Manager metadata "package"
     private static final String PACKAGE_MANAGER_SENTINEL = "@pm@";
 
     // locking around the pending-backup management
@@ -128,12 +132,27 @@
     private final Object mClearDataLock = new Object();
     private volatile boolean mClearingData;
 
-    // Current active transport & restore session
-    private int mTransportId;
+    // Transport bookkeeping
+    private final HashMap<String,IBackupTransport> mTransports
+            = new HashMap<String,IBackupTransport>();
+    private String mCurrentTransport;
     private IBackupTransport mLocalTransport, mGoogleTransport;
     private RestoreSession mActiveRestoreSession;
 
-    private File mStateDir;
+    private class RestoreParams {
+        public IBackupTransport transport;
+        public IRestoreObserver observer;
+        public long token;
+
+        RestoreParams(IBackupTransport _transport, IRestoreObserver _obs, long _token) {
+            transport = _transport;
+            observer = _obs;
+            token = _token;
+        }
+    }
+
+    // Where we keep our journal files and other bookkeeping
+    private File mBaseStateDir;
     private File mDataDir;
     private File mJournalDir;
     private File mJournal;
@@ -145,13 +164,15 @@
         mActivityManager = ActivityManagerNative.getDefault();
 
         // Set up our bookkeeping
-        mStateDir = new File(Environment.getDataDirectory(), "backup");
-        mStateDir.mkdirs();
+        // !!! STOPSHIP: make this disabled by default so that we then gate on
+        //               setupwizard or other opt-out UI
+        mEnabled = SystemProperties.getBoolean(BACKUP_ENABLED_PROPERTY, true);
+        mBaseStateDir = new File(Environment.getDataDirectory(), "backup");
         mDataDir = Environment.getDownloadCacheDirectory();
 
         // Set up the backup-request journaling
-        mJournalDir = new File(mStateDir, "pending");
-        mJournalDir.mkdirs();
+        mJournalDir = new File(mBaseStateDir, "pending");
+        mJournalDir.mkdirs();   // creates mBaseStateDir along the way
         makeJournalLocked();    // okay because no other threads are running yet
 
         // Build our mapping of uid to backup client services.  This implicitly
@@ -164,12 +185,19 @@
         // Set up our transport options and initialize the default transport
         // TODO: Have transports register themselves somehow?
         // TODO: Don't create transports that we don't need to?
-        mTransportId = BackupManager.TRANSPORT_LOCAL;
-        //mTransportId = BackupManager.TRANSPORT_GOOGLE;
         mLocalTransport = new LocalTransport(context);  // This is actually pretty cheap
-        mGoogleTransport = null;
+        ComponentName localName = new ComponentName(context, LocalTransport.class);
+        registerTransport(localName.flattenToShortString(), mLocalTransport);
 
-        // Attach to the Google backup transport.
+        mGoogleTransport = null;
+        // !!! TODO: set up the default transport name "the right way"
+        mCurrentTransport = SystemProperties.get(BACKUP_TRANSPORT_PROPERTY,
+                "com.google.android.backup/.BackupTransportService");
+        if (DEBUG) Log.v(TAG, "Starting with transport " + mCurrentTransport);
+
+        // Attach to the Google backup transport.  When this comes up, it will set
+        // itself as the current transport because we explicitly reset mCurrentTransport
+        // to null.
         Intent intent = new Intent().setComponent(new ComponentName(
                 "com.google.android.backup",
                 "com.google.android.backup.BackupTransportService"));
@@ -228,6 +256,13 @@
         }
     }
 
+    // Add a transport to our set of available backends
+    private void registerTransport(String name, IBackupTransport transport) {
+        synchronized (mTransports) {
+            mTransports.put(name, transport);
+        }
+    }
+
     // ----- Track installation/removal of packages -----
     BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
         public void onReceive(Context context, Intent intent) {
@@ -273,11 +308,13 @@
         public void onServiceConnected(ComponentName name, IBinder service) {
             if (DEBUG) Log.v(TAG, "Connected to Google transport");
             mGoogleTransport = IBackupTransport.Stub.asInterface(service);
+            registerTransport(name.flattenToShortString(), mGoogleTransport);
         }
 
         public void onServiceDisconnected(ComponentName name) {
             if (DEBUG) Log.v(TAG, "Disconnected from Google transport");
             mGoogleTransport = null;
+            registerTransport(name.flattenToShortString(), null);
         }
     };
 
@@ -289,7 +326,7 @@
             switch (msg.what) {
             case MSG_RUN_BACKUP:
             {
-                IBackupTransport transport = getTransport(mTransportId);
+                IBackupTransport transport = getTransport(mCurrentTransport);
                 if (transport == null) {
                     Log.v(TAG, "Backup requested but no transport available");
                     break;
@@ -336,9 +373,8 @@
 
             case MSG_RUN_RESTORE:
             {
-                int token = msg.arg1;
-                IBackupTransport transport = (IBackupTransport)msg.obj;
-                (new PerformRestoreThread(transport, token)).start();
+                RestoreParams params = (RestoreParams)msg.obj;
+                (new PerformRestoreThread(params.transport, params.observer, params.token)).start();
                 break;
             }
             }
@@ -373,7 +409,6 @@
                     mBackupParticipants.put(uid, set);
                 }
                 set.add(pkg.applicationInfo);
-                backUpPackageManagerData();
             }
         }
     }
@@ -417,7 +452,6 @@
                     for (ApplicationInfo entry: set) {
                         if (entry.packageName.equals(pkg.packageName)) {
                             set.remove(entry);
-                            backUpPackageManagerData();
                             break;
                         }
                     }
@@ -460,34 +494,27 @@
         addPackageParticipantsLockedInner(packageName, allApps);
     }
 
-    private void backUpPackageManagerData() {
-        // No need to schedule a backup just for the metadata; just piggyback on
-        // the next actual data backup.
-        synchronized(this) {
-            mDoPackageManager = true;
+    // The queue lock should be held when scheduling a backup pass
+    private void scheduleBackupPassLocked(long timeFromNowMillis) {
+        // We only schedule backups when we're actually enabled
+        synchronized (this) {
+            if (mEnabled) {
+                mBackupHandler.removeMessages(MSG_RUN_BACKUP);
+                mBackupHandler.sendEmptyMessageDelayed(MSG_RUN_BACKUP, timeFromNowMillis);
+            } else if (DEBUG) {
+                Log.v(TAG, "Disabled, so not scheduling backup pass");
+            }
         }
     }
 
-    // The queue lock should be held when scheduling a backup pass
-    private void scheduleBackupPassLocked(long timeFromNowMillis) {
-        mBackupHandler.removeMessages(MSG_RUN_BACKUP);
-        mBackupHandler.sendEmptyMessageDelayed(MSG_RUN_BACKUP, timeFromNowMillis);
-    }
-
     // Return the given transport
-    private IBackupTransport getTransport(int transportID) {
-        switch (transportID) {
-        case BackupManager.TRANSPORT_LOCAL:
-            Log.v(TAG, "Supplying local transport");
-            return mLocalTransport;
-
-        case BackupManager.TRANSPORT_GOOGLE:
-            Log.v(TAG, "Supplying Google transport");
-            return mGoogleTransport;
-
-        default:
-            Log.e(TAG, "Asked for unknown transport " + transportID);
-            return null;
+    private IBackupTransport getTransport(String transportName) {
+        synchronized (mTransports) {
+            IBackupTransport transport = mTransports.get(transportName);
+            if (transport == null) {
+                Log.w(TAG, "Requested unavailable transport: " + transportName);
+            }
+            return transport;
         }
     }
 
@@ -530,6 +557,19 @@
 
     // clear an application's data, blocking until the operation completes or times out
     void clearApplicationDataSynchronous(String packageName) {
+        // Don't wipe packages marked allowClearUserData=false
+        try {
+            PackageInfo info = mPackageManager.getPackageInfo(packageName, 0);
+            if ((info.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA) == 0) {
+                if (DEBUG) Log.i(TAG, "allowClearUserData=false so not wiping "
+                        + packageName);
+                return;
+            }
+        } catch (NameNotFoundException e) {
+            Log.w(TAG, "Tried to clear data for " + packageName + " but not found");
+            return;
+        }
+
         ClearDataObserver observer = new ClearDataObserver();
 
         synchronized(mClearDataLock) {
@@ -565,6 +605,7 @@
         private static final String TAG = "PerformBackupThread";
         IBackupTransport mTransport;
         ArrayList<BackupRequest> mQueue;
+        File mStateDir;
         File mJournal;
 
         public PerformBackupThread(IBackupTransport transport, ArrayList<BackupRequest> queue,
@@ -572,32 +613,31 @@
             mTransport = transport;
             mQueue = queue;
             mJournal = journal;
+
+            try {
+                mStateDir = new File(mBaseStateDir, transport.transportDirName());
+            } catch (RemoteException e) {
+                // can't happen; the transport is local
+            }
+            mStateDir.mkdirs();
         }
 
         @Override
         public void run() {
             if (DEBUG) Log.v(TAG, "Beginning backup of " + mQueue.size() + " targets");
 
-            // First, back up the package manager metadata if necessary
-            boolean doPackageManager;
-            synchronized (BackupManagerService.this) {
-                doPackageManager = mDoPackageManager;
-                mDoPackageManager = false;
-            }
-            if (doPackageManager) {
-                // The package manager doesn't have a proper <application> etc, but since
-                // it's running here in the system process we can just set up its agent
-                // directly and use a synthetic BackupRequest.
-                if (DEBUG) Log.i(TAG, "Running PM backup pass as well");
-
-                PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
-                        mPackageManager, allAgentPackages());
-                BackupRequest pmRequest = new BackupRequest(new ApplicationInfo(), false);
-                pmRequest.appInfo.packageName = PACKAGE_MANAGER_SENTINEL;
-                processOneBackup(pmRequest,
-                        IBackupAgent.Stub.asInterface(pmAgent.onBind()),
-                        mTransport);
-            }
+            // The package manager doesn't have a proper <application> etc, but since
+            // it's running here in the system process we can just set up its agent
+            // directly and use a synthetic BackupRequest.  We always run this pass
+            // because it's cheap and this way we guarantee that we don't get out of
+            // step even if we're selecting among various transports at run time.
+            PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
+                    mPackageManager, allAgentPackages());
+            BackupRequest pmRequest = new BackupRequest(new ApplicationInfo(), false);
+            pmRequest.appInfo.packageName = PACKAGE_MANAGER_SENTINEL;
+            processOneBackup(pmRequest,
+                    IBackupAgent.Stub.asInterface(pmAgent.onBind()),
+                    mTransport);
 
             // Now run all the backups in our queue
             doQueuedBackups(mTransport);
@@ -759,8 +799,10 @@
 
     class PerformRestoreThread extends Thread {
         private IBackupTransport mTransport;
-        private int mToken;
+        private IRestoreObserver mObserver;
+        private long mToken;
         private RestoreSet mImage;
+        private File mStateDir;
 
         class RestoreRequest {
             public PackageInfo app;
@@ -772,9 +814,18 @@
             }
         }
 
-        PerformRestoreThread(IBackupTransport transport, int restoreSetToken) {
+        PerformRestoreThread(IBackupTransport transport, IRestoreObserver observer,
+                long restoreSetToken) {
             mTransport = transport;
+            mObserver = observer;
             mToken = restoreSetToken;
+
+            try {
+                mStateDir = new File(mBaseStateDir, transport.transportDirName());
+            } catch (RemoteException e) {
+                // can't happen; the transport is local
+            }
+            mStateDir.mkdirs();
         }
 
         @Override
@@ -795,6 +846,8 @@
              * 4. shut down the transport
              */
 
+            int error = -1; // assume error
+
             // build the set of apps to restore
             try {
                 RestoreSet[] images = mTransport.getAvailableRestoreSets();
@@ -821,9 +874,19 @@
                 List<PackageInfo> agentPackages = allAgentPackages();
                 restorePackages.addAll(agentPackages);
 
-                // STOPSHIP TODO: pick out the set for this token (instead of images[0])
-                long token = images[0].token;
-                if (!mTransport.startRestore(token, restorePackages.toArray(new PackageInfo[0]))) {
+                // let the observer know that we're running
+                if (mObserver != null) {
+                    try {
+                        // !!! TODO: get an actual count from the transport after
+                        // its startRestore() runs?
+                        mObserver.restoreStarting(restorePackages.size());
+                    } catch (RemoteException e) {
+                        Log.d(TAG, "Restore observer died at restoreStarting");
+                        mObserver = null;
+                    }
+                }
+
+                if (!mTransport.startRestore(mToken, restorePackages.toArray(new PackageInfo[0]))) {
                     // STOPSHIP TODO: Handle the failure somehow?
                     Log.e(TAG, "Error starting restore operation");
                     return;
@@ -848,6 +911,7 @@
                         mPackageManager, agentPackages);
                 processOneRestore(omPackage, 0, IBackupAgent.Stub.asInterface(pmAgent.onBind()));
 
+                int count = 0;
                 for (;;) {
                     packageName = mTransport.nextRestorePackage();
                     if (packageName == null) {
@@ -858,6 +922,16 @@
                         break;
                     }
 
+                    if (mObserver != null) {
+                        ++count;
+                        try {
+                            mObserver.onUpdate(count);
+                        } catch (RemoteException e) {
+                            Log.d(TAG, "Restore observer died in onUpdate");
+                            mObserver = null;
+                        }
+                    }
+
                     Metadata metaInfo = pmAgent.getRestoredMetadata(packageName);
                     if (metaInfo == null) {
                         Log.e(TAG, "Missing metadata for " + packageName);
@@ -901,6 +975,9 @@
                         mActivityManager.unbindBackupAgent(packageInfo.applicationInfo);
                     }
                 }
+
+                // if we get this far, report success to the observer
+                error = 0;
             } catch (NameNotFoundException e) {
                 // STOPSHIP TODO: Handle the failure somehow?
                 Log.e(TAG, "Invalid paackage restoring data", e);
@@ -913,6 +990,14 @@
                 } catch (RemoteException e) {
                     Log.e(TAG, "Error finishing restore", e);
                 }
+
+                if (mObserver != null) {
+                    try {
+                        mObserver.restoreFinished(error);
+                    } catch (RemoteException e) {
+                        Log.d(TAG, "Restore observer died at restoreFinished");
+                    }
+                }
             }
         }
 
@@ -1016,7 +1101,7 @@
 
                 if (DEBUG) {
                     int numKeys = mPendingBackups.size();
-                    Log.d(TAG, "Scheduling backup for " + numKeys + " participants:");
+                    Log.d(TAG, "Now awaiting backup for " + numKeys + " participants:");
                     for (BackupRequest b : mPendingBackups.values()) {
                         Log.d(TAG, "    + " + b + " agent=" + b.appInfo.backupAgentName);
                     }
@@ -1046,7 +1131,7 @@
     // Run a backup pass immediately for any applications that have declared
     // that they have pending updates.
     public void backupNow() throws RemoteException {
-        mContext.enforceCallingPermission("android.permission.BACKUP", "tryBackupNow");
+        mContext.enforceCallingPermission("android.permission.BACKUP", "backupNow");
 
         if (DEBUG) Log.v(TAG, "Scheduling immediate backup pass");
         synchronized (mQueueLock) {
@@ -1054,21 +1139,78 @@
         }
     }
 
-    // Report the currently active transport
-    public int getCurrentTransport() {
-        mContext.enforceCallingPermission("android.permission.BACKUP", "selectBackupTransport");
-        Log.v(TAG, "getCurrentTransport() returning " + mTransportId);
-        return mTransportId;
+    // Enable/disable the backup transport
+    public void setBackupEnabled(boolean enable) {
+        mContext.enforceCallingPermission("android.permission.BACKUP", "setBackupEnabled");
+
+        boolean wasEnabled = mEnabled;
+        synchronized (this) {
+            SystemProperties.set(BACKUP_ENABLED_PROPERTY, enable ? "true" : "false");
+            mEnabled = enable;
+        }
+
+        if (enable && !wasEnabled) {
+            synchronized (mQueueLock) {
+                if (mPendingBackups.size() > 0) {
+                    // !!! TODO: better policy around timing of the first backup pass
+                    if (DEBUG) Log.v(TAG, "Backup enabled with pending data changes, scheduling");
+                    this.scheduleBackupPassLocked(COLLECTION_INTERVAL);
+                }
+            }
+        }
+}
+
+    // Report whether the backup mechanism is currently enabled
+    public boolean isBackupEnabled() {
+        mContext.enforceCallingPermission("android.permission.BACKUP", "isBackupEnabled");
+        return mEnabled;    // no need to synchronize just to read it
     }
 
-    // Select which transport to use for the next backup operation
-    public int selectBackupTransport(int transportId) {
+    // Report the name of the currently active transport
+    public String getCurrentTransport() {
+        mContext.enforceCallingPermission("android.permission.BACKUP", "getCurrentTransport");
+        Log.v(TAG, "getCurrentTransport() returning " + mCurrentTransport);
+        return mCurrentTransport;
+    }
+
+    // Report all known, available backup transports
+    public String[] listAllTransports() {
+        mContext.enforceCallingPermission("android.permission.BACKUP", "listAllTransports");
+
+        String[] list = null;
+        ArrayList<String> known = new ArrayList<String>();
+        for (Map.Entry<String, IBackupTransport> entry : mTransports.entrySet()) {
+            if (entry.getValue() != null) {
+                known.add(entry.getKey());
+            }
+        }
+
+        if (known.size() > 0) {
+            list = new String[known.size()];
+            known.toArray(list);
+        }
+        return list;
+    }
+
+    // Select which transport to use for the next backup operation.  If the given
+    // name is not one of the available transports, no action is taken and the method
+    // returns null.
+    public String selectBackupTransport(String transport) {
         mContext.enforceCallingPermission("android.permission.BACKUP", "selectBackupTransport");
 
-        int prevTransport = mTransportId;
-        mTransportId = transportId;
-        Log.v(TAG, "selectBackupTransport() set " + mTransportId + " returning " + prevTransport);
-        return prevTransport;
+        synchronized (mTransports) {
+            String prevTransport = null;
+            if (mTransports.get(transport) != null) {
+                prevTransport = mCurrentTransport;
+                mCurrentTransport = transport;
+                SystemProperties.set(BACKUP_TRANSPORT_PROPERTY, transport);
+                Log.v(TAG, "selectBackupTransport() set " + mCurrentTransport
+                        + " returning " + prevTransport);
+            } else {
+                Log.w(TAG, "Attempt to select unavailable transport " + transport);
+            }
+            return prevTransport;
+        }
     }
 
     // Callback: a requested backup agent has been instantiated.  This should only
@@ -1106,7 +1248,7 @@
     }
 
     // Hand off a restore session
-    public IRestoreSession beginRestoreSession(int transportID) {
+    public IRestoreSession beginRestoreSession(String transport) {
         mContext.enforceCallingPermission("android.permission.BACKUP", "beginRestoreSession");
 
         synchronized(this) {
@@ -1114,7 +1256,7 @@
                 Log.d(TAG, "Restore session requested but one already active");
                 return null;
             }
-            mActiveRestoreSession = new RestoreSession(transportID);
+            mActiveRestoreSession = new RestoreSession(transport);
         }
         return mActiveRestoreSession;
     }
@@ -1127,8 +1269,8 @@
         private IBackupTransport mRestoreTransport = null;
         RestoreSet[] mRestoreSets = null;
 
-        RestoreSession(int transportID) {
-            mRestoreTransport = getTransport(transportID);
+        RestoreSession(String transport) {
+            mRestoreTransport = getTransport(transport);
         }
 
         // --- Binder interface ---
@@ -1150,15 +1292,15 @@
             }
         }
 
-        public int performRestore(int token) throws android.os.RemoteException {
+        public int performRestore(long token, IRestoreObserver observer)
+                throws android.os.RemoteException {
             mContext.enforceCallingPermission("android.permission.BACKUP", "performRestore");
 
             if (mRestoreSets != null) {
                 for (int i = 0; i < mRestoreSets.length; i++) {
                     if (token == mRestoreSets[i].token) {
-                        Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE,
-                                mRestoreTransport);
-                        msg.arg1 = token;
+                        Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
+                        msg.obj = new RestoreParams(mRestoreTransport, observer, token);
                         mBackupHandler.sendMessage(msg);
                         return 0;
                     }
@@ -1189,6 +1331,11 @@
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         synchronized (mQueueLock) {
+            pw.println("Available transports:");
+            for (String t : listAllTransports()) {
+                String pad = (t.equals(mCurrentTransport)) ? "  * " : "    ";
+                pw.println(pad + t);
+            }
             int N = mBackupParticipants.size();
             pw.println("Participants:");
             for (int i=0; i<N; i++) {
@@ -1197,14 +1344,12 @@
                 pw.println(uid);
                 HashSet<ApplicationInfo> participants = mBackupParticipants.valueAt(i);
                 for (ApplicationInfo app: participants) {
-                    pw.print("    ");
-                    pw.println(app.toString());
+                    pw.println("    " + app.toString());
                 }
             }
             pw.println("Pending: " + mPendingBackups.size());
             for (BackupRequest req : mPendingBackups.values()) {
-                pw.print("   ");
-                pw.println(req);
+                pw.println("    " + req);
             }
         }
     }
diff --git a/services/java/com/android/server/PackageManagerBackupAgent.java b/services/java/com/android/server/PackageManagerBackupAgent.java
index 66fb86d..16f14e8 100644
--- a/services/java/com/android/server/PackageManagerBackupAgent.java
+++ b/services/java/com/android/server/PackageManagerBackupAgent.java
@@ -120,7 +120,10 @@
             // write its signature block to the output, keyed on the package name.
             for (PackageInfo pkg : mAllPackages) {
                 String packName = pkg.packageName;
-                if (!existing.contains(packName)) {
+                if (packName.equals(GLOBAL_METADATA_KEY)) {
+                    // We've already handled the metadata key; skip it here
+                    continue;
+                } else if (!existing.contains(packName)) {
                     // We haven't stored this app's signatures yet, so we do that now
                     try {
                         PackageInfo info = mPackageManager.getPackageInfo(packName,
diff --git a/services/java/com/android/server/Watchdog.java b/services/java/com/android/server/Watchdog.java
index fef3598..68bf4fb 100644
--- a/services/java/com/android/server/Watchdog.java
+++ b/services/java/com/android/server/Watchdog.java
@@ -504,6 +504,7 @@
                 if (mPhoneMemMonitor.checkLocked(curTime, mPhonePid,
                         mPhonePss)) {
                     // Just kill the phone process and let it restart.
+                    Log.i(TAG, "Watchdog is killing the phone process");
                     Process.killProcess(mPhonePid);
                 }
             } else {
@@ -848,6 +849,7 @@
 
             // Only kill the process if the debugger is not attached.
             if (!Debug.isDebuggerConnected()) {
+                Log.i(TAG, "Watchdog is killing the system process");
                 Process.killProcess(Process.myPid());
             }
         }
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 3b47ae7..9bad153 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -77,6 +77,7 @@
 import android.os.SystemProperties;
 import android.os.TokenWatcher;
 import android.provider.Settings;
+import android.util.DisplayMetrics;
 import android.util.EventLog;
 import android.util.Log;
 import android.util.SparseIntArray;
@@ -415,7 +416,8 @@
     final Rect mTempRect = new Rect();
 
     final Configuration mTempConfiguration = new Configuration();
-
+    int screenLayout = Configuration.SCREENLAYOUT_UNDEFINED;
+    
     public static WindowManagerService main(Context context,
             PowerManagerService pm, boolean haveInputMethods) {
         WMThread thr = new WMThread(context, pm, haveInputMethods);
@@ -3724,6 +3726,40 @@
             orientation = Configuration.ORIENTATION_LANDSCAPE;
         }
         config.orientation = orientation;
+        
+        if (screenLayout == Configuration.SCREENLAYOUT_UNDEFINED) {
+            // Note we only do this once because at this point we don't
+            // expect the screen to change in this way at runtime, and want
+            // to avoid all of this computation for every config change.
+            DisplayMetrics dm = new DisplayMetrics();
+            mDisplay.getMetrics(dm);
+            int longSize = dw;
+            int shortSize = dh;
+            if (longSize < shortSize) {
+                int tmp = longSize;
+                longSize = shortSize;
+                shortSize = tmp;
+            }
+            longSize = (int)(longSize/dm.density);
+            shortSize = (int)(shortSize/dm.density);
+            
+            // These semi-magic numbers define our compatibility modes for
+            // applications with different screens.  Don't change unless you
+            // make sure to test lots and lots of apps!
+            if (longSize < 470) {
+                // This is shorter than an HVGA normal density screen (which
+                // is 480 pixels on its long side).
+                screenLayout = Configuration.SCREENLAYOUT_SMALL;
+            } else if (longSize > 490 && shortSize > 330) {
+                // This is larger than an HVGA normal density screen (which
+                // is 480x320 pixels).
+                screenLayout = Configuration.SCREENLAYOUT_LARGE;
+            } else {
+                screenLayout = Configuration.SCREENLAYOUT_NORMAL;
+            }
+        }
+        config.screenLayout = screenLayout;
+        
         config.keyboardHidden = Configuration.KEYBOARDHIDDEN_NO;
         config.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_NO;
         mPolicy.adjustConfigurationLw(config);
@@ -3798,7 +3834,7 @@
                 "dispatchPointer " + ev);
 
         Object targetObj = mKeyWaiter.waitForNextEventTarget(null, qev,
-                ev, true, false);
+                ev, true, false, pid, uid);
 
         int action = ev.getAction();
 
@@ -3996,7 +4032,7 @@
                 TAG, "dispatchTrackball [" + ev.getAction() +"] <" + ev.getX() + ", " + ev.getY() + ">");
 
         Object focusObj = mKeyWaiter.waitForNextEventTarget(null, qev,
-                ev, false, false);
+                ev, false, false, pid, uid);
         if (focusObj == null) {
             Log.w(TAG, "No focus window, dropping trackball: " + ev);
             if (qev != null) {
@@ -4067,7 +4103,7 @@
         if (DEBUG_INPUT) Log.v(TAG, "Dispatch key: " + event);
 
         Object focusObj = mKeyWaiter.waitForNextEventTarget(event, null,
-                null, false, false);
+                null, false, false, pid, uid);
         if (focusObj == null) {
             Log.w(TAG, "No focus window, dropping: " + event);
             return INJECT_FAILED;
@@ -4184,10 +4220,14 @@
         KeyEvent newEvent = new KeyEvent(downTime, eventTime, action, code, repeatCount, metaState,
                 deviceId, scancode, KeyEvent.FLAG_FROM_SYSTEM);
 
-        int result = dispatchKey(newEvent, Binder.getCallingPid(), Binder.getCallingUid());
+        final int pid = Binder.getCallingPid();
+        final int uid = Binder.getCallingUid();
+        final long ident = Binder.clearCallingIdentity();
+        final int result = dispatchKey(newEvent, pid, uid);
         if (sync) {
-            mKeyWaiter.waitForNextEventTarget(null, null, null, false, true);
+            mKeyWaiter.waitForNextEventTarget(null, null, null, false, true, pid, uid);
         }
+        Binder.restoreCallingIdentity(ident);
         switch (result) {
             case INJECT_NO_PERMISSION:
                 throw new SecurityException(
@@ -4208,10 +4248,14 @@
      * @return Returns true if event was dispatched, false if it was dropped for any reason
      */
     public boolean injectPointerEvent(MotionEvent ev, boolean sync) {
-        int result = dispatchPointer(null, ev, Binder.getCallingPid(), Binder.getCallingUid());
+        final int pid = Binder.getCallingPid();
+        final int uid = Binder.getCallingUid();
+        final long ident = Binder.clearCallingIdentity();
+        final int result = dispatchPointer(null, ev, pid, uid);
         if (sync) {
-            mKeyWaiter.waitForNextEventTarget(null, null, null, false, true);
+            mKeyWaiter.waitForNextEventTarget(null, null, null, false, true, pid, uid);
         }
+        Binder.restoreCallingIdentity(ident);
         switch (result) {
             case INJECT_NO_PERMISSION:
                 throw new SecurityException(
@@ -4232,10 +4276,14 @@
      * @return Returns true if event was dispatched, false if it was dropped for any reason
      */
     public boolean injectTrackballEvent(MotionEvent ev, boolean sync) {
-        int result = dispatchTrackball(null, ev, Binder.getCallingPid(), Binder.getCallingUid());
+        final int pid = Binder.getCallingPid();
+        final int uid = Binder.getCallingUid();
+        final long ident = Binder.clearCallingIdentity();
+        final int result = dispatchTrackball(null, ev, pid, uid);
         if (sync) {
-            mKeyWaiter.waitForNextEventTarget(null, null, null, false, true);
+            mKeyWaiter.waitForNextEventTarget(null, null, null, false, true, pid, uid);
         }
+        Binder.restoreCallingIdentity(ident);
         switch (result) {
             case INJECT_NO_PERMISSION:
                 throw new SecurityException(
@@ -4344,7 +4392,7 @@
          */
         Object waitForNextEventTarget(KeyEvent nextKey, QueuedEvent qev,
                 MotionEvent nextMotion, boolean isPointerEvent,
-                boolean failIfTimeout) {
+                boolean failIfTimeout, int callingPid, int callingUid) {
             long startTime = SystemClock.uptimeMillis();
             long keyDispatchingTimeout = 5 * 1000;
             long waitedFor = 0;
@@ -4362,7 +4410,7 @@
                         ", mLastWin=" + mLastWin);
                 if (targetIsNew) {
                     Object target = findTargetWindow(nextKey, qev, nextMotion,
-                            isPointerEvent);
+                            isPointerEvent, callingPid, callingUid);
                     if (target == SKIP_TARGET_TOKEN) {
                         // The user has pressed a special key, and we are
                         // dropping all pending events before it.
@@ -4538,7 +4586,8 @@
         }
 
         Object findTargetWindow(KeyEvent nextKey, QueuedEvent qev,
-                MotionEvent nextMotion, boolean isPointerEvent) {
+                MotionEvent nextMotion, boolean isPointerEvent,
+                int callingPid, int callingUid) {
             mOutsideTouchTargets = null;
 
             if (nextKey != null) {
@@ -4547,9 +4596,16 @@
                 final int repeatCount = nextKey.getRepeatCount();
                 final boolean down = nextKey.getAction() != KeyEvent.ACTION_UP;
                 boolean dispatch = mKeyWaiter.checkShouldDispatchKey(keycode);
+                
                 if (!dispatch) {
-                    mPolicy.interceptKeyTi(null, keycode,
-                            nextKey.getMetaState(), down, repeatCount);
+                    if (callingUid == 0 ||
+                            mContext.checkPermission(
+                                    android.Manifest.permission.INJECT_EVENTS,
+                                    callingPid, callingUid)
+                                    == PackageManager.PERMISSION_GRANTED) {
+                        mPolicy.interceptKeyTi(null, keycode,
+                                nextKey.getMetaState(), down, repeatCount);
+                    }
                     Log.w(TAG, "Event timeout during app switch: dropping "
                             + nextKey);
                     return SKIP_TARGET_TOKEN;
@@ -4564,9 +4620,16 @@
 
                 wakeupIfNeeded(focus, LocalPowerManager.BUTTON_EVENT);
 
-                if (mPolicy.interceptKeyTi(focus,
-                        keycode, nextKey.getMetaState(), down, repeatCount)) {
-                    return CONSUMED_EVENT_TOKEN;
+                if (callingUid == 0 ||
+                        (focus != null && callingUid == focus.mSession.mUid) ||
+                        mContext.checkPermission(
+                                android.Manifest.permission.INJECT_EVENTS,
+                                callingPid, callingUid)
+                                == PackageManager.PERMISSION_GRANTED) {
+                    if (mPolicy.interceptKeyTi(focus,
+                            keycode, nextKey.getMetaState(), down, repeatCount)) {
+                        return CONSUMED_EVENT_TOKEN;
+                    }
                 }
 
                 return focus;
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 0d9d2b0..6d04b6b 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -1518,8 +1518,7 @@
                 }
             }
             
-            final BatteryStatsImpl bstats =
-                    (BatteryStatsImpl) mBatteryStatsService.getActiveStatistics();
+            final BatteryStatsImpl bstats = mBatteryStatsService.getActiveStatistics();
             synchronized(bstats) {
                 synchronized(mPidsSelfLocked) {
                     if (haveNewCpuStats) {
@@ -1534,7 +1533,7 @@
                                     ps.addCpuTimeLocked(st.rel_utime, st.rel_stime);
                                 } else {
                                     BatteryStatsImpl.Uid.Proc ps =
-                                            bstats.getProcessStatsLocked(st.name);
+                                            bstats.getProcessStatsLocked(st.name, st.pid);
                                     if (ps != null) {
                                         ps.addCpuTimeLocked(st.rel_utime, st.rel_stime);
                                     }
@@ -10473,8 +10472,17 @@
     // done with this agent
     public void unbindBackupAgent(ApplicationInfo appInfo) {
         if (DEBUG_BACKUP) Log.v(TAG, "unbindBackupAgent: " + appInfo);
+        if (appInfo == null) {
+            Log.w(TAG, "unbind backup agent for null app");
+            return;
+        }
 
         synchronized(this) {
+            if (mBackupAppName == null) {
+                Log.w(TAG, "Unbinding backup agent with no active backup");
+                return;
+            }
+
             if (!mBackupAppName.equals(appInfo.packageName)) {
                 Log.e(TAG, "Unbind of " + appInfo + " but is not the current backup target");
                 return;
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index d3942fc..dbe8431 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -701,17 +701,6 @@
     }
 
     /**
-     * Note: calls extractNetworkPortion(), so do not use for
-     * SIM EF[ADN] style records
-     *
-     * Exceptions thrown if extractNetworkPortion(s).length() == 0
-     */
-    public static byte[]
-    networkPortionToCalledPartyBCD(String s) {
-        return numberToCalledPartyBCD(extractNetworkPortion(s));
-    }
-
-    /**
      * Return true iff the network portion of <code>address</code> is,
      * as far as we can tell on the device, suitable for use as an SMS
      * destination address.
@@ -744,12 +733,25 @@
     }
 
     /**
+     * Note: calls extractNetworkPortion(), so do not use for
+     * SIM EF[ADN] style records
+     *
+     * Returns null if network portion is empty.
+     */
+    public static byte[]
+    networkPortionToCalledPartyBCD(String s) {
+        String networkPortion = extractNetworkPortion(s);
+        return numberToCalledPartyBCDHelper(networkPortion, false);
+    }
+
+    /**
      * Same as {@link #networkPortionToCalledPartyBCD}, but includes a
      * one-byte length prefix.
      */
     public static byte[]
     networkPortionToCalledPartyBCDWithLength(String s) {
-        return numberToCalledPartyBCDWithLength(extractNetworkPortion(s));
+        String networkPortion = extractNetworkPortion(s);
+        return numberToCalledPartyBCDHelper(networkPortion, true);
     }
 
     /**
@@ -761,61 +763,46 @@
      */
     public static byte[]
     numberToCalledPartyBCD(String number) {
-        // The extra byte required for '+' is taken into consideration while calculating
-        // length of ret.
-        int size = (hasPlus(number) ? number.length() - 1 : number.length());
-        byte[] ret = new byte[(size + 1) / 2 + 1];
-
-        return numberToCalledPartyBCDHelper(ret, 0, number);
+        return numberToCalledPartyBCDHelper(number, false);
     }
 
     /**
-     * Same as {@link #numberToCalledPartyBCD}, but includes a
-     * one-byte length prefix.
+     * If includeLength is true, prepend a one-byte length value to
+     * the return array.
      */
     private static byte[]
-    numberToCalledPartyBCDWithLength(String number) {
-        // The extra byte required for '+' is taken into consideration while calculating
-        // length of ret.
-        int size = (hasPlus(number) ? number.length() - 1 : number.length());
-        int length = (size + 1) / 2 + 1;
-        byte[] ret = new byte[length + 1];
+    numberToCalledPartyBCDHelper(String number, boolean includeLength) {
+        int numberLenReal = number.length();
+        int numberLenEffective = numberLenReal;
+        boolean hasPlus = number.indexOf('+') != -1;
+        if (hasPlus) numberLenEffective--;
 
-        ret[0] = (byte) (length & 0xff);
-        return numberToCalledPartyBCDHelper(ret, 1, number);
-    }
+        if (numberLenEffective == 0) return null;
 
-    private static boolean
-    hasPlus(String s) {
-      return s.indexOf('+') >= 0;
-    }
+        int resultLen = (numberLenEffective + 1) / 2;  // Encoded numbers require only 4 bits each.
+        int extraBytes = 1;                            // Prepended TOA byte.
+        if (includeLength) extraBytes++;               // Optional prepended length byte.
+        resultLen += extraBytes;
 
-    private static byte[]
-    numberToCalledPartyBCDHelper(byte[] ret, int offset, String number) {
-        if (hasPlus(number)) {
-            number = number.replaceAll("\\+", "");
-            ret[offset] = (byte) TOA_International;
-        } else {
-            ret[offset] = (byte) TOA_Unknown;
+        byte[] result = new byte[resultLen];
+
+        int digitCount = 0;
+        for (int i = 0; i < numberLenReal; i++) {
+            char c = number.charAt(i);
+            if (c == '+') continue;
+            int shift = ((digitCount & 0x01) == 1) ? 4 : 0;
+            result[extraBytes + (digitCount >> 1)] |= (byte)((charToBCD(c) & 0x0F) << shift);
+            digitCount++;
         }
 
-        int size = number.length();
-        int curChar = 0;
-        int countFullBytes = ret.length - offset - 1 - ((size - curChar) & 1);
-        for (int i = 1; i < 1 + countFullBytes; i++) {
-            ret[offset + i]
-                    = (byte) ((charToBCD(number.charAt(curChar++)))
-                              | (charToBCD(number.charAt(curChar++))) << 4);
-        }
+        // 1-fill any trailing odd nibble/quartet.
+        if ((digitCount & 0x01) == 1) result[extraBytes + (digitCount >> 1)] |= 0xF0;
 
-        // The left-over octet for odd-length phone numbers should be
-        // filled with 0xf.
-        if (countFullBytes + offset < ret.length - 1) {
-            ret[ret.length - 1]
-                    = (byte) (charToBCD(number.charAt(curChar))
-                              | (0xf << 4));
-        }
-        return ret;
+        int offset = 0;
+        if (includeLength) result[offset++] = (byte)(resultLen - 1);
+        result[offset] = (byte)(hasPlus ? TOA_International : TOA_Unknown);
+
+        return result;
     }
 
     /** all of 'a' up to len must match non-US trunk prefix ('0') */
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index a79eb3a..bf5df88 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -612,6 +612,21 @@
     }
 
     /**
+     * Returns the voice mail count.
+     * <p>
+     * Requires Permission:
+     *   {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * @hide
+     */
+    public int getVoiceMessageCount() {
+        try {
+            return getITelephony().getVoiceMessageCount();
+        } catch (RemoteException ex) {
+        }
+        return 0;
+    }
+
+    /**
      * Retrieves the alphabetic identifier associated with the voice
      * mail number.
      * <p>
diff --git a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
index af79404..c074cb8 100644
--- a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
@@ -98,8 +98,8 @@
     //***** Constants
     protected static final int RECONNECT_DELAY_INITIAL_MILLIS = 5 * 1000;
 
-    /** Cap out with 1 hour retry interval. */
-    protected static final int RECONNECT_DELAY_MAX_MILLIS = 60 * 60 * 1000;
+    /** Cap out with 30 min retry interval. */
+    protected static final int RECONNECT_DELAY_MAX_MILLIS = 30 * 60 * 1000;
 
     /** Slow poll when attempting connection recovery. */
     protected static final int POLL_NETSTAT_SLOW_MILLIS = 5000;
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 6e6f64c..d83b135 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -241,7 +241,7 @@
     /**
       * Returns the unread count of voicemails
       */
-    int getCountVoiceMessages();
+    int getVoiceMessageCount();
 
 }
 
diff --git a/telephony/java/com/android/internal/telephony/IccRecords.java b/telephony/java/com/android/internal/telephony/IccRecords.java
index 114094b..ea24c25 100644
--- a/telephony/java/com/android/internal/telephony/IccRecords.java
+++ b/telephony/java/com/android/internal/telephony/IccRecords.java
@@ -196,7 +196,7 @@
      * If not available (eg, on an older CPHS SIM) -1 is returned if
      * getVoiceMessageWaiting() is true
      */
-    public int getCountVoiceMessages() {
+    public int getVoiceMessageCount() {
         return countVoiceMessages;
     }
 
diff --git a/telephony/java/com/android/internal/telephony/Phone.java b/telephony/java/com/android/internal/telephony/Phone.java
index cdbfd61..c8d384d 100644
--- a/telephony/java/com/android/internal/telephony/Phone.java
+++ b/telephony/java/com/android/internal/telephony/Phone.java
@@ -831,7 +831,7 @@
      * Returns unread voicemail count. This count is shown when the  voicemail
      * notification is expanded.<p>
      */
-    int getCountVoiceMessages();
+    int getVoiceMessageCount();
 
     /**
      * Returns the alpha tag associated with the voice mail number.
diff --git a/telephony/java/com/android/internal/telephony/PhoneBase.java b/telephony/java/com/android/internal/telephony/PhoneBase.java
index 0783fe0..a26e729 100644
--- a/telephony/java/com/android/internal/telephony/PhoneBase.java
+++ b/telephony/java/com/android/internal/telephony/PhoneBase.java
@@ -631,6 +631,11 @@
         mNotifier.notifyDataActivity(this);
     }
 
+    public void notifyMessageWaitingIndicator() {
+        // This function is added to send the notification to DefaultPhoneNotifier.
+        mNotifier.notifyMessageWaitingChanged(this);
+    }
+
     public void notifyDataConnection(String reason) {
         mNotifier.notifyDataConnection(this, reason);
     }
@@ -638,7 +643,7 @@
     public abstract String getPhoneName();
 
     /** @hide */
-    public int getCountVoiceMessages(){
+    public int getVoiceMessageCount(){
         return 0;
     }
 
diff --git a/telephony/java/com/android/internal/telephony/PhoneProxy.java b/telephony/java/com/android/internal/telephony/PhoneProxy.java
index 4bb24dc..5b3c8dd 100644
--- a/telephony/java/com/android/internal/telephony/PhoneProxy.java
+++ b/telephony/java/com/android/internal/telephony/PhoneProxy.java
@@ -435,8 +435,8 @@
     }
 
      /** @hide */
-    public int getCountVoiceMessages(){
-        return mActivePhone.getCountVoiceMessages();
+    public int getVoiceMessageCount(){
+        return mActivePhone.getVoiceMessageCount();
     }
 
     public String getVoiceMailAlphaTag() {
diff --git a/telephony/java/com/android/internal/telephony/RIL.java b/telephony/java/com/android/internal/telephony/RIL.java
index 9f780c9..4db3e5b 100644
--- a/telephony/java/com/android/internal/telephony/RIL.java
+++ b/telephony/java/com/android/internal/telephony/RIL.java
@@ -1365,7 +1365,6 @@
         RILRequest rr
                 = RILRequest.obtain(RIL_REQUEST_CDMA_SMS_ACKNOWLEDGE, result);
 
-        rr.mp.writeInt(2);
         rr.mp.writeInt(success ? 0 : 1); //RIL_CDMA_SMS_ErrorClass
         // cause code according to X.S004-550E
         rr.mp.writeInt(cause);
diff --git a/telephony/java/com/android/internal/telephony/SMSDispatcher.java b/telephony/java/com/android/internal/telephony/SMSDispatcher.java
index a3016fa..890ea63 100644
--- a/telephony/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/SMSDispatcher.java
@@ -78,6 +78,7 @@
     protected static final String[] RAW_PROJECTION = new String[] {
         "pdu",
         "sequence",
+        "destination_port",
     };
 
     static final int MAIL_SEND_SMS = 1;
@@ -289,7 +290,13 @@
             sms = (SmsMessage) ar.result;
             try {
                 if (mStorageAvailable) {
-                    dispatchMessage(sms.mWrappedSmsMessage);
+                    int result = dispatchMessage(sms.mWrappedSmsMessage);
+                    if (result != Activity.RESULT_OK) {
+                        // RESULT_OK means that message was broadcast for app(s) to handle.
+                        // Any other result, we should ack here.
+                        boolean handled = (result == Intents.RESULT_SMS_HANDLED);
+                        acknowledgeLastIncomingSms(handled, result, null);
+                    }
                 } else {
                     acknowledgeLastIncomingSms(false, Intents.RESULT_SMS_OUT_OF_MEMORY, null);
                 }
@@ -469,8 +476,11 @@
      * Dispatches an incoming SMS messages.
      *
      * @param sms the incoming message from the phone
+     * @return a result code from {@link Telephony.Sms.Intents}, or
+     *         {@link Activity#RESULT_OK} if the message has been broadcast
+     *         to applications
      */
-    protected abstract void dispatchMessage(SmsMessageBase sms);
+    protected abstract int dispatchMessage(SmsMessageBase sms);
 
 
     /**
@@ -478,8 +488,11 @@
      * the part is stored for later processing.
      *
      * NOTE: concatRef (naturally) needs to be non-null, but portAddrs can be null.
+     * @return a result code from {@link Telephony.Sms.Intents}, or
+     *         {@link Activity#RESULT_OK} if the message has been broadcast
+     *         to applications
      */
-    protected void processMessagePart(SmsMessageBase sms,
+    protected int processMessagePart(SmsMessageBase sms,
             SmsHeader.ConcatRef concatRef, SmsHeader.PortAddrs portAddrs) {
 
         // Lookup all other related parts
@@ -506,8 +519,7 @@
                     values.put("destination_port", portAddrs.destPort);
                 }
                 mResolver.insert(mRawUri, values);
-                acknowledgeLastIncomingSms(true, Intents.RESULT_SMS_HANDLED, null);
-                return;
+                return Intents.RESULT_SMS_HANDLED;
             }
 
             // All the parts are in place, deal with them
@@ -529,8 +541,7 @@
         } catch (SQLException e) {
             Log.e(TAG, "Can't access multipart SMS database", e);
             // TODO:  Would OUT_OF_MEMORY be more appropriate?
-            acknowledgeLastIncomingSms(false, Intents.RESULT_SMS_GENERIC_ERROR, null);
-            return;
+            return Intents.RESULT_SMS_GENERIC_ERROR;
         } finally {
             if (cursor != null) cursor.close();
         }
@@ -555,7 +566,7 @@
                     output.write(data, 0, data.length);
                 }
                 // Handle the PUSH
-                mWapPush.dispatchWapPdu(output.toByteArray());
+                return mWapPush.dispatchWapPdu(output.toByteArray());
             } else {
                 // The messages were sent to a port, so concoct a URI for it
                 dispatchPortAddressedPdus(pdus, portAddrs.destPort);
@@ -564,6 +575,7 @@
             // The messages were not sent to a port
             dispatchPdus(pdus);
         }
+        return Activity.RESULT_OK;
     }
 
     /**
diff --git a/telephony/java/com/android/internal/telephony/WapPushOverSms.java b/telephony/java/com/android/internal/telephony/WapPushOverSms.java
index a9aacda..99709406 100644
--- a/telephony/java/com/android/internal/telephony/WapPushOverSms.java
+++ b/telephony/java/com/android/internal/telephony/WapPushOverSms.java
@@ -16,8 +16,10 @@
 
 package com.android.internal.telephony;
 
+import android.app.Activity;
 import android.content.Context;
 import android.content.Intent;
+import android.provider.Telephony;
 import android.provider.Telephony.Sms.Intents;
 import android.util.Config;
 import android.util.Log;
@@ -51,8 +53,11 @@
      * wap-230-wsp-20010705-a section 8 for details on the WAP PDU format.
      *
      * @param pdu The WAP PDU, made up of one or more SMS PDUs
+     * @return a result code from {@link Telephony.Sms.Intents}, or
+     *         {@link Activity#RESULT_OK} if the message has been broadcast
+     *         to applications
      */
-    public void dispatchWapPdu(byte[] pdu) {
+    public int dispatchWapPdu(byte[] pdu) {
 
         if (Config.LOGD) Log.d(LOG_TAG, "Rx: " + IccUtils.bytesToHexString(pdu));
 
@@ -64,7 +69,7 @@
         if ((pduType != WspTypeDecoder.PDU_TYPE_PUSH) &&
                 (pduType != WspTypeDecoder.PDU_TYPE_CONFIRMED_PUSH)) {
             if (Config.LOGD) Log.w(LOG_TAG, "Received non-PUSH WAP PDU. Type = " + pduType);
-            return;
+            return Intents.RESULT_SMS_HANDLED;
         }
 
         pduDecoder = new WspTypeDecoder(pdu);
@@ -77,7 +82,7 @@
          */
         if (pduDecoder.decodeUintvarInteger(index) == false) {
             if (Config.LOGD) Log.w(LOG_TAG, "Received PDU. Header Length error.");
-            return;
+            return Intents.RESULT_SMS_GENERIC_ERROR;
         }
         headerLength = (int)pduDecoder.getValue32();
         index += pduDecoder.getDecodedDataLength();
@@ -98,7 +103,7 @@
          */
         if (pduDecoder.decodeContentType(index) == false) {
             if (Config.LOGD) Log.w(LOG_TAG, "Received PDU. Header Content-Type error.");
-            return;
+            return Intents.RESULT_SMS_GENERIC_ERROR;
         }
         int binaryContentType;
         String mimeType = pduDecoder.getValueString();
@@ -128,7 +133,7 @@
                         Log.w(LOG_TAG,
                                 "Received PDU. Unsupported Content-Type = " + binaryContentType);
                     }
-                return;
+                return Intents.RESULT_SMS_HANDLED;
             }
         } else {
             if (mimeType.equals(WspTypeDecoder.CONTENT_MIME_TYPE_B_DRM_RIGHTS_XML)) {
@@ -145,7 +150,7 @@
                 binaryContentType = WspTypeDecoder.CONTENT_TYPE_B_MMS;
             } else {
                 if (Config.LOGD) Log.w(LOG_TAG, "Received PDU. Unknown Content-Type = " + mimeType);
-                return;
+                return Intents.RESULT_SMS_HANDLED;
             }
         }
         index += pduDecoder.getDecodedDataLength();
@@ -167,6 +172,7 @@
         if (dispatchedByApplication == false) {
             dispatchWapPdu_default(pdu, transactionId, pduType, mimeType, dataIndex);
         }
+        return Activity.RESULT_OK;
     }
 
     private void dispatchWapPdu_default(
diff --git a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
index 9aa7eab..3362de8 100755
--- a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
@@ -19,6 +19,7 @@
 import android.app.ActivityManagerNative;
 import android.content.Context;
 import android.content.Intent;
+import android.content.SharedPreferences;
 import android.os.AsyncResult;
 import android.os.Handler;
 import android.os.Looper;
@@ -26,6 +27,7 @@
 import android.os.Registrant;
 import android.os.RegistrantList;
 import android.os.SystemProperties;
+import android.preference.PreferenceManager;
 import android.provider.Settings;
 import android.telephony.CellLocation;
 import android.telephony.PhoneNumberUtils;
@@ -40,6 +42,7 @@
 import com.android.internal.telephony.Connection;
 import com.android.internal.telephony.DataConnection;
 import com.android.internal.telephony.IccCard;
+import com.android.internal.telephony.IccException;
 import com.android.internal.telephony.IccFileHandler;
 import com.android.internal.telephony.IccPhoneBookInterfaceManager;
 import com.android.internal.telephony.IccSmsInterfaceManager;
@@ -65,6 +68,9 @@
 
     // Default Emergency Callback Mode exit timer
     private static final int DEFAULT_ECM_EXIT_TIMER_VALUE = 30000;
+    static final String VM_COUNT_CDMA = "vm_count_key_cdma";
+    private static final String VM_NUMBER_CDMA = "vm_number_key_cdma";
+    private String mVmNumber = null;
 
     //***** Instance Variables
     CdmaCallTracker mCT;
@@ -147,6 +153,9 @@
         // This is needed to handle phone process crashes
         String inEcm=SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE, "false");
         mIsPhoneInECMState = inEcm.equals("true");
+
+        // Notify voicemails.
+        notifier.notifyMessageWaitingChanged(this);
     }
 
     public void dispose() {
@@ -300,7 +309,7 @@
 
     public boolean
     getMessageWaitingIndicator() {
-        return mRuimRecords.getVoiceMessageWaiting();
+        return (getVoiceMessageCount() > 0);
     }
 
     public List<? extends MmiCode>
@@ -678,22 +687,33 @@
     public void setVoiceMailNumber(String alphaTag,
                                    String voiceMailNumber,
                                    Message onComplete) {
-        //mSIMRecords.setVoiceMailNumber(alphaTag, voiceMailNumber, onComplete);
-        //TODO: Where do we have to store this value has to be clarified with QC
+        Message resp;
+        mVmNumber = voiceMailNumber;
+        resp = h.obtainMessage(EVENT_SET_VM_NUMBER_DONE, 0, 0, onComplete);
+        mRuimRecords.setVoiceMailNumber(alphaTag, mVmNumber, resp);
     }
 
     public String getVoiceMailNumber() {
-        //TODO: Where can we get this value has to be clarified with QC
-        //return mSIMRecords.getVoiceMailNumber();
-//      throw new RuntimeException();
-        return "*86";
+        String number = null;
+        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
+        // TODO(Moto): The default value of voicemail number should be read from a system property
+        number = sp.getString(VM_NUMBER_CDMA, "*86");
+        return number;
     }
 
     /* Returns Number of Voicemails
      * @hide
      */
-    public int getCountVoiceMessages() {
-        return mRuimRecords.getCountVoiceMessages();
+    public int getVoiceMessageCount() {
+        int voicemailCount =  mRuimRecords.getVoiceMessageCount();
+        // If mRuimRecords.getVoiceMessageCount returns zero, then there is possibility
+        // that phone was power cycled and would have lost the voicemail count.
+        // So get the count from preferences.
+        if (voicemailCount == 0) {
+            SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
+            voicemailCount = sp.getInt(VM_COUNT_CDMA, 0);
+        }
+        return voicemailCount;
     }
 
     public String getVoiceMailAlphaTag() {
@@ -820,9 +840,10 @@
         mRuimRecords.setVoiceMessageWaiting(1, mwi ? -1 : 0);
     }
 
-    public void
-    notifyMessageWaitingIndicator() {
-        mNotifier.notifyMessageWaitingChanged(this);
+    /* This function is overloaded to send number of voicemails instead of sending true/false */
+    /*package*/ void
+    updateMessageWaitingIndicator(int mwi) {
+        mRuimRecords.setVoiceMessageWaiting(1, mwi);
     }
 
     /**
@@ -984,6 +1005,19 @@
                 }
                 break;
 
+                case EVENT_SET_VM_NUMBER_DONE:{
+                    ar = (AsyncResult)msg.obj;
+                    if (IccException.class.isInstance(ar.exception)) {
+                        storeVoiceMailNumber(mVmNumber);
+                        ar.exception = null;
+                    }
+                    onComplete = (Message) ar.userObj;
+                    if (onComplete != null) {
+                        AsyncResult.forMessage(onComplete, ar.result, ar.exception);
+                        onComplete.sendToTarget();
+                    }
+                }
+
                 default:{
                     throw new RuntimeException("unexpected event not handled");
                 }
@@ -1198,4 +1232,16 @@
         int defRoamInd = getServiceState().getCdmaDefaultRoamingIndicator();
         return mEriManager.getCdmaEriText(roamInd, defRoamInd);
     }
+
+    /**
+     * Store the voicemail number in preferences
+     */
+    private void storeVoiceMailNumber(String number) {
+        // Update the preference value of voicemail number
+        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
+        SharedPreferences.Editor editor = sp.edit();
+        editor.putString(VM_NUMBER_CDMA, number);
+        editor.commit();
+    }
+
 }
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java
index da9fd0c4..ed2ea90 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java
@@ -253,11 +253,7 @@
             // Always unmute when answering a new call
             setMute(false);
             cm.acceptCall(obtainCompleteMessage());
-        } else if ((foregroundCall.connections.size() > 0) &&
-                   (ringingCall.getState() == CdmaCall.State.WAITING)) {
-            // TODO(Moto): jsh asks, "Is this check necessary? I don't think it should be
-            // possible to have no fg connection and a WAITING call, but if we should hit
-            // this situation, is a CallStateExcetion appropriate?"
+        } else if (ringingCall.getState() == CdmaCall.State.WAITING) {
             CdmaConnection cwConn = (CdmaConnection)(ringingCall.getLatestConnection());
 
             // Since there is no network response for supplimentary
@@ -530,10 +526,6 @@
                         CdmaConnection cn = (CdmaConnection)foregroundCall.connections.get(n);
                         droppedDuringPoll.add(cn);
                     }
-                    // TODO(Moto): jsh asks, "Are we sure we don't need to do this for
-                    // ringingCall as well? What if there's a WAITING call (ie, UI timer
-                    // hasn't expired, moving it to DISCONNECTED)? How/when will it
-                    // transition to DISCONNECTED?"
                 }
                 foregroundCall.setGeneric(false);
                 // Dropped connections are removed from the CallTracker
@@ -681,8 +673,12 @@
             // set the ringing call state to IDLE here to avoid a race condition
             // where a new call waiting could get a hang up from an old call
             // waiting ringingCall.
-            // TODO(Moto): jsh asks, "Should we call conn.ondisconnect() here or Somewhere?"
-            ringingCall.detach(conn);
+            //
+            // PhoneApp does the call log itself since only PhoneApp knows
+            // the hangup reason is user ignoring or timing out. So conn.onDisconnect()
+            // is not called here. Instead, conn.onLocalDisconnect() is called.
+            conn.onLocalDisconnect();
+            phone.notifyCallStateChanged();
             return;
         } else {
             try {
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaConnection.java b/telephony/java/com/android/internal/telephony/cdma/CdmaConnection.java
index 588bdf0..025382d 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaConnection.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaConnection.java
@@ -452,12 +452,7 @@
         this.cause = cause;
 
         if (!disconnected) {
-            index = -1;
-
-            disconnectTime = System.currentTimeMillis();
-            duration = SystemClock.elapsedRealtime() - connectTimeReal;
-            disconnected = true;
-
+            doDisconnect();
             if (Config.LOGD) Log.d(LOG_TAG,
                     "[CDMAConn] onDisconnect: cause=" + cause);
 
@@ -470,6 +465,21 @@
         releaseWakeLock();
     }
 
+    /** Called when the call waiting connection has been hung up */
+    /*package*/ void
+    onLocalDisconnect() {
+        if (!disconnected) {
+            doDisconnect();
+            if (Config.LOGD) Log.d(LOG_TAG,
+                    "[CDMAConn] onLoalDisconnect" );
+
+            if (parent != null) {
+                parent.detach(this);
+            }
+        }
+        releaseWakeLock();
+    }
+
     // Returns true if state has changed, false if nothing changed
     /*package*/ boolean
     update (DriverCall dc) {
@@ -587,6 +597,14 @@
     }
 
     private void
+    doDisconnect() {
+       index = -1;
+       disconnectTime = System.currentTimeMillis();
+       duration = SystemClock.elapsedRealtime() - connectTimeReal;
+       disconnected = true;
+    }
+
+    private void
     onStartedHolding() {
         holdingStartTime = SystemClock.elapsedRealtime();
     }
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
index 2a65de3..9d29272 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
@@ -26,22 +26,18 @@
 import android.net.NetworkInfo;
 import android.net.wifi.WifiManager;
 import android.os.AsyncResult;
-import android.os.Handler;
 import android.os.INetStatService;
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
-import android.os.SystemProperties;
 import android.preference.PreferenceManager;
 import android.provider.Checkin;
-import android.provider.Settings;
-import android.provider.Settings.SettingNotFoundException;
 import android.telephony.ServiceState;
 import android.telephony.TelephonyManager;
 import android.telephony.cdma.CdmaCellLocation;
-import android.util.EventLog;
 import android.text.TextUtils;
+import android.util.EventLog;
 import android.util.Log;
 
 import com.android.internal.telephony.CommandsInterface;
@@ -50,7 +46,6 @@
 import com.android.internal.telephony.DataConnection.FailCause;
 import com.android.internal.telephony.DataConnectionTracker;
 import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneProxy;
 import com.android.internal.telephony.TelephonyEventLog;
 
 import java.util.ArrayList;
@@ -67,6 +62,7 @@
     // Indicates baseband will not auto-attach
     private boolean noAutoAttach = false;
     long nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS;
+    private boolean mReregisterOnReconnectFailure = false;
     private boolean mIsScreenOn = true;
 
     //useful for debugging
@@ -464,6 +460,7 @@
         startNetStatPoll();
         // reset reconnect timer
         nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS;
+        mReregisterOnReconnectFailure = false;
     }
 
     private void resetPollStats() {
@@ -619,6 +616,21 @@
 
     private void reconnectAfterFail(FailCause lastFailCauseCode, String reason) {
         if (state == State.FAILED) {
+            if (nextReconnectDelay > RECONNECT_DELAY_MAX_MILLIS) {
+                if (mReregisterOnReconnectFailure) {
+                    // We have already tried to re-register to the network.
+                    // This might be a problem with the data network.
+                    nextReconnectDelay = RECONNECT_DELAY_MAX_MILLIS;
+                } else {
+                    // Try to Re-register to the network.
+                    Log.d(LOG_TAG, "PDP activate failed, Reregistering to the network");
+                    mReregisterOnReconnectFailure = true;
+                    mCdmaPhone.mSST.reRegisterNetwork(null);
+                    nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS;
+                    return;
+                }
+            }
+
             Log.d(LOG_TAG, "Data Connection activate failed. Scheduling next attempt for "
                     + (nextReconnectDelay / 1000) + "s");
 
@@ -634,9 +646,6 @@
 
             // double it for next time
             nextReconnectDelay *= 2;
-            if (nextReconnectDelay > RECONNECT_DELAY_MAX_MILLIS) {
-                nextReconnectDelay = RECONNECT_DELAY_MAX_MILLIS;
-            }
 
             if (!shouldPostNotification(lastFailCauseCode)) {
                 Log.d(LOG_TAG,"NOT Posting Data Connection Unavailable notification "
@@ -716,6 +725,7 @@
         // Make sure our reconnect delay starts at the initial value
         // next time the radio comes on
         nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS;
+        mReregisterOnReconnectFailure = false;
 
         if (phone.getSimulatedRadioControl() != null) {
             // Assume data is connected on the simulator
@@ -793,6 +803,7 @@
         } else {
             // reset reconnect timer
             nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS;
+            mReregisterOnReconnectFailure = false;
             // in case data setup was attempted when we were on a voice call
             trySetupData(Phone.REASON_VOICE_CALL_ENDED);
         }
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
index 2b4a700..ecdc8f6 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
@@ -20,11 +20,14 @@
 import android.app.Activity;
 import android.app.PendingIntent;
 import android.content.ContentValues;
+import android.content.SharedPreferences;
 import android.database.Cursor;
 import android.database.SQLException;
 import android.os.AsyncResult;
 import android.os.Message;
+import android.provider.Telephony;
 import android.provider.Telephony.Sms.Intents;
+import android.preference.PreferenceManager;
 import android.util.Config;
 import android.util.Log;
 
@@ -64,17 +67,12 @@
         Log.d(TAG, "handleStatusReport is a special GSM function, should never be called in CDMA!");
     }
 
-    /**
-     * Dispatches an incoming SMS messages.
-     *
-     * @param smsb the incoming message from the phone
-     */
-    protected void dispatchMessage(SmsMessageBase smsb) {
+    /** {@inheritDoc} */
+    protected int dispatchMessage(SmsMessageBase smsb) {
 
         // If sms is null, means there was a parsing error.
-        // TODO: Should NAK this.
         if (smsb == null) {
-            return;
+            return Intents.RESULT_SMS_GENERIC_ERROR;
         }
 
         // Decode BD stream and set sms variables.
@@ -83,27 +81,6 @@
         int teleService = sms.getTeleService();
         boolean handled = false;
 
-        // Teleservices W(E)MT and VMN are handled together:
-        if ((teleService == SmsEnvelope.TELESERVICE_WMT)
-                || (teleService == SmsEnvelope.TELESERVICE_WEMT)
-                || (teleService == SmsEnvelope.TELESERVICE_VMN)) {
-            // From here on we need decoded BD.
-            // Special case the message waiting indicator messages
-            if (sms.isMWISetMessage()) {
-                mCdmaPhone.updateMessageWaitingIndicator(true);
-                handled |= sms.isMwiDontStore();
-                if (Config.LOGD) {
-                    Log.d(TAG, "Received voice mail indicator set SMS shouldStore=" + !handled);
-                }
-            } else if (sms.isMWIClearMessage()) {
-                mCdmaPhone.updateMessageWaitingIndicator(false);
-                handled |= sms.isMwiDontStore();
-                if (Config.LOGD) {
-                    Log.d(TAG, "Received voice mail indicator clear SMS shouldStore=" + !handled);
-                }
-            }
-        }
-
         if (sms.getUserData() == null) {
             if (Config.LOGD) {
                 Log.d(TAG, "Received SMS without user data");
@@ -111,11 +88,25 @@
             handled = true;
         }
 
-        if (handled) return;
+        if (handled) {
+            return Intents.RESULT_SMS_HANDLED;
+        }
 
         if (SmsEnvelope.TELESERVICE_WAP == teleService){
-            processCdmaWapPdu(sms.getUserData(), sms.messageRef, sms.getOriginatingAddress());
-            return;
+            return processCdmaWapPdu(sms.getUserData(), sms.messageRef,
+                    sms.getOriginatingAddress());
+        } else if (SmsEnvelope.TELESERVICE_VMN == teleService) {
+            // handling Voicemail
+            int voicemailCount = sms.getNumOfVoicemails();
+            Log.d(TAG, "Voicemail count=" + voicemailCount);
+            // Store the voicemail count in preferences.
+            SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(
+                    ((CDMAPhone) mPhone).getContext());
+            SharedPreferences.Editor editor = sp.edit();
+            editor.putInt(CDMAPhone.VM_COUNT_CDMA, voicemailCount);
+            editor.commit();
+            ((CDMAPhone) mPhone).updateMessageWaitingIndicator(voicemailCount);
+            return Intents.RESULT_SMS_HANDLED;
         }
 
         /**
@@ -145,17 +136,19 @@
             if (smsHeader != null && smsHeader.portAddrs != null) {
                 if (smsHeader.portAddrs.destPort == SmsHeader.PORT_WAP_PUSH) {
                     // GSM-style WAP indication
-                    mWapPush.dispatchWapPdu(sms.getUserData());
+                    return mWapPush.dispatchWapPdu(sms.getUserData());
+                } else {
+                    // The message was sent to a port, so concoct a URI for it.
+                    dispatchPortAddressedPdus(pdus, smsHeader.portAddrs.destPort);
                 }
-                // The message was sent to a port, so concoct a URI for it.
-                dispatchPortAddressedPdus(pdus, smsHeader.portAddrs.destPort);
             } else {
                 // Normal short and non-port-addressed message, dispatch it.
                 dispatchPdus(pdus);
             }
+            return Activity.RESULT_OK;
         } else {
             // Process the message part.
-            processMessagePart(sms, smsHeader.concatRef, smsHeader.portAddrs);
+            return processMessagePart(sms, smsHeader.concatRef, smsHeader.portAddrs);
         }
     }
 
@@ -165,29 +158,35 @@
      * WDP segments are gathered until a datagram completes and gets dispatched.
      *
      * @param pdu The WAP-WDP PDU segment
+     * @return a result code from {@link Telephony.Sms.Intents}, or
+     *         {@link Activity#RESULT_OK} if the message has been broadcast
+     *         to applications
      */
-    protected void processCdmaWapPdu(byte[] pdu, int referenceNumber, String address) {
+    protected int processCdmaWapPdu(byte[] pdu, int referenceNumber, String address) {
         int segment;
         int totalSegments;
         int index = 0;
         int msgType;
 
-        int sourcePort;
-        int destinationPort;
+        int sourcePort = 0;
+        int destinationPort = 0;
 
         msgType = pdu[index++];
         if (msgType != 0){
             Log.w(TAG, "Received a WAP SMS which is not WDP. Discard.");
-            return;
+            return Intents.RESULT_SMS_HANDLED;
         }
         totalSegments = pdu[index++]; // >=1
         segment = pdu[index++]; // >=0
 
-        //process WDP segment
-        sourcePort = (0xFF & pdu[index++]) << 8;
-        sourcePort |= 0xFF & pdu[index++];
-        destinationPort = (0xFF & pdu[index++]) << 8;
-        destinationPort |= 0xFF & pdu[index++];
+        // Only the first segment contains sourcePort and destination Port
+        if (segment == 0) {
+            //process WDP segment
+            sourcePort = (0xFF & pdu[index++]) << 8;
+            sourcePort |= 0xFF & pdu[index++];
+            destinationPort = (0xFF & pdu[index++]) << 8;
+            destinationPort |= 0xFF & pdu[index++];
+        }
 
         // Lookup all other related parts
         StringBuilder where = new StringBuilder("reference_number =");
@@ -217,7 +216,7 @@
 
                 mResolver.insert(mRawUri, values);
 
-                return;
+                return Intents.RESULT_SMS_HANDLED;
             }
 
             // All the parts are in place, deal with them
@@ -228,6 +227,11 @@
             for (int i = 0; i < cursorCount; i++) {
                 cursor.moveToNext();
                 int cursorSequence = (int)cursor.getLong(sequenceColumn);
+                // Read the destination port from the first segment
+                if (cursorSequence == 0) {
+                    int destinationPortColumn = cursor.getColumnIndex("destination_port");
+                    destinationPort = (int)cursor.getLong(destinationPortColumn);
+                }
                 pdus[cursorSequence] = HexDump.hexStringToByteArray(
                         cursor.getString(pduColumn));
             }
@@ -237,7 +241,7 @@
             mResolver.delete(mRawUri, where.toString(), whereArgs);
         } catch (SQLException e) {
             Log.e(TAG, "Can't access multipart SMS database", e);
-            return;  // TODO: NACK the message or something, don't just discard.
+            return Intents.RESULT_SMS_GENERIC_ERROR;
         } finally {
             if (cursor != null) cursor.close();
         }
@@ -257,15 +261,14 @@
         switch (destinationPort) {
         case SmsHeader.PORT_WAP_PUSH:
             // Handle the PUSH
-            mWapPush.dispatchWapPdu(datagram);
-            break;
+            return mWapPush.dispatchWapPdu(datagram);
 
         default:{
             pdus = new byte[1][];
             pdus[0] = datagram;
             // The messages were sent to any other WAP port
             dispatchPortAddressedPdus(pdus, destinationPort);
-            break;
+            return Activity.RESULT_OK;
         }
         }
     }
diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
index 63d2c47..3a92064 100644
--- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
@@ -773,6 +773,12 @@
         return asciiDigit;
     }
 
+    /** This function  shall be called to get the number of voicemails.
+     * @hide
+     */
+    /*package*/ int getNumOfVoicemails() {
+        return mBearerData.numberOfMessages;
+    }
 
 
 }
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
index a835dee..eaeda64 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
@@ -83,7 +83,7 @@
     public static final int MESSAGE_TYPE_DELIVER_REPORT = 0x07;
     public static final int MESSAGE_TYPE_SUBMIT_REPORT  = 0x08;
 
-    public byte messageType;
+    public int messageType;
 
     /**
      * 16-bit value indicating the message ID, which increments modulo 65536.
@@ -102,7 +102,7 @@
     public static final int PRIORITY_EMERGENCY     = 0x3;
 
     public boolean priorityIndicatorSet = false;
-    public byte priority = PRIORITY_NORMAL;
+    public int priority = PRIORITY_NORMAL;
 
     /**
      * Supported privacy modes for CDMA SMS messages
@@ -114,7 +114,7 @@
     public static final int PRIVACY_SECRET         = 0x3;
 
     public boolean privacyIndicatorSet = false;
-    public byte privacy = PRIVACY_NOT_RESTRICTED;
+    public int privacy = PRIVACY_NOT_RESTRICTED;
 
     /**
      * Supported alert priority modes for CDMA SMS messages
@@ -139,7 +139,7 @@
     public static final int DISPLAY_MODE_USER           = 0x2;
 
     public boolean displayModeSet = false;
-    public byte displayMode = DISPLAY_MODE_DEFAULT;
+    public int displayMode = DISPLAY_MODE_DEFAULT;
 
     /**
      * Language Indicator values.  NOTE: the spec (3GPP2 C.S0015-B,
@@ -799,7 +799,7 @@
     private static void decodeUserData(BearerData bData, BitwiseInputStream inStream)
         throws BitwiseInputStream.AccessException
     {
-        byte paramBytes = inStream.read(8);
+        int paramBytes = inStream.read(8);
         bData.userData = new UserData();
         bData.userData.msgEncoding = inStream.read(5);
         bData.userData.msgEncodingSet = true;
@@ -867,7 +867,7 @@
             inStream.skip(offset);
             byte[] expandedData = new byte[numFields];
             for (int i = 0; i < numFields; i++) {
-                expandedData[i] = inStream.read(7);
+                expandedData[i] = (byte)inStream.read(7);
             }
             return new String(expandedData, 0, numFields, "US-ASCII");
         } catch (java.io.UnsupportedEncodingException ex) {
@@ -919,10 +919,126 @@
         }
     }
 
+    /**
+     * IS-91 Voice Mail message decoding
+     * (See 3GPP2 C.S0015-A, Table 4.3.1.4.1-1)
+     * (For character encodings, see TIA/EIA/IS-91, Annex B)
+     *
+     * Protocol Summary: The user data payload may contain 3-14
+     * characters.  The first two characters are parsed as a number
+     * and indicate the number of voicemails.  The third character is
+     * either a SPACE or '!' to indicate normal or urgent priority,
+     * respectively.  Any following characters are treated as normal
+     * text user data payload.
+     *
+     * Note that the characters encoding is 6-bit packed.
+     */
+    private static void decodeIs91VoicemailStatus(BearerData bData)
+        throws BitwiseInputStream.AccessException, CodingException
+    {
+        BitwiseInputStream inStream = new BitwiseInputStream(bData.userData.payload);
+        int dataLen = inStream.available() / 6;  // 6-bit packed character encoding.
+        int numFields = bData.userData.numFields;
+        if ((dataLen > 14) || (dataLen < 3) || (dataLen < numFields)) {
+            throw new CodingException("IS-91 voicemail status decoding failed");
+        }
+        try {
+            StringBuffer strbuf = new StringBuffer(dataLen);
+            while (inStream.available() >= 6) {
+                strbuf.append(UserData.IA5_MAP[inStream.read(6)]);
+            }
+            String data = strbuf.toString();
+            bData.numberOfMessages = Integer.parseInt(data.substring(0, 2));
+            char prioCode = data.charAt(2);
+            if (prioCode == ' ') {
+                bData.priority = PRIORITY_NORMAL;
+            } else if (prioCode == '!') {
+                bData.priority = PRIORITY_URGENT;
+            } else {
+                throw new CodingException("IS-91 voicemail status decoding failed: " +
+                        "illegal priority setting (" + prioCode + ")");
+            }
+            bData.priorityIndicatorSet = true;
+            bData.userData.payloadStr = data.substring(3, numFields - 3);
+       } catch (java.lang.NumberFormatException ex) {
+            throw new CodingException("IS-91 voicemail status decoding failed: " + ex);
+        } catch (java.lang.IndexOutOfBoundsException ex) {
+            throw new CodingException("IS-91 voicemail status decoding failed: " + ex);
+        }
+    }
+
+    /**
+     * IS-91 Short Message decoding
+     * (See 3GPP2 C.S0015-A, Table 4.3.1.4.1-1)
+     * (For character encodings, see TIA/EIA/IS-91, Annex B)
+     *
+     * Protocol Summary: The user data payload may contain 1-14
+     * characters, which are treated as normal text user data payload.
+     * Note that the characters encoding is 6-bit packed.
+     */
+    private static void decodeIs91ShortMessage(BearerData bData)
+        throws BitwiseInputStream.AccessException, CodingException
+    {
+        BitwiseInputStream inStream = new BitwiseInputStream(bData.userData.payload);
+        int dataLen = inStream.available() / 6;  // 6-bit packed character encoding.
+        int numFields = bData.userData.numFields;
+        if ((dataLen > 14) || (dataLen < numFields)) {
+            throw new CodingException("IS-91 voicemail status decoding failed");
+        }
+        StringBuffer strbuf = new StringBuffer(dataLen);
+        for (int i = 0; i < numFields; i++) {
+            strbuf.append(UserData.IA5_MAP[inStream.read(6)]);
+        }
+        bData.userData.payloadStr = strbuf.toString();
+    }
+
+    /**
+     * IS-91 CLI message (callback number) decoding
+     * (See 3GPP2 C.S0015-A, Table 4.3.1.4.1-1)
+     *
+     * Protocol Summary: The data payload may contain 1-32 digits,
+     * encoded using standard 4-bit DTMF, which are treated as a
+     * callback number.
+     */
+    private static void decodeIs91Cli(BearerData bData) throws CodingException {
+        BitwiseInputStream inStream = new BitwiseInputStream(bData.userData.payload);
+        int dataLen = inStream.available() / 4;  // 4-bit packed DTMF digit encoding.
+        int numFields = bData.userData.numFields;
+        if ((dataLen > 14) || (dataLen < 3) || (dataLen < numFields)) {
+            throw new CodingException("IS-91 voicemail status decoding failed");
+        }
+        CdmaSmsAddress addr = new CdmaSmsAddress();
+        addr.digitMode = CdmaSmsAddress.DIGIT_MODE_4BIT_DTMF;
+        addr.origBytes = bData.userData.payload;
+        addr.numberOfDigits = (byte)numFields;
+        decodeSmsAddress(addr);
+        bData.callbackNumber = addr;
+    }
+
+    private static void decodeIs91(BearerData bData)
+        throws BitwiseInputStream.AccessException, CodingException
+    {
+        switch (bData.userData.msgType) {
+        case UserData.IS91_MSG_TYPE_VOICEMAIL_STATUS:
+            decodeIs91VoicemailStatus(bData);
+            break;
+        case UserData.IS91_MSG_TYPE_CLI:
+            decodeIs91Cli(bData);
+            break;
+        case UserData.IS91_MSG_TYPE_SHORT_MESSAGE_FULL:
+        case UserData.IS91_MSG_TYPE_SHORT_MESSAGE:
+            decodeIs91ShortMessage(bData);
+            break;
+        default:
+            throw new CodingException("unsupported IS-91 message type (" +
+                    bData.userData.msgType + ")");
+        }
+    }
+
     private static void decodeReplyOption(BearerData bData, BitwiseInputStream inStream)
         throws BitwiseInputStream.AccessException, CodingException
     {
-        byte paramBytes = inStream.read(8);
+        int paramBytes = inStream.read(8);
         if (paramBytes != 1) {
             throw new CodingException("REPLY_OPTION subparam size incorrect");
         }
@@ -985,7 +1101,7 @@
     private static void decodeCallbackNumber(BearerData bData, BitwiseInputStream inStream)
         throws BitwiseInputStream.AccessException, CodingException
     {
-        byte paramBytes = inStream.read(8);
+        int paramBytes = inStream.read(8);
         CdmaSmsAddress addr = new CdmaSmsAddress();
         addr.digitMode = inStream.read(1);
         byte fieldBits = 4;
@@ -1219,7 +1335,18 @@
                 throw new CodingException("missing MESSAGE_IDENTIFIER subparam");
             }
             if (bData.userData != null) {
-                decodeUserDataPayload(bData.userData, bData.hasUserDataHeader);
+                if (bData.userData.msgEncoding == UserData.ENCODING_IS91_EXTENDED_PROTOCOL) {
+                    if ((foundSubparamMask ^
+                             (1 << SUBPARAM_MESSAGE_IDENTIFIER) ^
+                             (1 << SUBPARAM_USER_DATA))
+                            != 0) {
+                        Log.e(LOG_TAG, "IS-91 must occur without extra subparams (" +
+                              foundSubparamMask + ")");
+                    }
+                    decodeIs91(bData);
+                } else {
+                    decodeUserDataPayload(bData.userData, bData.hasUserDataHeader);
+                }
             }
             return bData;
         } catch (BitwiseInputStream.AccessException ex) {
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java b/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java
index 917ec9d..4d799665 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java
@@ -29,7 +29,7 @@
     static public final int DIGIT_MODE_4BIT_DTMF              = 0x00;
     static public final int DIGIT_MODE_8BIT_CHAR              = 0x01;
 
-    public byte digitMode;
+    public int digitMode;
 
     /**
      * Number Mode Indicator is 1-bit value that indicates whether the
@@ -39,7 +39,7 @@
     static public final int NUMBER_MODE_NOT_DATA_NETWORK      = 0x00;
     static public final int NUMBER_MODE_DATA_NETWORK          = 0x01;
 
-    public byte numberMode;
+    public int numberMode;
 
     /**
      * Number Types for data networks.
@@ -65,7 +65,7 @@
      * This field shall be set to the number of address digits
      * (See 3GPP2 C.S0015-B, v2, 3.4.3.3)
      */
-    public byte numberOfDigits;
+    public int numberOfDigits;
 
     /**
      * Numbering Plan identification is a 0 or 4-bit value that
@@ -78,7 +78,7 @@
     //static protected final int NUMBERING_PLAN_TELEX             = 0x4;
     //static protected final int NUMBERING_PLAN_PRIVATE           = 0x9;
 
-    public byte numberPlan;
+    public int numberPlan;
 
     /**
      * NOTE: the parsed string address and the raw byte array values
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java b/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java
index d8a48cc..34cbbfa 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java
@@ -40,12 +40,26 @@
     public static final int ENCODING_GSM_DCS                    = 0x0A;
 
     /**
+     * IS-91 message types.
+     * (See TIA/EIS/IS-91-A-ENGL 1999, table 3.7.1.1-3)
+     */
+    public static final int IS91_MSG_TYPE_VOICEMAIL_STATUS   = 0x82;
+    public static final int IS91_MSG_TYPE_SHORT_MESSAGE_FULL = 0x83;
+    public static final int IS91_MSG_TYPE_CLI                = 0x84;
+    public static final int IS91_MSG_TYPE_SHORT_MESSAGE      = 0x85;
+
+    /**
      * IA5 data encoding character mappings.
      * (See CCITT Rec. T.50 Tables 1 and 3)
      *
      * Note this mapping is the the same as for printable ASCII
      * characters, with a 0x20 offset, meaning that the ASCII SPACE
      * character occurs with code 0x20.
+     *
+     * Note this mapping is also equivalent to that used by the IS-91
+     * protocol, except for the latter using only 6 bits, and hence
+     * mapping only entries up to the '_' character.
+     *
      */
     public static final char[] IA5_MAP = {
         ' ', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/',
@@ -96,7 +110,6 @@
     public int msgEncoding;
     public boolean msgEncodingSet = false;
 
-    // XXX needed when encoding is IS91 or DCS (not supported yet):
     public int msgType;
 
     /**
diff --git a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
index 70d71fc..d1e4b4f 100755
--- a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
@@ -448,11 +448,6 @@
     }
 
     public void
-    notifyMessageWaitingIndicator() {
-        mNotifier.notifyMessageWaitingChanged(this);
-    }
-
-    public void
     notifyCallForwardingIndicator() {
         mNotifier.notifyCallForwardingChanged(this);
     }
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
index 035c690..c33d4b6 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -65,6 +65,7 @@
     private static final String LOG_TAG = "GSM";
     private static final boolean DBG = true;
 
+    private GSMPhone mGsmPhone;
     /**
      * Handles changes to the APN db.
      */
@@ -85,6 +86,7 @@
     // Indicates baseband will not auto-attach
     private boolean noAutoAttach = false;
     long nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS;
+    private boolean mReregisterOnReconnectFailure = false;
     private ContentResolver mResolver;
 
     private boolean mPingTestActive = false;
@@ -204,6 +206,7 @@
 
     GsmDataConnectionTracker(GSMPhone p) {
         super(p);
+        mGsmPhone = p;
 
         p.mCM.registerForAvailable (this, EVENT_RADIO_AVAILABLE, null);
         p.mCM.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
@@ -250,16 +253,16 @@
         //Unregister for all events
         phone.mCM.unregisterForAvailable(this);
         phone.mCM.unregisterForOffOrNotAvailable(this);
-        ((GSMPhone) phone).mSIMRecords.unregisterForRecordsLoaded(this);
+        mGsmPhone.mSIMRecords.unregisterForRecordsLoaded(this);
         phone.mCM.unregisterForDataStateChanged(this);
-        ((GSMPhone) phone).mCT.unregisterForVoiceCallEnded(this);
-        ((GSMPhone) phone).mCT.unregisterForVoiceCallStarted(this);
-        ((GSMPhone) phone).mSST.unregisterForGprsAttached(this);
-        ((GSMPhone) phone).mSST.unregisterForGprsDetached(this);
-        ((GSMPhone) phone).mSST.unregisterForRoamingOn(this);
-        ((GSMPhone) phone).mSST.unregisterForRoamingOff(this);
-        ((GSMPhone) phone).mSST.unregisterForPsRestrictedEnabled(this);
-        ((GSMPhone) phone).mSST.unregisterForPsRestrictedDisabled(this);
+        mGsmPhone.mCT.unregisterForVoiceCallEnded(this);
+        mGsmPhone.mCT.unregisterForVoiceCallStarted(this);
+        mGsmPhone.mSST.unregisterForGprsAttached(this);
+        mGsmPhone.mSST.unregisterForGprsDetached(this);
+        mGsmPhone.mSST.unregisterForRoamingOn(this);
+        mGsmPhone.mSST.unregisterForRoamingOff(this);
+        mGsmPhone.mSST.unregisterForPsRestrictedEnabled(this);
+        mGsmPhone.mSST.unregisterForPsRestrictedDisabled(this);
 
         phone.getContext().unregisterReceiver(this.mIntentReceiver);
         phone.getContext().getContentResolver().unregisterContentObserver(this.apnObserver);
@@ -374,10 +377,14 @@
             removeMessages(EVENT_RESTORE_DEFAULT_APN);
             setEnabled(type, false);
             if (isApnTypeActive(Phone.APN_TYPE_DEFAULT)) {
+                mRequestedApnType = Phone.APN_TYPE_DEFAULT;
                 if (dataEnabled[APN_DEFAULT_ID]) {
                     return Phone.APN_ALREADY_ACTIVE;
                 } else {
-                    cleanUpConnection(true, Phone.REASON_DATA_DISABLED);
+                    Message msg = obtainMessage(EVENT_CLEAN_UP_CONNECTION);
+                    msg.arg1 = 1; // tearDown is true;
+                    msg.obj = Phone.REASON_DATA_DISABLED;
+                    sendMessage(msg);
                     return Phone.APN_REQUEST_STARTED;
                 }
             } else {
@@ -407,8 +414,8 @@
     public boolean isDataConnectionAsDesired() {
         boolean roaming = phone.getServiceState().getRoaming();
 
-        if (((GSMPhone) phone).mSIMRecords.getRecordsLoaded() &&
-                ((GSMPhone) phone).mSST.getCurrentGprsState() == ServiceState.STATE_IN_SERVICE &&
+        if (mGsmPhone.mSIMRecords.getRecordsLoaded() &&
+                mGsmPhone.mSST.getCurrentGprsState() == ServiceState.STATE_IN_SERVICE &&
                 (!roaming || getDataOnRoamingEnabled()) &&
             !mIsWifiConnected &&
             !mIsPsRestricted ) {
@@ -572,13 +579,13 @@
             return true;
         }
 
-        int gprsState = ((GSMPhone) phone).mSST.getCurrentGprsState();
+        int gprsState = mGsmPhone.mSST.getCurrentGprsState();
         boolean roaming = phone.getServiceState().getRoaming();
-        boolean desiredPowerState = ((GSMPhone) phone).mSST.getDesiredPowerState();
+        boolean desiredPowerState = mGsmPhone.mSST.getDesiredPowerState();
 
         if ((state == State.IDLE || state == State.SCANNING)
                 && (gprsState == ServiceState.STATE_IN_SERVICE || noAutoAttach)
-                && ((GSMPhone) phone).mSIMRecords.getRecordsLoaded()
+                && mGsmPhone.mSIMRecords.getRecordsLoaded()
                 && phone.getState() == Phone.State.IDLE
                 && isDataAllowed()
                 && !mIsPsRestricted
@@ -604,8 +611,8 @@
                 log("trySetupData: Not ready for data: " +
                     " dataState=" + state +
                     " gprsState=" + gprsState +
-                    " sim=" + ((GSMPhone) phone).mSIMRecords.getRecordsLoaded() +
-                    " UMTS=" + ((GSMPhone) phone).mSST.isConcurrentVoiceAndData() +
+                    " sim=" + mGsmPhone.mSIMRecords.getRecordsLoaded() +
+                    " UMTS=" + mGsmPhone.mSST.isConcurrentVoiceAndData() +
                     " phoneState=" + phone.getState() +
                     " dataEnabled=" + getAnyDataEnabled() +
                     " roaming=" + roaming +
@@ -792,7 +799,7 @@
         isConnected = (state != State.IDLE && state != State.FAILED);
 
         // The "current" may no longer be valid.  MMS depends on this to send properly.
-        ((GSMPhone) phone).updateCurrentCarrierInProvider();
+        mGsmPhone.updateCurrentCarrierInProvider();
 
         // TODO: It'd be nice to only do this if the changed entrie(s)
         // match the current operator.
@@ -802,6 +809,7 @@
             if (!isConnected) {
                 // reset reconnect timer
                 nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS;
+                mReregisterOnReconnectFailure = false;
                 trySetupData(Phone.REASON_APN_CHANGED);
             }
         }
@@ -882,6 +890,7 @@
         startNetStatPoll();
         // reset reconnect timer
         nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS;
+        mReregisterOnReconnectFailure = false;
     }
 
     private void setupDnsProperties() {
@@ -952,7 +961,7 @@
             } else {
                 mPdpResetCount = 0;
                 EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_REREGISTER_NETWORK, sentSinceLastRecv);
-                ((GSMPhone) phone).mSST.reRegisterNetwork(null);
+                mGsmPhone.mSST.reRegisterNetwork(null);
             }
             // TODO: Add increasingly drastic recovery steps, eg,
             // reset the radio, reset the device.
@@ -1166,6 +1175,20 @@
 
     private void reconnectAfterFail(FailCause lastFailCauseCode, String reason) {
         if (state == State.FAILED) {
+            if (nextReconnectDelay > RECONNECT_DELAY_MAX_MILLIS) {
+                if (mReregisterOnReconnectFailure) {
+                    // We have already tried to re-register to the network.
+                    // This might be a problem with the data network.
+                    nextReconnectDelay = RECONNECT_DELAY_MAX_MILLIS;
+                } else {
+                    // Try to Re-register to the network.
+                    Log.d(LOG_TAG, "PDP activate failed, Reregistering to the network");
+                    mReregisterOnReconnectFailure = true;
+                    mGsmPhone.mSST.reRegisterNetwork(null);
+                    nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS;
+                    return;
+                }
+            }
             Log.d(LOG_TAG, "PDP activate failed. Scheduling next attempt for "
                     + (nextReconnectDelay / 1000) + "s");
 
@@ -1181,9 +1204,6 @@
 
             // double it for next time
             nextReconnectDelay *= 2;
-            if (nextReconnectDelay > RECONNECT_DELAY_MAX_MILLIS) {
-                nextReconnectDelay = RECONNECT_DELAY_MAX_MILLIS;
-            }
 
             if (!shouldPostNotification(lastFailCauseCode)) {
                 Log.d(LOG_TAG,"NOT Posting GPRS Unavailable notification "
@@ -1219,10 +1239,9 @@
     protected void onRestoreDefaultApn() {
         if (DBG) Log.d(LOG_TAG, "Restore default APN");
         setEnabled(Phone.APN_TYPE_MMS, false);
-
+        mRequestedApnType = Phone.APN_TYPE_DEFAULT;
         if (!isApnTypeActive(Phone.APN_TYPE_DEFAULT)) {
             cleanUpConnection(true, Phone.REASON_RESTORE_DEFAULT_APN);
-            mRequestedApnType = Phone.APN_TYPE_DEFAULT;
         }
     }
 
@@ -1258,6 +1277,7 @@
         // Make sure our reconnect delay starts at the initial value
         // next time the radio comes on
         nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS;
+        mReregisterOnReconnectFailure = false;
 
         if (phone.getSimulatedRadioControl() != null) {
             // Assume data is connected on the simulator
@@ -1370,7 +1390,7 @@
     }
 
     protected void onVoiceCallStarted() {
-        if (state == State.CONNECTED && !((GSMPhone) phone).mSST.isConcurrentVoiceAndData()) {
+        if (state == State.CONNECTED && ! mGsmPhone.mSST.isConcurrentVoiceAndData()) {
             stopNetStatPoll();
             phone.notifyDataConnection(Phone.REASON_VOICE_CALL_STARTED);
         }
@@ -1378,7 +1398,7 @@
 
     protected void onVoiceCallEnded() {
         if (state == State.CONNECTED) {
-            if (!((GSMPhone) phone).mSST.isConcurrentVoiceAndData()) {
+            if (!mGsmPhone.mSST.isConcurrentVoiceAndData()) {
                 startNetStatPoll();
                 phone.notifyDataConnection(Phone.REASON_VOICE_CALL_ENDED);
             } else {
@@ -1388,6 +1408,7 @@
         } else {
             // reset reconnect timer
             nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS;
+            mReregisterOnReconnectFailure = false;
             // in case data setup was attempted when we were on a voice call
             trySetupData(Phone.REASON_VOICE_CALL_ENDED);
         }
@@ -1417,7 +1438,7 @@
      */
     private void createAllApnList() {
         allApns = new ArrayList<ApnSetting>();
-        String operator = ((GSMPhone) phone).mSIMRecords.getSIMOperatorNumeric();
+        String operator = mGsmPhone.mSIMRecords.getSIMOperatorNumeric();
 
         if (operator != null) {
             String selection = "numeric = '" + operator + "'";
@@ -1459,7 +1480,7 @@
         DataConnection pdp;
 
         for (int i = 0; i < PDP_CONNECTION_POOL_SIZE; i++) {
-            pdp = new PdpConnection((GSMPhone) phone);
+            pdp = new PdpConnection(mGsmPhone);
             pdpList.add(pdp);
          }
     }
@@ -1478,7 +1499,7 @@
      */
     private ArrayList<ApnSetting> buildWaitingApns() {
         ArrayList<ApnSetting> apnList = new ArrayList<ApnSetting>();
-        String operator = ((GSMPhone )phone).mSIMRecords.getSIMOperatorNumeric();
+        String operator = mGsmPhone.mSIMRecords.getSIMOperatorNumeric();
 
         if (mRequestedApnType.equals(Phone.APN_TYPE_DEFAULT)) {
             if (canSetPreferApn && preferredApn != null) {
@@ -1664,6 +1685,7 @@
                     if (state == State.FAILED) {
                         cleanUpConnection(false, Phone.REASON_PS_RESTRICT_ENABLED);
                         nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS;
+                        mReregisterOnReconnectFailure = false;
                     }
                     trySetupData(Phone.REASON_PS_RESTRICT_ENABLED);
                 }
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
index f87392a..2770ddc 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
@@ -78,24 +78,16 @@
                 }
             }
         }
-
-        if (mCm != null) {
-            mCm.acknowledgeLastIncomingGsmSms(true, Intents.RESULT_SMS_HANDLED, null);
-        }
+        acknowledgeLastIncomingSms(true, Intents.RESULT_SMS_HANDLED, null);
     }
 
 
-    /**
-     * Dispatches an incoming SMS messages.
-     *
-     * @param sms the incoming message from the phone
-     */
-    protected void dispatchMessage(SmsMessageBase smsb) {
+    /** {@inheritDoc} */
+    protected int dispatchMessage(SmsMessageBase smsb) {
 
         // If sms is null, means there was a parsing error.
-        // TODO: Should NAK this.
         if (smsb == null) {
-            return;
+            return Intents.RESULT_SMS_GENERIC_ERROR;
         }
         SmsMessage sms = (SmsMessage) smsb;
         boolean handled = false;
@@ -115,7 +107,9 @@
             }
         }
 
-        if (handled) return;
+        if (handled) {
+            return Intents.RESULT_SMS_HANDLED;
+        }
 
         SmsHeader smsHeader = sms.getUserDataHeader();
          // See if message is partial or port addressed.
@@ -126,17 +120,19 @@
 
             if (smsHeader != null && smsHeader.portAddrs != null) {
                 if (smsHeader.portAddrs.destPort == SmsHeader.PORT_WAP_PUSH) {
-                    mWapPush.dispatchWapPdu(sms.getUserData());
+                    return mWapPush.dispatchWapPdu(sms.getUserData());
+                } else {
+                    // The message was sent to a port, so concoct a URI for it.
+                    dispatchPortAddressedPdus(pdus, smsHeader.portAddrs.destPort);
                 }
-                // The message was sent to a port, so concoct a URI for it.
-                dispatchPortAddressedPdus(pdus, smsHeader.portAddrs.destPort);
             } else {
                 // Normal short and non-port-addressed message, dispatch it.
                 dispatchPdus(pdus);
             }
+            return Activity.RESULT_OK;
         } else {
             // Process the message part.
-            processMessagePart(sms, smsHeader.concatRef, smsHeader.portAddrs);
+            return processMessagePart(sms, smsHeader.concatRef, smsHeader.portAddrs);
         }
     }
 
diff --git a/tests/AndroidTests/src/com/android/unit_tests/BitwiseStreamsTest.java b/tests/AndroidTests/src/com/android/unit_tests/BitwiseStreamsTest.java
index a935247..c5562b3 100644
--- a/tests/AndroidTests/src/com/android/unit_tests/BitwiseStreamsTest.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/BitwiseStreamsTest.java
@@ -25,6 +25,8 @@
 
 import android.util.Log;
 
+import java.util.Random;
+
 public class BitwiseStreamsTest extends AndroidTestCase {
     private final static String LOG_TAG = "BitwiseStreamsTest";
 
@@ -39,7 +41,7 @@
         BitwiseInputStream inStream = new BitwiseInputStream(outBuf);
         byte[] inBufDup = new byte[inBuf.length];
         inStream.skip(offset);
-        for (int i = 0; i < inBufDup.length; i++) inBufDup[i] = inStream.read(8);
+        for (int i = 0; i < inBufDup.length; i++) inBufDup[i] = (byte)inStream.read(8);
         assertEquals(HexDump.toHexString(inBuf), HexDump.toHexString(inBufDup));
     }
 
@@ -53,7 +55,7 @@
         BitwiseInputStream inStream = new BitwiseInputStream(outStream.toByteArray());
         inStream.skip(offset);
         byte[] inBufDup = new byte[inBuf.length];
-        for (int i = 0; i < inBufDup.length; i++) inBufDup[i] = inStream.read(8);
+        for (int i = 0; i < inBufDup.length; i++) inBufDup[i] = (byte)inStream.read(8);
         assertEquals(HexDump.toHexString(inBuf), HexDump.toHexString(inBufDup));
     }
 
@@ -67,7 +69,7 @@
         BitwiseInputStream inStream = new BitwiseInputStream(outStream.toByteArray());
         inStream.skip(offset);
         byte[] inBufDup = new byte[inBuf.length];
-        for (int i = 0; i < inBufDup.length; i++) inBufDup[i] = inStream.read(8);
+        for (int i = 0; i < inBufDup.length; i++) inBufDup[i] = (byte)inStream.read(8);
         assertEquals(HexDump.toHexString(inBuf), HexDump.toHexString(inBufDup));
     }
 
@@ -84,12 +86,33 @@
         BitwiseInputStream inStream = new BitwiseInputStream(outStream.toByteArray());
         inStream.skip(offset);
         byte[] inBufDup = new byte[inBuf.length];
-        for (int i = 0; i < inBufDup.length; i++) inBufDup[i] = inStream.read(8);
+        for (int i = 0; i < inBufDup.length; i++) inBufDup[i] = (byte)inStream.read(8);
         assertEquals(HexDump.toHexString(inBuf), HexDump.toHexString(inBufDup));
     }
 
     @SmallTest
     public void testFive() throws Exception {
+        Random random = new Random();
+        int iterations = 10000;
+        int[] sizeArr = new int[iterations];
+        int[] valueArr = new int[iterations];
+        BitwiseOutputStream outStream = new BitwiseOutputStream(iterations * 4);
+        for (int i = 0; i < iterations; i++) {
+            int x = random.nextInt();
+            int size = (x & 0x07) + 1;
+            int value = x & (-1 >>> (32 - size));
+            sizeArr[i] = size;
+            valueArr[i] = value;
+            outStream.write(size, value);
+        }
+        BitwiseInputStream inStream = new BitwiseInputStream(outStream.toByteArray());
+        for (int i = 0; i < iterations; i++) {
+            assertEquals(valueArr[i], inStream.read(sizeArr[i]));
+        }
+    }
+
+    @SmallTest
+    public void testSix() throws Exception {
         int num_runs = 10;
         long start = android.os.SystemClock.elapsedRealtime();
         for (int run = 0; run < num_runs; run++) {
@@ -104,7 +127,7 @@
             BitwiseInputStream inStream = new BitwiseInputStream(outStream.toByteArray());
             inStream.skip(offset);
             byte[] inBufDup = new byte[inBuf.length];
-            for (int i = 0; i < inBufDup.length; i++) inBufDup[i] = inStream.read(8);
+            for (int i = 0; i < inBufDup.length; i++) inBufDup[i] = (byte)inStream.read(8);
             assertEquals(HexDump.toHexString(inBuf), HexDump.toHexString(inBufDup));
         }
         long end = android.os.SystemClock.elapsedRealtime();
diff --git a/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java b/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java
index b2529811..2ff0a6a 100644
--- a/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java
@@ -30,6 +30,8 @@
 
 import java.util.Iterator;
 
+import java.lang.Integer;
+
 import android.util.Log;
 
 public class CdmaSmsTest extends AndroidTestCase {
@@ -171,14 +173,14 @@
         assertEquals(bearerData.msgCenterTimeStamp.minute, 1);
         assertEquals(bearerData.msgCenterTimeStamp.second, 59);
         assertEquals(bearerData.validityPeriodAbsolute, null);
-        assertEquals(bearerData.validityPeriodRelative, -63);
+        assertEquals(bearerData.validityPeriodRelative, 193);
         assertEquals(bearerData.deferredDeliveryTimeAbsolute.year, 1997);
         assertEquals(bearerData.deferredDeliveryTimeAbsolute.month, 5);
         assertEquals(bearerData.deferredDeliveryTimeAbsolute.monthDay, 18);
         assertEquals(bearerData.deferredDeliveryTimeAbsolute.hour, 0);
         assertEquals(bearerData.deferredDeliveryTimeAbsolute.minute, 0);
         assertEquals(bearerData.deferredDeliveryTimeAbsolute.second, 0);
-        assertEquals(bearerData.deferredDeliveryTimeRelative, -57);
+        assertEquals(bearerData.deferredDeliveryTimeRelative, 199);
         assertEquals(bearerData.hasUserDataHeader, false);
         assertEquals(bearerData.userData.msgEncoding, UserData.ENCODING_7BIT_ASCII);
         assertEquals(bearerData.userData.numFields, 2);
@@ -225,7 +227,7 @@
         assertEquals(bearerData.deferredDeliveryTimeAbsolute.hour, 0);
         assertEquals(bearerData.deferredDeliveryTimeAbsolute.minute, 0);
         assertEquals(bearerData.deferredDeliveryTimeAbsolute.second, 0);
-        assertEquals(bearerData.deferredDeliveryTimeRelative, -110);
+        assertEquals(bearerData.deferredDeliveryTimeRelative, 146);
         assertEquals(bearerData.hasUserDataHeader, false);
         assertEquals(bearerData.userData.msgEncoding, UserData.ENCODING_7BIT_ASCII);
         assertEquals(bearerData.userData.numFields, 2);
@@ -679,4 +681,15 @@
         assertEquals(revBearerData.displayModeSet, true);
         assertEquals(revBearerData.displayMode, bearerData.displayMode);
     }
+
+    @SmallTest
+    public void testIs91() throws Exception {
+        String pdu1 = "000320001001070c2039acc13880";
+        BearerData bd1 = BearerData.decode(HexDump.hexStringToByteArray(pdu1));
+        assertEquals(bd1.callbackNumber.address, "3598271");
+        String pdu4 = "000320001001080c283c314724b34e";
+        BearerData bd4 = BearerData.decode(HexDump.hexStringToByteArray(pdu4));
+        assertEquals(bd4.userData.payloadStr, "ABCDEFG");
+    }
+
 }
diff --git a/tests/CoreTests/com/android/internal/telephony/PhoneNumberUtilsTest.java b/tests/CoreTests/com/android/internal/telephony/PhoneNumberUtilsTest.java
index e8bd239..577d384 100644
--- a/tests/CoreTests/com/android/internal/telephony/PhoneNumberUtilsTest.java
+++ b/tests/CoreTests/com/android/internal/telephony/PhoneNumberUtilsTest.java
@@ -93,6 +93,13 @@
             assertEquals(b[i], bRet[i]);
         }
 
+        bRet = PhoneNumberUtils.networkPortionToCalledPartyBCDWithLength("+17005550020");
+        assertEquals(8, bRet.length);
+        assertEquals(bRet[0], 7);
+        for (int i = 1; i < 8; i++) {
+            assertEquals(b[i - 1], bRet[i]);
+        }
+
         bRet = PhoneNumberUtils.networkPortionToCalledPartyBCD("7005550020");
         assertEquals("7005550020",
             PhoneNumberUtils.calledPartyBCDToString(bRet, 0, bRet.length));
diff --git a/tests/DpiTest/AndroidManifest.xml b/tests/DpiTest/AndroidManifest.xml
index f71cff2..64ad7be 100644
--- a/tests/DpiTest/AndroidManifest.xml
+++ b/tests/DpiTest/AndroidManifest.xml
@@ -16,6 +16,8 @@
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.google.android.test.dpi">
+    <uses-sdk android:minSdkVersion="3" android:targetSdkVersion="3" />
+    <supports-screens android:smallScreens="true" />
     <application android:label="DpiTest">
         <activity android:name="DpiTestActivity" android:label="DpiTest">
             <intent-filter>
diff --git a/tests/DpiTest/res/values-largeScreen/strings.xml b/tests/DpiTest/res/values-largeScreen/strings.xml
new file mode 100644
index 0000000..f4dd543
--- /dev/null
+++ b/tests/DpiTest/res/values-largeScreen/strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 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="act_title">DpiTest: Large Screen</string>
+</resources>
diff --git a/tests/DpiTest/res/values-normalScreen/strings.xml b/tests/DpiTest/res/values-normalScreen/strings.xml
new file mode 100644
index 0000000..256d696
--- /dev/null
+++ b/tests/DpiTest/res/values-normalScreen/strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 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="act_title">DpiTest: Normal Screen</string>
+</resources>
diff --git a/tests/DpiTest/res/values-smallScreen/strings.xml b/tests/DpiTest/res/values-smallScreen/strings.xml
new file mode 100644
index 0000000..cdb4ac9
--- /dev/null
+++ b/tests/DpiTest/res/values-smallScreen/strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 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="act_title">DpiTest: Small Screen</string>
+</resources>
diff --git a/tests/DpiTest/res/values/strings.xml b/tests/DpiTest/res/values/strings.xml
new file mode 100644
index 0000000..ef924ac
--- /dev/null
+++ b/tests/DpiTest/res/values/strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 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="act_title">DpiTest: Unknown Screen</string>
+</resources>
diff --git a/tests/DpiTest/src/com/google/android/test/dpi/DpiTestActivity.java b/tests/DpiTest/src/com/google/android/test/dpi/DpiTestActivity.java
index 3759622..5a9f3f5 100644
--- a/tests/DpiTest/src/com/google/android/test/dpi/DpiTestActivity.java
+++ b/tests/DpiTest/src/com/google/android/test/dpi/DpiTestActivity.java
@@ -34,6 +34,7 @@
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
+        this.setTitle(R.string.act_title);
         LinearLayout root = new LinearLayout(this);
         root.setOrientation(LinearLayout.VERTICAL);
 
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/FileList.java b/tests/DumpRenderTree/src/com/android/dumprendertree/FileList.java
index 0218317..e741177 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/FileList.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/FileList.java
@@ -23,7 +23,9 @@
 import java.util.Map;
 import java.io.File;
 
+import android.app.AlertDialog;
 import android.app.ListActivity;
+import android.content.DialogInterface;
 import android.view.KeyEvent;
 import android.view.View;
 import android.widget.ListView;
@@ -31,7 +33,7 @@
 import android.os.Bundle;
 
 
-public abstract class FileList extends ListActivity 
+public abstract class FileList extends ListActivity
 {
 	public boolean onKeyDown(int keyCode, KeyEvent event) {
 		switch (keyCode)
@@ -39,7 +41,7 @@
 			case KeyEvent.KEYCODE_DPAD_LEFT:
 				if (mPath.length() > mBaseLength) {
 					File f = new File(mPath);
-					mFocusFile = f.getName(); 
+					mFocusFile = f.getName();
 					mFocusIndex = 0;
 					f = f.getParentFile();
 					mPath = f.getPath();
@@ -47,7 +49,7 @@
 					return true;
 				}
 				break;
-				
+
 			case KeyEvent.KEYCODE_DPAD_RIGHT:
 				{
 					Map map = (Map) getListView().getItemAtPosition(getListView().getSelectedItemPosition());
@@ -61,24 +63,24 @@
 					}
                     return true;
 				}
-	
+
 			default:
 				break;
 		}
 		return super.onKeyDown(keyCode, event);
 	}
 
-	public void onCreate(Bundle icicle) 
+	public void onCreate(Bundle icicle)
     {
         super.onCreate(icicle);
         setupPath();
         updateList();
     }
-    
+
     protected List getData()
     {
         List myData = new ArrayList<HashMap>();
-        
+
         File f = new File(mPath);
         if (!f.exists()) {
         	addItem(myData, "!LayoutTests path missing!", "");
@@ -103,10 +105,10 @@
 	        	    addItem(myData, files[i], path);
         	}
         }
-        
+
         return myData;
     }
-    
+
     protected void addItem(List<Map> data, String name, String path)
     {
         HashMap temp = new HashMap();
@@ -114,34 +116,58 @@
         temp.put("path", path);
         data.add(temp);
     }
-    
+
     protected void onListItemClick(ListView l, View v, int position, long id)
     {
-    	Map map = (Map) l.getItemAtPosition(position);
-    	String path = (String)map.get("path");
+        Map map = (Map) l.getItemAtPosition(position);
+        final String path = (String)map.get("path");
 
         if ((new File(path)).isDirectory()) {
-            mPath = path;
-            mFocusFile = null;
-            updateList();
+            final CharSequence[] items = {"Open", "Run"};
+            AlertDialog.Builder builder = new AlertDialog.Builder(this);
+            builder.setTitle("Select an Action");
+            builder.setSingleChoiceItems(items, -1,
+                    new DialogInterface.OnClickListener(){
+                public void onClick(DialogInterface dialog, int which) {
+                    switch (which) {
+                        case OPEN_DIRECTORY:
+                            dialog.dismiss();
+                            mPath = path;
+                            mFocusFile = null;
+                            updateList();
+                            break;
+                        case RUN_TESTS:
+                            dialog.dismiss();
+                            processDirectory(path, false);
+                            break;
+                    }
+                }
+            });
+            builder.create().show();
         } else {
             processFile(path, false);
         }
     }
-    
+
+    /*
+     * This function is called when the user has selected a directory in the
+     * list and wants to perform an action on it instead of navigating into
+     * the directory.
+     */
+    abstract void processDirectory(String path, boolean selection);
     /*
      * This function is called when the user has selected a file in the
      * file list. The selected file could be a file or a directory.
      * The flag indicates if this was from a selection or not.
      */
     abstract void processFile(String filename, boolean selection);
-    
+
     /*
      * This function is called when the file list is being built. Return
      * true if the file is to be added to the file list.
      */
     abstract boolean fileFilter(File f);
-    
+
     protected void updateList() {
         setListAdapter(new SimpleAdapter(this,
                 getData(),
@@ -152,16 +178,19 @@
         setTitle(title);
         getListView().setSelection(mFocusIndex);
     }
-    
-    protected void setupPath() 
+
+    protected void setupPath()
     {
     	mPath = "/sdcard/android/layout_tests";
     	mBaseLength = mPath.length();
     }
-    
+
     protected String mPath;
     protected int mBaseLength;
     protected String mFocusFile;
     protected int mFocusIndex;
-  
+    
+    private final static int OPEN_DIRECTORY = 0;
+    private final static int RUN_TESTS = 1;
+
 }
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/FsUtils.java b/tests/DumpRenderTree/src/com/android/dumprendertree/FsUtils.java
new file mode 100644
index 0000000..cc2f1f5
--- /dev/null
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/FsUtils.java
@@ -0,0 +1,80 @@
+package com.android.dumprendertree;
+
+import android.util.Log;
+
+import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+
+public class FsUtils {
+
+    private static final String LOGTAG = "FsUtils";
+    private FsUtils() {
+        //no creation of instances
+    }
+
+    public static void findLayoutTestsRecursively(BufferedOutputStream bos,
+            String dir) throws IOException {
+        Log.v(LOGTAG, "Searching tests under " + dir);
+
+        File d = new File(dir);
+        if (!d.isDirectory()) {
+            throw new AssertionError("A directory expected, but got " + dir);
+        }
+
+        String[] files = d.list();
+        for (int i = 0; i < files.length; i++) {
+            String s = dir + "/" + files[i];
+            if (FileFilter.ignoreTest(s)) {
+                Log.v(LOGTAG, "  Ignoring: " + s);
+                continue;
+            }
+            if (s.toLowerCase().endsWith(".html")
+                    || s.toLowerCase().endsWith(".xml")) {
+                bos.write(s.getBytes());
+                bos.write('\n');
+                continue;
+            }
+
+            File f = new File(s);
+            if (f.isDirectory()) {
+                findLayoutTestsRecursively(bos, s);
+                continue;
+            }
+
+            Log.v(LOGTAG, "Skipping " + s);
+        }
+    }
+
+    public static void updateTestStatus(String statusFile, String s) {
+        try {
+            BufferedOutputStream bos = new BufferedOutputStream(
+                    new FileOutputStream(statusFile));
+            bos.write(s.getBytes());
+            bos.close();
+        } catch (Exception e) {
+            Log.e(LOGTAG, "Cannot update file " + statusFile);
+        }
+    }
+
+    public static String readTestStatus(String statusFile) {
+        // read out the test name it stopped last time.
+        String status = null;
+        File testStatusFile = new File(statusFile);
+        if(testStatusFile.exists()) {
+            try {
+                BufferedReader inReader = new BufferedReader(
+                        new FileReader(testStatusFile));
+                status = inReader.readLine();
+                inReader.close();
+            } catch (IOException e) {
+                Log.e(LOGTAG, "Error reading test status.", e);
+            }
+        }
+        return status;
+    }
+
+}
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java
index f169a26..a03490d 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java
@@ -178,15 +178,13 @@
     private void resumeTestList() {
         // read out the test name it stoped last time.
         try {
-            BufferedReader inReader = new BufferedReader(new FileReader(TEST_STATUS_FILE));
-            String line = inReader.readLine();
+            String line = FsUtils.readTestStatus(TEST_STATUS_FILE);
             for (int i = 0; i < mTestList.size(); i++) {
                 if (mTestList.elementAt(i).equals(line)) {
                     mTestList = new Vector<String>(mTestList.subList(i+1, mTestList.size()));
                     break;
                 }
             }
-            inReader.close();
         } catch (Exception e) {
             Log.e(LOGTAG, "Error reading " + TEST_STATUS_FILE);
         }
@@ -204,18 +202,7 @@
             Log.e(LOGTAG, "Fail to delete " + TEST_STATUS_FILE + " : " + e.getMessage());
         }
     }
-  
-    private void updateTestStatus(String s) {
-        // Write TEST_STATUS_FILE
-        try {
-            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(TEST_STATUS_FILE));
-            bos.write(s.getBytes());
-            bos.close();
-        } catch (Exception e) {
-            Log.e(LOGTAG, "Cannot update file " + TEST_STATUS_FILE);
-        }
-    }
-    
+
     private String getResultFile(String test) {
         String shortName = test.substring(0, test.lastIndexOf('.'));
         // Write actual results to result directory.
@@ -392,12 +379,12 @@
         // Run tests.
         for (int i = 0; i < mTestList.size(); i++) {
             String s = mTestList.elementAt(i);
-            updateTestStatus(s);
+            FsUtils.updateTestStatus(TEST_STATUS_FILE, s);
             // Run tests
             runTestAndWaitUntilDone(activity, s, runner.mTimeoutInMillis);
         }
 
-        updateTestStatus("#DONE");
+        FsUtils.updateTestStatus(TEST_STATUS_FILE, "#DONE");
         
         activity.finish();
     }
@@ -424,7 +411,7 @@
         try {
             File tests_list = new File(LAYOUT_TESTS_LIST_FILE);
             BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(tests_list, false));
-            findTestsRecursively(bos, getTestPath());
+            FsUtils.findLayoutTestsRecursively(bos, getTestPath());
             bos.flush();
             bos.close();
        } catch (Exception e) {
@@ -432,38 +419,6 @@
        }
     }
 
-    private void findTestsRecursively(BufferedOutputStream bos, String dir) throws IOException {
-         Log.v(LOGTAG, "Searching tests under " + dir);
-         
-         File d = new File(dir);
-         if (!d.isDirectory()) {
-             throw new AssertionError("A directory expected, but got " + dir);
-         }
-         
-         String[] files = d.list();
-         for (int i = 0; i < files.length; i++) {
-             String s = dir + "/" + files[i];
-             if (FileFilter.ignoreTest(s)) {
-                 Log.v(LOGTAG, "  Ignoring: " + s);
-                 continue;
-             }
-             if (s.toLowerCase().endsWith(".html") 
-                 || s.toLowerCase().endsWith(".xml")) {
-                 bos.write(s.getBytes());
-                 bos.write('\n');
-                 continue;
-             }
-             
-             File f = new File(s);
-             if (f.isDirectory()) {
-                 findTestsRecursively(bos, s);
-                 continue;
-             }
-             
-             Log.v(LOGTAG, "Skipping " + s);
-        }
-    }
-    
     // Running all the layout tests at once sometimes
     // causes the dumprendertree to run out of memory.
     // So, additional tests are added to run the tests
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/Menu.java b/tests/DumpRenderTree/src/com/android/dumprendertree/Menu.java
index 00e0f89..e15ab65 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/Menu.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/Menu.java
@@ -17,19 +17,23 @@
 package com.android.dumprendertree;
 
 import android.content.Intent;
-import android.net.Uri;
 import android.os.Bundle;
 import android.util.Log;
 
+import java.io.BufferedOutputStream;
 import java.io.File;
+import java.io.FileOutputStream;
 
 public class Menu extends FileList {
-    
-    public void onCreate(Bundle icicle) 
-    {
+
+    private static final int MENU_START = 0x01;
+    private static String LOGTAG = "MenuActivity";
+    static final String LAYOUT_TESTS_LIST_FILE = "/sdcard/android/layout_tests_list.txt";
+
+    public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
     }
-    
+
     boolean fileFilter(File f) {
     	if (f.getName().startsWith("."))
     		return false;
@@ -41,14 +45,36 @@
     		return true;
     	return false;
     }
-    
-    void processFile(String filename, boolean selection)
-    {        
+
+    void processFile(String filename, boolean selection) {
         Intent intent = new Intent(Intent.ACTION_VIEW);
         intent.setClass(this, TestShellActivity.class);
         intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
         intent.putExtra(TestShellActivity.TEST_URL, "file://" + filename);
         startActivity(intent);
     }
+
+    @Override
+    void processDirectory(String path, boolean selection) {
+        generateTestList(path);
+        Intent intent = new Intent(Intent.ACTION_VIEW);
+        intent.setClass(this, TestShellActivity.class);
+        intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
+        intent.putExtra(TestShellActivity.UI_AUTO_TEST, LAYOUT_TESTS_LIST_FILE);
+        startActivity(intent);
+    }
+
+    private void generateTestList(String path) {
+        try {
+            File tests_list = new File(LAYOUT_TESTS_LIST_FILE);
+            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(tests_list, false));
+            FsUtils.findLayoutTestsRecursively(bos, path);
+            bos.flush();
+            bos.close();
+       } catch (Exception e) {
+           Log.e(LOGTAG, "Error when creating test list: " + e.getMessage());
+       }
+    }
+
 }
 
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTest.java b/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTest.java
index 16973be..de39800 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTest.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTest.java
@@ -45,7 +45,7 @@
 
         //always try to resume first, hence cleaning up status will be the
         //responsibility of driver scripts
-        String lastUrl = readTestStatus();
+        String lastUrl = FsUtils.readTestStatus(TEST_STATUS_FILE);
         if(lastUrl != null && !TEST_DONE.equals(lastUrl))
             fastForward(listReader, lastUrl);
 
@@ -62,7 +62,7 @@
                 continue;
             start = System.currentTimeMillis();
             Log.v(LOGTAG, "Testing URL: " + url);
-            updateTestStatus(url);
+            FsUtils.updateTestStatus(TEST_STATUS_FILE, url);
             activity.reset();
             //use message to send new URL to avoid interacting with
             //WebView in non-UI thread
@@ -92,7 +92,7 @@
             System.gc();
             System.gc();
         }
-        updateTestStatus(TEST_DONE);
+        FsUtils.updateTestStatus(TEST_STATUS_FILE, TEST_DONE);
         activity.finish();
         listReader.close();
     }
@@ -122,35 +122,6 @@
         }
     }
 
-    private void updateTestStatus(String s) {
-        // write last tested url into status file
-        try {
-            BufferedOutputStream bos = new BufferedOutputStream(
-                    new FileOutputStream(TEST_STATUS_FILE));
-            bos.write(s.getBytes());
-            bos.close();
-        } catch (IOException e) {
-            Log.e(LOGTAG, "Cannot update file " + TEST_STATUS_FILE, e);
-        }
-    }
-
-    private String readTestStatus() {
-        // read out the test name it stopped last time.
-        String status = null;
-        File testStatusFile = new File(TEST_STATUS_FILE);
-        if(testStatusFile.exists()) {
-            try {
-                BufferedReader inReader = new BufferedReader(
-                        new FileReader(testStatusFile));
-                status = inReader.readLine();
-                inReader.close();
-            } catch (IOException e) {
-                Log.e(LOGTAG, "Error reading test status.", e);
-            }
-        }
-        return status;
-    }
-
     private void fastForward(BufferedReader testListReader, String lastUrl) {
         //fastforward the BufferedReader to the position right after last url
         if(lastUrl == null)
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
index 1ba291c..0d22eca 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
@@ -17,7 +17,10 @@
 package com.android.dumprendertree;
 
 import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
 import android.content.Intent;
+import android.content.DialogInterface.OnClickListener;
 import android.graphics.Bitmap;
 import android.net.http.SslError;
 import android.os.Bundle;
@@ -35,21 +38,24 @@
 import android.webkit.WebViewClient;
 import android.widget.LinearLayout;
 
+import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileOutputStream;
+import java.io.FileReader;
 import java.io.IOException;
 import java.util.Vector;
 
 public class TestShellActivity extends Activity implements LayoutTestController {
-    
+
     static enum DumpDataType {DUMP_AS_TEXT, EXT_REPR, NO_OP}
-    
+
     public class AsyncHandler extends Handler {
         @Override
         public void handleMessage(Message msg) {
             if (msg.what == MSG_TIMEOUT) {
                 mTimedOut = true;
-                mCallback.timedOut(mWebView.getUrl());
+                if(mCallback != null)
+                    mCallback.timedOut(mWebView.getUrl());
                 requestWebKitData();
                 return;
             } else if (msg.what == MSG_WEBKIT_DATA) {
@@ -63,10 +69,10 @@
 
     public void requestWebKitData() {
         Message callback = mHandler.obtainMessage(MSG_WEBKIT_DATA);
-        
+
         if (mRequestedWebKitData)
             throw new AssertionError("Requested webkit data twice: " + mWebView.getUrl());
-        
+
         mRequestedWebKitData = true;
         switch (mDumpDataType) {
             case DUMP_AS_TEXT:
@@ -79,12 +85,12 @@
                 finished();
                 break;
         }
-    } 
+    }
 
     @Override
     protected void onCreate(Bundle icicle) {
         super.onCreate(icicle);
-        
+
         LinearLayout contentView = new LinearLayout(this);
         contentView.setOrientation(LinearLayout.VERTICAL);
         setContentView(contentView);
@@ -133,59 +139,122 @@
         mWebView.addJavascriptInterface(mCallbackProxy, "layoutTestController");
         mWebView.addJavascriptInterface(mCallbackProxy, "eventSender");
         contentView.addView(mWebView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT, 0.0f));
- 
+
         mWebView.getSettings().setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL);
-            
+
         mHandler = new AsyncHandler();
-        
+
         Intent intent = getIntent();
         if (intent != null) {
             executeIntent(intent);
         }
     }
-    
+
     @Override
     protected void onNewIntent(Intent intent) {
         super.onNewIntent(intent);
         executeIntent(intent);
     }
-    
+
     private void executeIntent(Intent intent) {
         resetTestStatus();
         if (!Intent.ACTION_VIEW.equals(intent.getAction())) {
             return;
         }
-        
+
         mTestUrl = intent.getStringExtra(TEST_URL);
-        if (mTestUrl == null)
+        if (mTestUrl == null) {
+            mUiAutoTestPath = intent.getStringExtra(UI_AUTO_TEST);
+            if(mUiAutoTestPath != null) {
+                beginUiAutoTest();
+            }
             return;
-        
+        }
+
         mResultFile = intent.getStringExtra(RESULT_FILE);
         mTimeoutInMillis = intent.getIntExtra(TIMEOUT_IN_MILLIS, 0);
 
         Log.v(LOGTAG, "  Loading " + mTestUrl);
         mWebView.loadUrl(mTestUrl);
-            
+
         if (mTimeoutInMillis > 0) {
             // Create a timeout timer
             Message m = mHandler.obtainMessage(MSG_TIMEOUT);
             mHandler.sendMessageDelayed(m, mTimeoutInMillis);
         }
     }
-    
+
+    private void beginUiAutoTest() {
+        try {
+            mTestListReader = new BufferedReader(
+                    new FileReader(mUiAutoTestPath));
+        } catch (IOException ioe) {
+            Log.e(LOGTAG, "Failed to open test list for read.", ioe);
+            finishUiAutoTest();
+            return;
+        }
+        moveToNextTest();
+    }
+
+    private void finishUiAutoTest() {
+        try {
+            if(mTestListReader != null)
+                mTestListReader.close();
+        } catch (IOException ioe) {
+            Log.w(LOGTAG, "Failed to close test list file.", ioe);
+        }
+        finished();
+    }
+
+    private void moveToNextTest() {
+        String url = null;
+        try {
+            url = mTestListReader.readLine();
+        } catch (IOException ioe) {
+            Log.e(LOGTAG, "Failed to read next test.", ioe);
+            finishUiAutoTest();
+            return;
+        }
+        if (url == null) {
+            mUiAutoTestPath = null;
+            finishUiAutoTest();
+            AlertDialog.Builder builder = new AlertDialog.Builder(this);
+            builder.setMessage("All tests finished. Exit?")
+                   .setCancelable(false)
+                   .setPositiveButton("Yes", new OnClickListener(){
+                       public void onClick(DialogInterface dialog, int which) {
+                           TestShellActivity.this.finish();
+                       }
+                   })
+                   .setNegativeButton("No", new OnClickListener(){
+                       public void onClick(DialogInterface dialog, int which) {
+                           dialog.cancel();
+                       }
+                   });
+            builder.create().show();
+            return;
+        }
+        url = "file://" + url;
+        Intent intent = new Intent(Intent.ACTION_VIEW);
+        intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
+        intent.putExtra(TestShellActivity.TEST_URL, url);
+        intent.putExtra(TIMEOUT_IN_MILLIS, 10000);
+        executeIntent(intent);
+    }
+
     @Override
     protected void onStop() {
         super.onStop();
         mWebView.stopLoading();
     }
-    
+
     @Override
     protected void onDestroy() {
         super.onDestroy();
         mWebView.destroy();
         mWebView = null;
     }
-    
+
     @Override
     public void onLowMemory() {
         super.onLowMemory();
@@ -199,13 +268,13 @@
             finished();
             return;
         }
-        
+
         try {
             File parentDir = new File(mResultFile).getParentFile();
             if (!parentDir.exists()) {
                 parentDir.mkdirs();
             }
-            
+
             FileOutputStream os = new FileOutputStream(mResultFile);
             if (timeout) {
                 Log.w("Layout test: Timeout", mResultFile);
@@ -222,22 +291,27 @@
             os.flush();
             os.close();
         } catch (IOException ex) {
-            Log.e(LOGTAG, "Cannot write to " + mResultFile + ", " + ex.getMessage());          
+            Log.e(LOGTAG, "Cannot write to " + mResultFile + ", " + ex.getMessage());
         }
 
         finished();
     }
-    
+
     public void setCallback(TestShellCallback callback) {
         mCallback = callback;
     }
-    
+
     public void finished() {
-        if (mCallback != null) {
-            mCallback.finished();
+        if (mUiAutoTestPath != null) {
+            //don't really finish here
+            moveToNextTest();
+        } else {
+            if (mCallback != null) {
+                mCallback.finished();
+            }
         }
     }
-   
+
     public void setDefaultDumpDataType(DumpDataType defaultDumpDataType) {
         mDefaultDumpDataType = defaultDumpDataType;
     }
@@ -257,7 +331,7 @@
         String url = mWebView.getUrl();
         Log.v(LOGTAG, "waitUntilDone called: " + url);
     }
-    
+
     public void notifyDone() {
         String url = mWebView.getUrl();
         Log.v(LOGTAG, "notifyDone called: " + url);
@@ -266,7 +340,7 @@
             mChromeClient.onProgressChanged(mWebView, 100);
         }
     }
-    
+
     public void display() {
         mWebView.invalidate();
     }
@@ -332,7 +406,7 @@
     }
 
     public void queueScript(String scriptToRunInCurrentContext) {
-        mWebView.loadUrl("javascript:"+scriptToRunInCurrentContext);     
+        mWebView.loadUrl("javascript:"+scriptToRunInCurrentContext);
     }
 
     public void repaintSweepHorizontally() {
@@ -359,7 +433,7 @@
     public void testRepaint() {
         mWebView.invalidate();
     }
-    
+
     private final WebChromeClient mChromeClient = new WebChromeClient() {
         @Override
         public void onProgressChanged(WebView view, int newProgress) {
@@ -406,7 +480,7 @@
             result.confirm();
             return true;
         }
-        
+
         @Override
         public boolean onJsConfirm(WebView view, String url, String message,
                 JsResult result) {
@@ -419,7 +493,7 @@
             result.confirm();
             return true;
         }
-        
+
         @Override
         public boolean onJsPrompt(WebView view, String url, String message,
                 String defaultValue, JsPromptResult result) {
@@ -435,7 +509,7 @@
             return true;
         }
     };
-    
+
     private void resetTestStatus() {
         mWaitUntilDone = false;
         mDumpDataType = mDefaultDumpDataType;
@@ -444,17 +518,19 @@
         mRequestedWebKitData = false;
         mEventSender.resetMouse();
     }
-    
+
     private WebView mWebView;
     private WebViewEventSender mEventSender;
     private AsyncHandler mHandler;
     private TestShellCallback mCallback;
 
     private CallbackProxy mCallbackProxy;
-        
+
     private String mTestUrl;
     private String mResultFile;
     private int mTimeoutInMillis;
+    private String mUiAutoTestPath;
+    private BufferedReader mTestListReader;
 
     // States
     private boolean mTimedOut;
@@ -472,13 +548,14 @@
     private Vector mWebHistory;
 
     static final String TIMEOUT_STR = "**Test timeout";
-    
+
     static final int MSG_TIMEOUT = 0;
     static final int MSG_WEBKIT_DATA = 1;
 
     static final String LOGTAG="TestShell";
-    
+
     static final String TEST_URL = "TestUrl";
     static final String RESULT_FILE = "ResultFile";
     static final String TIMEOUT_IN_MILLIS = "TimeoutInMillis";
+    static final String UI_AUTO_TEST = "UiAutoTest";
 }
diff --git a/tests/backup/src/com/android/backuptest/BackupTestAgent.java b/tests/backup/src/com/android/backuptest/BackupTestAgent.java
index c6acc66..8e4fd39 100644
--- a/tests/backup/src/com/android/backuptest/BackupTestAgent.java
+++ b/tests/backup/src/com/android/backuptest/BackupTestAgent.java
@@ -18,11 +18,15 @@
 
 import android.backup.BackupHelperAgent;
 import android.backup.FileBackupHelper;
+import android.backup.SharedPreferencesBackupHelper;
 
 public class BackupTestAgent extends BackupHelperAgent
 {
     public void onCreate() {
         addHelper("data_files", new FileBackupHelper(this, BackupTestActivity.FILE_NAME));
+        addHelper("more_data_files", new FileBackupHelper(this, "another_file.txt", "3.txt",
+                    "empty.txt"));
+        addHelper("shared_prefs", new SharedPreferencesBackupHelper(this, "settings", "raw"));
     }
 }
 
diff --git a/tests/backup/test_backup.sh b/tests/backup/test_backup.sh
new file mode 100755
index 0000000..dbf9ed2
--- /dev/null
+++ b/tests/backup/test_backup.sh
@@ -0,0 +1,27 @@
+#!/bin/bash
+
+#adb kill-server
+
+# set the transport
+adb shell bmgr transport 1
+
+# load up the three files
+adb shell "rm /data/data/com.android.backuptest/files/* ; \
+           mkdir /data/data/com.android.backuptest ; \
+           mkdir /data/data/com.android.backuptest/files ; \
+           mkdir /data/data/com.android.backuptest/shared_prefs ; \
+           echo -n \"<map><int name=\\\"pref\\\" value=\\\"1\\\" /></map>\" > /data/data/com.android.backuptest/shared_prefs/raw.xml ; \
+           echo -n first file > /data/data/com.android.backuptest/files/file.txt ; \
+           echo -n asdf > /data/data/com.android.backuptest/files/another_file.txt ; \
+           echo -n 3 > /data/data/com.android.backuptest/files/3.txt ; \
+           echo -n "" > /data/data/com.android.backuptest/files/empty.txt ; \
+"
+
+# say that the data has changed
+adb shell bmgr backup com.android.backuptest
+
+# run the backup
+adb shell bmgr run
+
+
+
diff --git a/tests/backup/test_restore.sh b/tests/backup/test_restore.sh
new file mode 100755
index 0000000..ccf29cf
--- /dev/null
+++ b/tests/backup/test_restore.sh
@@ -0,0 +1,53 @@
+#!/bin/bash
+
+function check_file
+{
+    data=$(adb shell cat /data/data/com.android.backuptest/$1)
+    if [ "$data" = "$2" ] ; then
+        echo "$1 has correct value [$2]"
+    else
+        echo $1 is INCORRECT
+        echo "   value:    [$data]"
+        echo "   expected: [$2]"
+    fi
+}
+
+# delete the old data
+echo --- Previous files
+adb shell "ls -l /data/data/com.android.backuptest/files"
+adb shell "rm /data/data/com.android.backuptest/files/*"
+echo --- Previous shared_prefs
+adb shell "ls -l /data/data/com.android.backuptest/shared_prefs"
+adb shell "rm /data/data/com.android.backuptest/shared_prefs/*"
+echo --- Erased files and shared_prefs
+adb shell "ls -l /data/data/com.android.backuptest/files"
+adb shell "ls -l /data/data/com.android.backuptest/shared_prefs"
+echo ---
+
+echo
+echo
+echo
+
+# run the restore
+adb shell bmgr restore 0
+
+echo
+echo
+echo
+
+# check the results
+check_file files/file.txt "first file"
+check_file files/another_file.txt "asdf"
+check_file files/3.txt "3"
+check_file files/empty.txt ""
+check_file shared_prefs/raw.xml '<map><int name="pref" value="1" /></map>'
+
+echo
+echo
+echo
+echo --- Restored files
+adb shell "ls -l /data/data/com.android.backuptest/files"
+echo --- Restored shared_prefs
+adb shell "ls -l /data/data/com.android.backuptest/shared_prefs"
+echo ---
+echo
diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp
index 6bc1ee6..67af116 100644
--- a/tools/aapt/AaptAssets.cpp
+++ b/tools/aapt/AaptAssets.cpp
@@ -187,6 +187,13 @@
         return 0;
     }
 
+    // screen layout
+    if (getScreenLayoutName(part.string(), &config)) {
+        *axis = AXIS_SCREENLAYOUT;
+        *value = config.screenLayout;
+        return 0;
+    }
+
     // version
     if (getVersionName(part.string(), &config)) {
         *axis = AXIS_VERSION;
@@ -202,7 +209,7 @@
 {
     Vector<String8> parts;
 
-    String8 mcc, mnc, loc, orient, den, touch, key, keysHidden, nav, size, vers;
+    String8 mcc, mnc, loc, orient, den, touch, key, keysHidden, nav, size, layout, vers;
 
     const char *p = dir;
     const char *q;
@@ -378,6 +385,18 @@
         //printf("not screen size: %s\n", part.string());
     }
 
+    if (getScreenLayoutName(part.string())) {
+        layout = part;
+
+        index++;
+        if (index == N) {
+            goto success;
+        }
+        part = parts[index];
+    } else {
+        //printf("not screen layout: %s\n", part.string());
+    }
+
     if (getVersionName(part.string())) {
         vers = part;
 
@@ -404,6 +423,7 @@
     this->keyboard = key;
     this->navigation = nav;
     this->screenSize = size;
+    this->screenLayout = layout;
     this->version = vers;
 
     // what is this anyway?
@@ -435,6 +455,8 @@
     s += ",";
     s += screenSize;
     s += ",";
+    s += screenLayout;
+    s += ",";
     s += version;
     return s;
 }
@@ -483,6 +505,10 @@
         s += "-";
         s += screenSize;
     }
+    if (this->screenLayout != "") {
+        s += "-";
+        s += screenLayout;
+    }
     if (this->version != "") {
         s += "-";
         s += version;
@@ -786,6 +812,26 @@
     return true;
 }
 
+bool AaptGroupEntry::getScreenLayoutName(const char* name,
+                                     ResTable_config* out)
+{
+    if (strcmp(name, kWildcardName) == 0) {
+        if (out) out->screenLayout = out->SCREENLAYOUT_ANY;
+        return true;
+    } else if (strcmp(name, "smallscreen") == 0) {
+        if (out) out->screenLayout = out->SCREENLAYOUT_SMALL;
+        return true;
+    } else if (strcmp(name, "normalscreen") == 0) {
+        if (out) out->screenLayout = out->SCREENLAYOUT_NORMAL;
+        return true;
+    } else if (strcmp(name, "largescreen") == 0) {
+        if (out) out->screenLayout = out->SCREENLAYOUT_LARGE;
+        return true;
+    }
+
+    return false;
+}
+
 bool AaptGroupEntry::getVersionName(const char* name,
                                     ResTable_config* out)
 {
@@ -828,6 +874,7 @@
     if (v == 0) v = keyboard.compare(o.keyboard);
     if (v == 0) v = navigation.compare(o.navigation);
     if (v == 0) v = screenSize.compare(o.screenSize);
+    if (v == 0) v = screenLayout.compare(o.screenLayout);
     if (v == 0) v = version.compare(o.version);
     return v;
 }
@@ -846,6 +893,7 @@
     getKeyboardName(keyboard.string(), &params);
     getNavigationName(navigation.string(), &params);
     getScreenSizeName(screenSize.string(), &params);
+    getScreenLayoutName(screenLayout.string(), &params);
     getVersionName(version.string(), &params);
     return params;
 }
diff --git a/tools/aapt/AaptAssets.h b/tools/aapt/AaptAssets.h
index 01c8140..3b96412 100644
--- a/tools/aapt/AaptAssets.h
+++ b/tools/aapt/AaptAssets.h
@@ -37,6 +37,7 @@
     AXIS_KEYBOARD,
     AXIS_NAVIGATION,
     AXIS_SCREENSIZE,
+    AXIS_SCREENLAYOUT,
     AXIS_VERSION
 };
 
@@ -62,6 +63,7 @@
     String8 keyboard;
     String8 navigation;
     String8 screenSize;
+    String8 screenLayout;
     String8 version;
 
     bool initFromDirName(const char* dir, String8* resType);
@@ -78,6 +80,7 @@
     static bool getKeyboardName(const char* name, ResTable_config* out = NULL);
     static bool getNavigationName(const char* name, ResTable_config* out = NULL);
     static bool getScreenSizeName(const char* name, ResTable_config* out = NULL);
+    static bool getScreenLayoutName(const char* name, ResTable_config* out = NULL);
     static bool getVersionName(const char* name, ResTable_config* out = NULL);
 
     int compare(const AaptGroupEntry& o) const;
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index 503f661..e04491d 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -329,6 +329,9 @@
     TARGET_SDK_VERSION_ATTR = 0x01010270,
     TEST_ONLY_ATTR = 0x01010272,
     DENSITY_ATTR = 0x0101026c,
+    SMALL_SCREEN_ATTR = 0x01010284,
+    NORMAL_SCREEN_ATTR = 0x01010285,
+    LARGE_SCREEN_ATTR = 0x01010286,
 };
 
 const char *getComponentName(String8 &pkgName, String8 &componentName) {
@@ -499,6 +502,10 @@
             bool isLauncherActivity = false;
             bool withinApplication = false;
             bool withinReceiver = false;
+            int targetSdk = 0;
+            int smallScreen = 1;
+            int normalScreen = 1;
+            int largeScreen = 1;
             String8 pkg;
             String8 activityName;
             String8 activityLabel;
@@ -572,8 +579,10 @@
                                         error.string());
                                 goto bail;
                             }
+                            if (name == "Donut") targetSdk = 4;
                             printf("sdkVersion:'%s'\n", name.string());
                         } else if (code != -1) {
+                            targetSdk = code;
                             printf("sdkVersion:'%d'\n", code);
                         }
                         code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
@@ -585,8 +594,12 @@
                                         error.string());
                                 goto bail;
                             }
+                            if (name == "Donut" && targetSdk < 4) targetSdk = 4;
                             printf("targetSdkVersion:'%s'\n", name.string());
                         } else if (code != -1) {
+                            if (targetSdk < code) {
+                                targetSdk = code;
+                            }
                             printf("targetSdkVersion:'%d'\n", code);
                         }
                     } else if (tag == "uses-configuration") {
@@ -625,6 +638,13 @@
                             goto bail;
                         }
                         printf("supports-density:'%d'\n", dens);
+                    } else if (tag == "supports-screens") {
+                        smallScreen = getIntegerAttribute(tree,
+                                SMALL_SCREEN_ATTR, NULL, 1);
+                        normalScreen = getIntegerAttribute(tree,
+                                NORMAL_SCREEN_ATTR, NULL, 1);
+                        largeScreen = getIntegerAttribute(tree,
+                                LARGE_SCREEN_ATTR, NULL, 1);
                     }
                 } else if (depth == 3 && withinApplication) {
                     withinActivity = false;
@@ -733,6 +753,25 @@
                 }
             }
             
+            // Determine default values for any unspecified screen sizes,
+            // based on the target SDK of the package.  As of 4 (donut)
+            // the screen size support was introduced, so all default to
+            // enabled.
+            if (smallScreen > 0) {
+                smallScreen = targetSdk >= 4 ? -1 : 0;
+            }
+            if (normalScreen > 0) {
+                normalScreen = -1;
+            }
+            if (largeScreen > 0) {
+                largeScreen = targetSdk >= 4 ? -1 : 0;
+            }
+            printf("supports-screens:");
+            if (smallScreen != 0) printf(" 'small'");
+            if (normalScreen != 0) printf(" 'normal'");
+            if (largeScreen != 0) printf(" 'large'");
+            printf("\n");
+            
             printf("locales:");
             Vector<String8> locales;
             res.getLocales(&locales);
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeAssetManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeAssetManager.java
index 1fa11af..06dd96f 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeAssetManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeAssetManager.java
@@ -59,7 +59,7 @@
     public void setConfiguration(int mcc, int mnc, String locale,
             int orientation, int touchscreen, int density, int keyboard,
             int keyboardHidden, int navigation, int screenWidth, int screenHeight,
-            int version)  {
+            int screenLayout, int version)  {
         
         Configuration c = new Configuration();
         c.mcc = mcc;
@@ -70,5 +70,6 @@
         c.keyboardHidden = keyboardHidden;
         c.navigation = navigation;
         c.orientation = orientation;
+        c.screenLayout = screenLayout;
     }
 }
diff --git a/vpn/java/android/net/vpn/IVpnService.aidl b/vpn/java/android/net/vpn/IVpnService.aidl
index 0e658df..fedccb0 100644
--- a/vpn/java/android/net/vpn/IVpnService.aidl
+++ b/vpn/java/android/net/vpn/IVpnService.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007, The Android Open Source Project
+ * 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.
diff --git a/vpn/java/android/net/vpn/L2tpIpsecProfile.java b/vpn/java/android/net/vpn/L2tpIpsecProfile.java
index 181619d..4ae2dec 100644
--- a/vpn/java/android/net/vpn/L2tpIpsecProfile.java
+++ b/vpn/java/android/net/vpn/L2tpIpsecProfile.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007, The Android Open Source Project
+ * 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.
@@ -19,15 +19,14 @@
 import android.os.Parcel;
 
 /**
- * The profile for L2TP-over-IPSec type of VPN.
+ * The profile for certificate-based L2TP-over-IPSec type of VPN.
  * {@hide}
  */
-public class L2tpIpsecProfile extends VpnProfile {
+public class L2tpIpsecProfile extends L2tpProfile {
     private static final long serialVersionUID = 1L;
 
     private String mUserCertificate;
     private String mCaCertificate;
-    private String mUserkey;
 
     @Override
     public VpnType getType() {
@@ -50,20 +49,11 @@
         return mUserCertificate;
     }
 
-    public void setUserkey(String name) {
-        mUserkey = name;
-    }
-
-    public String getUserkey() {
-        return mUserkey;
-    }
-
     @Override
     protected void readFromParcel(Parcel in) {
         super.readFromParcel(in);
         mCaCertificate = in.readString();
         mUserCertificate = in.readString();
-        mUserkey = in.readString();
     }
 
     @Override
@@ -71,6 +61,5 @@
         super.writeToParcel(parcel, flags);
         parcel.writeString(mCaCertificate);
         parcel.writeString(mUserCertificate);
-        parcel.writeString(mUserkey);
     }
 }
diff --git a/vpn/java/android/net/vpn/L2tpIpsecPskProfile.java b/vpn/java/android/net/vpn/L2tpIpsecPskProfile.java
new file mode 100644
index 0000000..7a03018
--- /dev/null
+++ b/vpn/java/android/net/vpn/L2tpIpsecPskProfile.java
@@ -0,0 +1,54 @@
+/*
+ * 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 android.net.vpn;
+
+import android.os.Parcel;
+
+/**
+ * The profile for pre-shared-key-based L2TP-over-IPSec type of VPN.
+ * {@hide}
+ */
+public class L2tpIpsecPskProfile extends L2tpProfile {
+    private static final long serialVersionUID = 1L;
+
+    private String mPresharedKey;
+
+    @Override
+    public VpnType getType() {
+        return VpnType.L2TP_IPSEC_PSK;
+    }
+
+    public void setPresharedKey(String key) {
+        mPresharedKey = key;
+    }
+
+    public String getPresharedKey() {
+        return mPresharedKey;
+    }
+
+    @Override
+    protected void readFromParcel(Parcel in) {
+        super.readFromParcel(in);
+        mPresharedKey = in.readString();
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        super.writeToParcel(parcel, flags);
+        parcel.writeString(mPresharedKey);
+    }
+}
diff --git a/vpn/java/android/net/vpn/L2tpProfile.java b/vpn/java/android/net/vpn/L2tpProfile.java
index 59d4981..dbba0c5 100644
--- a/vpn/java/android/net/vpn/L2tpProfile.java
+++ b/vpn/java/android/net/vpn/L2tpProfile.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007, The Android Open Source Project
+ * 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.
@@ -16,6 +16,8 @@
 
 package android.net.vpn;
 
+import android.os.Parcel;
+
 /**
  * The profile for L2TP type of VPN.
  * {@hide}
@@ -23,8 +25,44 @@
 public class L2tpProfile extends VpnProfile {
     private static final long serialVersionUID = 1L;
 
+    private boolean mSecret;
+    private String mSecretString;
+
     @Override
     public VpnType getType() {
         return VpnType.L2TP;
     }
+
+    /**
+     * Enables/disables the secret for authenticating tunnel connection.
+     */
+    public void setSecretEnabled(boolean enabled) {
+        mSecret = enabled;
+    }
+
+    public boolean isSecretEnabled() {
+        return mSecret;
+    }
+
+    public void setSecretString(String secret) {
+        mSecretString = secret;
+    }
+
+    public String getSecretString() {
+        return mSecretString;
+    }
+
+    @Override
+    protected void readFromParcel(Parcel in) {
+        super.readFromParcel(in);
+        mSecret = in.readInt() > 0;
+        mSecretString = in.readString();
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        super.writeToParcel(parcel, flags);
+        parcel.writeInt(mSecret ? 1 : 0);
+        parcel.writeString(mSecretString);
+    }
 }
diff --git a/vpn/java/android/net/vpn/PptpProfile.java b/vpn/java/android/net/vpn/PptpProfile.java
new file mode 100644
index 0000000..c68bb71
--- /dev/null
+++ b/vpn/java/android/net/vpn/PptpProfile.java
@@ -0,0 +1,30 @@
+/*
+ * 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 android.net.vpn;
+
+/**
+ * The profile for PPTP type of VPN.
+ * {@hide}
+ */
+public class PptpProfile extends VpnProfile {
+    private static final long serialVersionUID = 1L;
+
+    @Override
+    public VpnType getType() {
+        return VpnType.PPTP;
+    }
+}
diff --git a/vpn/java/android/net/vpn/VpnManager.java b/vpn/java/android/net/vpn/VpnManager.java
index 98795bd..dc70b26 100644
--- a/vpn/java/android/net/vpn/VpnManager.java
+++ b/vpn/java/android/net/vpn/VpnManager.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007, The Android Open Source Project
+ * 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.
diff --git a/vpn/java/android/net/vpn/VpnProfile.aidl b/vpn/java/android/net/vpn/VpnProfile.aidl
index ad34bfc..edeaef0 100644
--- a/vpn/java/android/net/vpn/VpnProfile.aidl
+++ b/vpn/java/android/net/vpn/VpnProfile.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007, The Android Open Source Project
+ * 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.
diff --git a/vpn/java/android/net/vpn/VpnProfile.java b/vpn/java/android/net/vpn/VpnProfile.java
index 9e24da4..bd6c809 100644
--- a/vpn/java/android/net/vpn/VpnProfile.java
+++ b/vpn/java/android/net/vpn/VpnProfile.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007, The Android Open Source Project
+ * 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.
diff --git a/vpn/java/android/net/vpn/VpnState.java b/vpn/java/android/net/vpn/VpnState.java
index 977d938..ebd9364 100644
--- a/vpn/java/android/net/vpn/VpnState.java
+++ b/vpn/java/android/net/vpn/VpnState.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007, The Android Open Source Project
+ * 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.
diff --git a/vpn/java/android/net/vpn/VpnType.java b/vpn/java/android/net/vpn/VpnType.java
index 91b0ea2..c7df943 100644
--- a/vpn/java/android/net/vpn/VpnType.java
+++ b/vpn/java/android/net/vpn/VpnType.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007, The Android Open Source Project
+ * 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.
@@ -21,14 +21,21 @@
  * {@hide}
  */
 public enum VpnType {
-    L2TP_IPSEC("L2TP/IPSec", L2tpIpsecProfile.class),
-    L2TP("L2TP", L2tpProfile.class);
+    PPTP("PPTP", "", PptpProfile.class),
+    L2TP("L2TP", "", L2tpProfile.class),
+    L2TP_IPSEC_PSK("L2TP/IPSec PSK", "Pre-shared key based L2TP/IPSec VPN",
+            L2tpIpsecPskProfile.class),
+    L2TP_IPSEC("L2TP/IPSec CRT", "Certificate based L2TP/IPSec VPN",
+            L2tpIpsecProfile.class);
 
     private String mDisplayName;
+    private String mDescription;
     private Class<? extends VpnProfile> mClass;
 
-    VpnType(String displayName, Class<? extends VpnProfile> klass) {
+    VpnType(String displayName, String description,
+            Class<? extends VpnProfile> klass) {
         mDisplayName = displayName;
+        mDescription = description;
         mClass = klass;
     }
 
@@ -36,6 +43,10 @@
         return mDisplayName;
     }
 
+    public String getDescription() {
+        return mDescription;
+    }
+
     public Class<? extends VpnProfile> getProfileClass() {
         return mClass;
     }