Merge change 5600

* changes:
  Rework the property parsing code.
diff --git a/Android.mk b/Android.mk
index 58b149e..01aef7a 100644
--- a/Android.mk
+++ b/Android.mk
@@ -85,6 +85,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 8b5f7ed..1e79de5 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -17313,6 +17313,17 @@
  visibility="public"
 >
 </field>
+<field name="ERROR_CODE_BAD_REQUEST"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="8"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="ERROR_CODE_CANCELED"
  type="int"
  transient="false"
@@ -24301,6 +24312,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"
@@ -24409,6 +24433,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"
@@ -184646,7 +184680,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 2f4a1c9..d8ca525 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);
@@ -661,7 +661,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 +1104,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..8b8b54c 100644
--- a/camera/libcameraservice/CameraService.h
+++ b/camera/libcameraservice/CameraService.h
@@ -199,7 +199,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..01083f1 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;
@@ -180,6 +181,25 @@
         }
     }
 
+    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;
         try {
@@ -189,6 +209,8 @@
             return;
         }
 
+        RestoreObserver observer = new RestoreObserver();
+
         try {
             int curTransport = mBmgr.getCurrentTransport();
             mRestore = mBmgr.beginRestoreSession(curTransport);
@@ -200,7 +222,7 @@
             for (RestoreSet s : sets) {
                 if (s.token == token) {
                     System.out.println("Scheduling restore: " + s.name);
-                    mRestore.performRestore(token);
+                    mRestore.performRestore(token, observer);
                     break;
                 }
             }
@@ -209,6 +231,17 @@
             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() {
@@ -229,4 +262,4 @@
         System.err.println("       bmgr restore token#");
         System.err.println("       bmgr run");
     }
-}
\ No newline at end of file
+}
diff --git a/core/java/android/accounts/Constants.java b/core/java/android/accounts/Constants.java
index b383c61..fde920e 100644
--- a/core/java/android/accounts/Constants.java
+++ b/core/java/android/accounts/Constants.java
@@ -25,6 +25,7 @@
     public static final int ERROR_CODE_INVALID_RESPONSE = 5;
     public static final int ERROR_CODE_UNSUPPORTED_OPERATION = 6;
     public static final int ERROR_CODE_BAD_ARGUMENTS = 7;
+    public static final int ERROR_CODE_BAD_REQUEST = 8;
 
     public static final String ACCOUNTS_KEY = "accounts";
     public static final String AUTHENTICATOR_TYPES_KEY = "authenticator_types";
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/backup/AbsoluteFileBackupHelper.java b/core/java/android/backup/AbsoluteFileBackupHelper.java
new file mode 100644
index 0000000..ab24675
--- /dev/null
+++ b/core/java/android/backup/AbsoluteFileBackupHelper.java
@@ -0,0 +1,66 @@
+/*
+ * 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;
+
+import android.content.Context;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileDescriptor;
+
+/**
+ * Like FileBackupHelper, but takes absolute paths for the files instead of
+ * subpaths of getFilesDir()
+ *
+ * @hide
+ */
+public class AbsoluteFileBackupHelper extends FileBackupHelperBase implements BackupHelper {
+    private static final String TAG = "AbsoluteFileBackupHelper";
+
+    Context mContext;
+    String[] mFiles;
+
+    public AbsoluteFileBackupHelper(Context context, String... files) {
+        super(context);
+
+        mContext = context;
+        mFiles = files;
+    }
+
+    /**
+     * Based on oldState, determine which of the files from the application's data directory
+     * need to be backed up, write them to the data stream, and fill in newState with the
+     * state as it exists now.
+     */
+    public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
+            ParcelFileDescriptor newState) {
+        // use the file paths as the keys, too
+        performBackup_checked(oldState, data, newState, mFiles, mFiles);
+    }
+
+    public void restoreEntity(BackupDataInputStream data) {
+        // TODO: turn this off before ship
+        Log.d(TAG, "got entity '" + data.getKey() + "' size=" + data.size());
+        String key = data.getKey();
+        if (isKeyInList(key, mFiles)) {
+            File f = new File(key);
+            writeFile(f, data);
+        }
+    }
+}
+
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/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..ac01c2d 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(int token, IRestoreObserver observer);
 
     /**
      * End this restore session.  After this method is called, the IRestoreSession binder
diff --git a/core/java/android/backup/RestoreSet.java b/core/java/android/backup/RestoreSet.java
index 96a99ae..eeca148 100644
--- a/core/java/android/backup/RestoreSet.java
+++ b/core/java/android/backup/RestoreSet.java
@@ -43,14 +43,14 @@
      * transport.  This is guaranteed to be valid for the duration of a restore
      * session, but is meaningless once the session has ended.
      */
-    public int token;
+    public long token;
 
 
     public RestoreSet() {
         // Leave everything zero / null
     }
 
-    public RestoreSet(String _name, String _dev, int _token) {
+    public RestoreSet(String _name, String _dev, long _token) {
         name = _name;
         device = _dev;
         token = _token;
@@ -65,7 +65,7 @@
     public void writeToParcel(Parcel out, int flags) {
         out.writeString(name);
         out.writeString(device);
-        out.writeInt(token);
+        out.writeLong(token);
     }
 
     public static final Parcelable.Creator<RestoreSet> CREATOR
@@ -82,6 +82,6 @@
     private RestoreSet(Parcel in) {
         name = in.readString();
         device = in.readString();
-        token = in.readInt();
+        token = in.readLong();
     }
-}
\ No newline at end of file
+}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 33e769e..87f762d 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1593,6 +1593,15 @@
     public static final String ACTION_REMOTE_INTENT =
             "android.intent.action.REMOTE_INTENT";
 
+    /**
+     * @hide
+     * TODO: This will be unhidden in a later CL.
+     * Broadcast Action: The TextToSpeech synthesizer has completed processing 
+     * all of the text in the speech queue.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_TTS_QUEUE_PROCESSING_COMPLETED =
+            "android.intent.action.TTS_QUEUE_PROCESSING_COMPLETED";
 
     // ---------------------------------------------------------------------
     // ---------------------------------------------------------------------
diff --git a/core/java/android/content/res/CompatibilityInfo.java b/core/java/android/content/res/CompatibilityInfo.java
index 680fef8..179b9bd 100644
--- a/core/java/android/content/res/CompatibilityInfo.java
+++ b/core/java/android/content/res/CompatibilityInfo.java
@@ -17,8 +17,15 @@
 package android.content.res;
 
 import android.content.pm.ApplicationInfo;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.Region;
 import android.util.DisplayMetrics;
+import android.util.Log;
 import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams;
 
 /**
  * CompatibilityInfo class keeps the information about compatibility mode that the application is
@@ -27,6 +34,9 @@
  *  {@hide} 
  */
 public class CompatibilityInfo {
+    private static final boolean DBG = false;
+    private static final String TAG = "CompatibilityInfo";
+    
     /** default compatibility info object for compatible applications */
     public static final CompatibilityInfo DEFAULT_COMPATIBILITY_INFO = new CompatibilityInfo(); 
 
@@ -41,36 +51,75 @@
     public static final int DEFAULT_PORTRAIT_HEIGHT = 480;
 
     /**
+     * The x-shift mode that controls the position of the content or the window under
+     * compatibility mode.
+     * {@see getTranslator}
+     * {@see Translator#mShiftMode}
+     */
+    private static final int X_SHIFT_NONE = 0;
+    private static final int X_SHIFT_CONTENT = 1;
+    private static final int X_SHIFT_AND_CLIP_CONTENT = 2;
+    private static final int X_SHIFT_WINDOW = 3;
+
+
+    /**
+     *  A compatibility flags
+     */
+    private int compatibilityFlags;
+    
+    /**
+     * A flag mask to tell if the application needs scaling (when mApplicationScale != 1.0f)
+     * {@see compatibilityFlag}
+     */
+    private static final int SCALING_REQUIRED = 1; 
+
+    /**
+     * A flag mask to indicates that the application can expand over the original size.
+     * The flag is set to true if
+     * 1) Application declares its expandable in manifest file using <expandable /> or
+     * 2) The screen size is same as (320 x 480) * density. 
+     * {@see compatibilityFlag}
+     */
+    private static final int EXPANDABLE = 2;
+    
+    /**
+     * A flag mask to tell if the application is configured to be expandable. This differs
+     * from EXPANDABLE in that the application that is not expandable will be 
+     * marked as expandable if it runs in (320x 480) * density screen size.
+     */
+    private static final int CONFIGURED_EXPANDABLE = 4; 
+
+    private static final int SCALING_EXPANDABLE_MASK = SCALING_REQUIRED | EXPANDABLE;
+
+    /**
      * Application's scale.
      */
-    public final float mApplicationScale;
+    public final float applicationScale;
 
     /**
      * Application's inverted scale.
      */
-    public final float mApplicationInvertedScale;
-    
-    /**
-     * A boolean flag to indicates that the application can expand over the original size.
-     * The flag is set to true if
-     * 1) Application declares its expandable in manifest file using <expandable /> or
-     * 2) The screen size is same as (320 x 480) * density. 
-     */
-    public boolean mExpandable;
+    public final float applicationInvertedScale;
+
 
     /**
-     * A expandable flag in the configuration.
+     * Window size in Compatibility Mode, in real pixels. This is updated by
+     * {@link DisplayMetrics#updateMetrics}.
      */
-    public final boolean mConfiguredExpandable;
+    private int mWidth;
+    private int mHeight;
     
     /**
-     * A boolean flag to tell if the application needs scaling (when mApplicationScale != 1.0f)
+     * The x offset to center the window content. In X_SHIFT_WINDOW mode, the offset is added
+     * to the window's layout. In X_SHIFT_CONTENT/X_SHIFT_AND_CLIP_CONTENT mode, the offset
+     * is used to translate the Canvas.
      */
-    public final boolean mScalingRequired;
+    private int mXOffset;
 
     public CompatibilityInfo(ApplicationInfo appInfo) {
-        mExpandable = mConfiguredExpandable =
-            (appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) != 0;
+        if ((appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) != 0) {
+            compatibilityFlags = EXPANDABLE | CONFIGURED_EXPANDABLE;
+        }
         
         float packageDensityScale = -1.0f;
         if (appInfo.supportsDensities != null) {
@@ -93,23 +142,323 @@
             }
         }
         if (packageDensityScale > 0.0f) {
-            mApplicationScale = packageDensityScale;
+            applicationScale = packageDensityScale;
         } else {
-            mApplicationScale = DisplayMetrics.DEVICE_DENSITY / (float) DisplayMetrics.DEFAULT_DENSITY;
+            applicationScale =
+                    DisplayMetrics.DEVICE_DENSITY / (float) DisplayMetrics.DEFAULT_DENSITY;
         }
-        mApplicationInvertedScale = 1.0f / mApplicationScale;
-        mScalingRequired = mApplicationScale != 1.0f;
+        applicationInvertedScale = 1.0f / applicationScale;
+        if (applicationScale != 1.0f) {
+            compatibilityFlags |= SCALING_REQUIRED;
+        }
     }
 
     private CompatibilityInfo() {
-        mApplicationScale = mApplicationInvertedScale = 1.0f;
-        mExpandable = mConfiguredExpandable = true;
-        mScalingRequired = false;
+        applicationScale = applicationInvertedScale = 1.0f;
+        compatibilityFlags = EXPANDABLE | CONFIGURED_EXPANDABLE;
     }
 
+    /**
+     * Sets the application's visible rect in compatibility mode.
+     * @param xOffset the application's x offset that is added to center the content.
+     * @param widthPixels the application's width in real pixels on the screen.
+     * @param heightPixels the application's height in real pixels on the screen.
+     */
+    public void setVisibleRect(int xOffset, int widthPixels, int heightPixels) {
+        this.mXOffset = xOffset; 
+        mWidth = widthPixels;
+        mHeight = heightPixels;
+    }
+    
+    /**
+     * Sets expandable bit in the compatibility flag.
+     */
+    public void setExpandable(boolean expandable) {
+        if (expandable) {
+            compatibilityFlags |= CompatibilityInfo.EXPANDABLE;
+        } else {
+            compatibilityFlags &= ~CompatibilityInfo.EXPANDABLE;
+        }
+    }
+
+    /**
+     * @return true if the application is configured to be expandable.
+     */
+    public boolean isConfiguredExpandable() {
+        return (compatibilityFlags & CompatibilityInfo.CONFIGURED_EXPANDABLE) != 0;
+    }
+
+    /**
+     * @return true if the scaling is required
+     */
+    public boolean isScalingRequired() {
+        return (compatibilityFlags & SCALING_REQUIRED) != 0;
+    }
+    
     @Override
     public String toString() {
-        return "CompatibilityInfo{scale=" + mApplicationScale +
-                ", expandable=" + mExpandable + "}"; 
+        return "CompatibilityInfo{scale=" + applicationScale +
+                ", compatibility flag=" + compatibilityFlags + "}"; 
     }
-}
+
+    /**
+     * Returns the translator which can translate the coordinates of the window.
+     * There are five different types of Translator.
+     * 
+     * 1) {@link CompatibilityInfo#X_SHIFT_AND_CLIP_CONTENT}
+     *   Shift and clip the content of the window at drawing time. Used for activities'
+     *   main window (with no gravity).
+     * 2) {@link CompatibilityInfo#X_SHIFT_CONTENT}
+     *   Shift the content of the window at drawing time. Used for windows that is created by
+     *   an application and expected to be aligned with the application window.
+     * 3) {@link CompatibilityInfo#X_SHIFT_WINDOW}
+     *   Create the window with adjusted x- coordinates. This is typically used 
+     *   in popup window, where it has to be placed relative to main window.
+     * 4) {@link CompatibilityInfo#X_SHIFT_NONE}
+     *   No adjustment required, such as dialog.
+     * 5) Same as X_SHIFT_WINDOW, but no scaling. This is used by {@link SurfaceView}, which
+     *  does not require scaling, but its window's location has to be adjusted.
+     * 
+     * @param params the window's parameter
+     */
+    public Translator getTranslator(WindowManager.LayoutParams params) {
+        if ( (compatibilityFlags & CompatibilityInfo.SCALING_EXPANDABLE_MASK)
+                == CompatibilityInfo.EXPANDABLE) {
+            if (DBG) Log.d(TAG, "no translation required");
+            return null;
+        }
+        
+        if ((compatibilityFlags & 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);
+            } else {
+                int shiftMode;
+                if (params.gravity == Gravity.NO_GRAVITY) {
+                    // For Regular Application window
+                    shiftMode = X_SHIFT_AND_CLIP_CONTENT;
+                    if (DBG) Log.d(TAG, "shift and clip translator");
+                } else if (params.width == WindowManager.LayoutParams.FILL_PARENT) {
+                    // For Regular Application window
+                    shiftMode = X_SHIFT_CONTENT;
+                    if (DBG) Log.d(TAG, "shift content translator");
+                } else if ((params.gravity & Gravity.LEFT) != 0 && params.x > 0) {
+                    shiftMode = X_SHIFT_WINDOW;
+                    if (DBG) Log.d(TAG, "shift window translator");
+                } else {
+                    shiftMode = X_SHIFT_NONE;
+                    if (DBG) Log.d(TAG, "no content/window translator");
+                }
+                return new Translator(shiftMode);
+            }
+        } else if (isScalingRequired()) {
+            return new Translator();
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * A helper object to translate the screen and window coordinates back and forth.
+     * @hide
+     */
+    public class Translator {
+        final private int mShiftMode;
+        final public boolean scalingRequired;
+        final public float applicationScale;
+        final public float applicationInvertedScale;
+        
+        private Rect mContentInsetsBuffer = null;
+        private Rect mVisibleInsets = null;
+        
+        Translator(int shiftMode, boolean scalingRequired, float applicationScale,
+                float applicationInvertedScale) {
+            mShiftMode = shiftMode;
+            this.scalingRequired = scalingRequired;
+            this.applicationScale = applicationScale;
+            this.applicationInvertedScale = applicationInvertedScale;
+        }
+
+        Translator(int shiftMode) {
+            this(shiftMode,
+                    isScalingRequired(),
+                    CompatibilityInfo.this.applicationScale,
+                    CompatibilityInfo.this.applicationInvertedScale);
+        }
+        
+        Translator() {
+            this(X_SHIFT_NONE);
+        }
+
+        /**
+         * Translate the screen rect to the application frame.
+         */
+        public void translateRectInScreenToAppWinFrame(Rect rect) {
+            if (rect.isEmpty()) return; // skip if the window size is empty.
+            switch (mShiftMode) {
+                case X_SHIFT_AND_CLIP_CONTENT:
+                    rect.intersect(0, 0, mWidth, mHeight);
+                    break;
+                case X_SHIFT_CONTENT:
+                    rect.intersect(0, 0, mWidth + mXOffset, mHeight);
+                    break;
+                case X_SHIFT_WINDOW:
+                case X_SHIFT_NONE:
+                    break;
+            }
+            if (scalingRequired) {
+                rect.scale(applicationInvertedScale);
+            }
+        }
+
+        /**
+         * Translate the region in window to screen. 
+         */
+        public void translateRegionInWindowToScreen(Region transparentRegion) {
+            switch (mShiftMode) {
+                case X_SHIFT_AND_CLIP_CONTENT:
+                case X_SHIFT_CONTENT:
+                    transparentRegion.scale(applicationScale);
+                    transparentRegion.translate(mXOffset, 0);
+                    break;
+                case X_SHIFT_WINDOW:
+                case X_SHIFT_NONE:
+                    transparentRegion.scale(applicationScale);
+            }
+        }
+
+        /**
+         * Apply translation to the canvas that is necessary to draw the content.
+         */
+        public void translateCanvas(Canvas canvas) {
+            if (mShiftMode == X_SHIFT_CONTENT ||
+                    mShiftMode == X_SHIFT_AND_CLIP_CONTENT) {
+                // TODO: clear outside when rotation is changed.
+
+                // Translate x-offset only when the content is shifted.
+                canvas.translate(mXOffset, 0);
+            }
+            if (scalingRequired) {
+                canvas.scale(applicationScale, applicationScale);
+            }
+        }
+
+        /**
+         * Translate the motion event captured on screen to the application's window.
+         */
+        public void translateEventInScreenToAppWindow(MotionEvent event) {
+            if (mShiftMode == X_SHIFT_CONTENT ||
+                    mShiftMode == X_SHIFT_AND_CLIP_CONTENT) {
+                event.translate(-mXOffset, 0);
+            }
+            if (scalingRequired) {
+                event.scale(applicationInvertedScale);
+            }
+        }
+
+        /**
+         * Translate the window's layout parameter, from application's view to
+         * Screen's view.
+         */
+        public void translateWindowLayout(WindowManager.LayoutParams params) {
+            switch (mShiftMode) {
+                case X_SHIFT_NONE:
+                case X_SHIFT_AND_CLIP_CONTENT:
+                case X_SHIFT_CONTENT:
+                    params.scale(applicationScale);
+                    break;
+                case X_SHIFT_WINDOW:
+                    params.scale(applicationScale);
+                    params.x += mXOffset;
+                    break;
+            }
+        }
+        
+        /**
+         * Translate a Rect in application's window to screen.
+         */
+        public void translateRectInAppWindowToScreen(Rect rect) {
+            // TODO Auto-generated method stub
+            if (scalingRequired) {
+                rect.scale(applicationScale);
+            }
+            switch(mShiftMode) {
+                case X_SHIFT_NONE:
+                case X_SHIFT_WINDOW:
+                    break;
+                case X_SHIFT_CONTENT:
+                case X_SHIFT_AND_CLIP_CONTENT:
+                    rect.offset(mXOffset, 0);
+                    break;
+            }
+        }
+ 
+        /**
+         * Translate a Rect in screen coordinates into the app window's coordinates.
+         */
+        public void translateRectInScreenToAppWindow(Rect rect) {
+            switch (mShiftMode) {
+                case X_SHIFT_NONE:
+                case X_SHIFT_WINDOW:
+                    break;
+                case X_SHIFT_CONTENT: {
+                    rect.intersects(mXOffset, 0, rect.right, rect.bottom);
+                    int dx = Math.min(mXOffset, rect.left);
+                    rect.offset(-dx, 0);
+                    break;
+                }
+                case X_SHIFT_AND_CLIP_CONTENT: {
+                    rect.intersects(mXOffset, 0, mWidth + mXOffset, mHeight);
+                    int dx = Math.min(mXOffset, rect.left);
+                    rect.offset(-dx, 0);
+                    break;
+                }
+            }
+            if (scalingRequired) {
+                rect.scale(applicationInvertedScale);
+            }
+        }
+
+        /**
+         * Translate the location of the sub window.
+         * @param params
+         */
+        public void translateLayoutParamsInAppWindowToScreen(LayoutParams params) {
+            if (scalingRequired) {
+                params.scale(applicationScale);
+            }
+            switch (mShiftMode) {
+                // the window location on these mode does not require adjustmenet.
+                case X_SHIFT_NONE:
+                case X_SHIFT_WINDOW:
+                    break;
+                case X_SHIFT_CONTENT:
+                case X_SHIFT_AND_CLIP_CONTENT:
+                    params.x += mXOffset;
+                    break;
+            }
+        }
+
+        /**
+         * Translate the content insets in application window to Screen. This uses
+         * the internal buffer for content insets to avoid extra object allocation.
+         */
+        public Rect getTranslatedContentInsets(Rect contentInsets) {
+            if (mContentInsetsBuffer == null) mContentInsetsBuffer = new Rect();
+            mContentInsetsBuffer.set(contentInsets);
+            translateRectInAppWindowToScreen(mContentInsetsBuffer);
+            return mContentInsetsBuffer;
+        }
+
+        /**
+         * Translate the visible insets in application window to Screen. This uses
+         * the internal buffer for content insets to avoid extra object allocation.
+         */
+        public Rect getTranslatedVisbileInsets(Rect visibleInsets) {
+            if (mVisibleInsets == null) mVisibleInsets = new Rect();
+            mVisibleInsets.set(visibleInsets);
+            translateRectInAppWindowToScreen(mVisibleInsets);
+            return mVisibleInsets;
+        }
+    }
+}
\ No newline at end of file
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 71dbd38..cb9d46e 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -158,10 +158,10 @@
         }
         updateConfiguration(config, metrics);
         assets.ensureStringBlocks();
-        if (!mCompatibilityInfo.mScalingRequired) {
-            mPreloadedDrawables = sPreloadedDrawables;
-        } else {
+        if (mCompatibilityInfo.isScalingRequired()) {
             mPreloadedDrawables = emptySparseArray();
+        } else {
+            mPreloadedDrawables = sPreloadedDrawables;
         }
     }
 
diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java
index 1064fb6..d48cbd5 100644
--- a/core/java/android/net/MobileDataStateTracker.java
+++ b/core/java/android/net/MobileDataStateTracker.java
@@ -377,6 +377,8 @@
         if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_MMS)) {
             mLastCallingPid = callingPid;
             return setEnableApn(Phone.APN_TYPE_MMS, true);
+        } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_DEFAULT)) {
+            return setEnableApn(Phone.APN_TYPE_DEFAULT_FEATURE, true);
         } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_SUPL)) {
             return setEnableApn(Phone.APN_TYPE_SUPL, true);
         } else {
@@ -399,6 +401,8 @@
     public int stopUsingNetworkFeature(String feature, int callingPid, int callingUid) {
         if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_MMS)) {
             return setEnableApn(Phone.APN_TYPE_MMS, false);
+        } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_DEFAULT)) {
+            return setEnableApn(Phone.APN_TYPE_DEFAULT_FEATURE, false);
         } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_SUPL)) {
             return setEnableApn(Phone.APN_TYPE_SUPL, false);
         } else {
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/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 48dc3ae..5cc3315 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -16,6 +16,9 @@
 
 package android.provider;
 
+import java.util.Arrays;
+import java.util.List;
+
 import android.content.Intent;
 import android.graphics.BitmapFactory;
 import android.net.Uri;
@@ -127,6 +130,19 @@
         public static final String STARRED = "starred";
 
         /**
+         * A custom ringtone associated with a person. Not always present.
+         * <P>Type: TEXT (URI to the ringtone)</P>
+         */
+        public static final String CUSTOM_RINGTONE = "custom_ringtone";
+
+        /**
+         * Whether the person should always be sent to voicemail. Not always
+         * present.
+         * <P>Type: INTEGER (0 for false, 1 for true)</P>
+         */
+        public static final String SEND_TO_VOICEMAIL = "send_to_voicemail";
+
+        /**
          * Reference to the row in the data table holding the primary phone number.
          * <P>Type: INTEGER REFERENCES data(_id)</P>
          */
@@ -143,12 +159,6 @@
          * <P>Type: INTEGER REFERENCES data(_id)</P>
          */
         public static final String PHOTO_ID = "photo_id";
-
-        /**
-         * Reference to a row containing custom ringtone and send to voicemail information.
-         * <P>Type: INTEGER REFERENCES data(_id)</P>
-         */
-        public static final String CUSTOM_RINGTONE_ID = "custom_ringtone_id";
     }
 
     /**
@@ -261,6 +271,19 @@
         private Contacts()  {}
 
         /**
+         * The package name that owns this contact and all of its details. This
+         * package has control over the {@link #IS_RESTRICTED} flag, and can
+         * grant {@link RestrictionExceptions} to other packages.
+         */
+        public static final String PACKAGE = "package";
+
+        /**
+         * Flag indicating that this data entry has been restricted by the owner
+         * {@link #PACKAGE}.
+         */
+        public static final String IS_RESTRICTED = "is_restricted";
+
+        /**
          * A reference to the {@link Accounts#_ID} that this data belongs to.
          */
         public static final String ACCOUNTS_ID = "accounts_id";
@@ -332,11 +355,6 @@
 
     private interface DataColumns {
         /**
-         * The package name that defines this type of data.
-         */
-        public static final String PACKAGE = "package";
-
-        /**
          * The mime-type of the item represented by this row.
          */
         public static final String MIMETYPE = "mimetype";
@@ -361,12 +379,6 @@
         public static final String IS_SUPER_PRIMARY = "is_super_primary";
 
         /**
-         * Flag indicating that this data entry has been restricted by the owner
-         * {@link #PACKAGE}.
-         */
-        public static final String IS_RESTRICTED = "is_restricted";
-
-        /**
          * The version of this data record. This is a read-only value. The data column is
          * guaranteed to not change without the version going up. This value is monotonically
          * increasing.
@@ -519,6 +531,18 @@
         }
 
         /**
+         * Returns the precedence of the status code the higher number being the higher precedence.
+         *
+         * @param status The status code.
+         * @return An integer representing the precedence, 0 being the lowest.
+         */
+        public static final int getPresencePrecedence(int status) {
+            // Keep this function here incase we want to enforce a different precedence than the
+            // natural order of the status constants.
+            return status;
+        }
+
+        /**
          * The MIME type of {@link #CONTENT_URI} providing a directory of
          * presence details.
          */
@@ -914,28 +938,6 @@
         }
 
         /**
-         * Custom ringtone associated with the contact.
-         */
-        public static final class CustomRingtone implements BaseCommonColumns {
-            private CustomRingtone() {}
-
-            public static final String CONTENT_ITEM_TYPE =
-                    "vnd.android.cursor.item/custom_ringtone";
-
-            /**
-             * Whether to send the number to voicemail.
-             * <P>Type: INTEGER (if set, non-0 means true)</P>
-             */
-            public static final String SEND_TO_VOICEMAIL = "data1";
-
-            /**
-             * The ringtone uri.
-             * <P>Type: TEXT</P>
-             */
-            public static final String RINGTONE_URI = "data2";
-        }
-
-        /**
          * Group Membership.
          */
         public static final class GroupMembership implements BaseCommonColumns {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index ebe54fcd..303d3fc 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1290,6 +1290,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).
          */
@@ -1884,6 +1928,12 @@
         public static final String LOCATION_PROVIDERS_ALLOWED = "location_providers_allowed";
 
         /**
+         * Whether assisted GPS should be enabled or not.
+         * @hide
+         */
+        public static final String ASSISTED_GPS_ENABLED = "assisted_gps_enabled";
+
+        /**
          * The Logging ID (a unique 64-bit value) as a hex string.
          * Used as a pseudonymous identifier for logging.
          * @deprecated This identifier is poorly initialized and has
@@ -2712,6 +2762,12 @@
             "gtalk_max_retries_for_auth_expired";
 
         /**
+         * This is the url for getting the app token for server-to-device data messaging.
+         */
+        public static final String DATA_MESSAGE_GET_APP_TOKEN_URL =
+                "data_messaging_get_app_token_url";
+        
+        /**
          * Enable use of ssl session caching.
          * 'db' - save each session in a (per process) database
          * 'file' - save each session in a (per process) file
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index c064284..1502d98 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);
@@ -333,8 +333,8 @@
                 return;
             }
             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);
             } catch (RemoteException e) {
                 // TTS died; restart it.
                 mStarted = false;
@@ -353,6 +353,27 @@
 
 
     /**
+     * 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.
+     */
+    public void speakIpa(String ipaText, int queueMode, HashMap<String,String> params)
+    {
+        //TODO: Implement speakIpa
+    }
+
+
+    /**
      * Plays the earcon using the specified queueing mode and parameters.
      *
      * @param earcon
@@ -470,7 +491,9 @@
             }
             try {
                 if (speechRate > 0) {
-                    mITts.setSpeechRate((int)(speechRate*100));
+                    mCachedRate = (int)(speechRate*100);
+                    updateCachedParamArray();
+                    mITts.setSpeechRate(mCachedRate);
                 }
             } catch (RemoteException e) {
                 // TTS died; restart it.
@@ -528,7 +551,11 @@
                 return;
             }
             try {
-                mITts.setLanguage(loc.getISO3Language(), loc.getISO3Country(), loc.getVariant());
+                mCachedLang = loc.getISO3Language();
+                mCachedCountry = loc.getISO3Country();
+                mCachedVariant = loc.getVariant();
+                updateCachedParamArray();
+                mITts.setLanguage(mCachedLang, mCachedCountry, mCachedVariant);
             } catch (RemoteException e) {
                 // TTS died; restart it.
                 mStarted = false;
@@ -537,6 +564,20 @@
         }
     }
 
+    /**
+     * 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) {
+        //TODO: Implement isLanguageAvailable
+        return TTS_LANG_NOT_SUPPORTED;
+    }
+
+
 
     /**
      * Speaks the given text using the specified queueing mode and parameters.
diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java
index a095913..d89ada0 100644
--- a/core/java/android/util/DisplayMetrics.java
+++ b/core/java/android/util/DisplayMetrics.java
@@ -106,16 +106,8 @@
      * {@hide}
      */
     public void updateMetrics(CompatibilityInfo compatibilityInfo, int orientation) {
-        if (compatibilityInfo.mScalingRequired) {
-            float invertedRatio = compatibilityInfo.mApplicationInvertedScale;
-            density *= invertedRatio;
-            scaledDensity *= invertedRatio;
-            xdpi *= invertedRatio;
-            ydpi *= invertedRatio;
-            widthPixels *= invertedRatio;
-            heightPixels *= invertedRatio;
-        }
-        if (!compatibilityInfo.mConfiguredExpandable) {
+        int xOffset = 0;
+        if (!compatibilityInfo.isConfiguredExpandable()) {
             // Note: this assume that configuration is updated before calling
             // updateMetrics method.
             int defaultWidth;
@@ -141,11 +133,13 @@
             
             if (defaultWidth == widthPixels && defaultHeight == heightPixels) {
                 // the screen size is same as expected size. make it expandable
-                compatibilityInfo.mExpandable = true;
+                compatibilityInfo.setExpandable(true);
             } else {
-                compatibilityInfo.mExpandable = false;
+                compatibilityInfo.setExpandable(false);
                 // adjust the size only when the device's screen is bigger.
                 if (defaultWidth < widthPixels) {
+                    // content/window's x offset in original pixels
+                    xOffset = ((widthPixels - defaultWidth) / 2);
                     widthPixels = defaultWidth;
                 }
                 if (defaultHeight < heightPixels) {
@@ -153,8 +147,19 @@
                 }
             }
         }
+        compatibilityInfo.setVisibleRect(xOffset, widthPixels, heightPixels);
+        if (compatibilityInfo.isScalingRequired()) {
+            float invertedRatio = compatibilityInfo.applicationInvertedScale;
+            density *= invertedRatio;
+            scaledDensity *= invertedRatio;
+            xdpi *= invertedRatio;
+            ydpi *= invertedRatio;
+            widthPixels *= invertedRatio;
+            heightPixels *= invertedRatio;
+        }
     }
 
+    @Override
     public String toString() {
         return "DisplayMetrics{density=" + density + ", width=" + widthPixels +
             ", height=" + heightPixels + ", scaledDensity=" + scaledDensity +
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 69c6a7c..0e37b26 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -270,28 +270,47 @@
     }
 
     /**
-     * Scales down the cood of this event by the given scale.
+     * Scales down the coordination of this event by the given scale.
      *
      * @hide
      */
     public void scale(float scale) {
-        if (scale != 1.0f) {
-            mX *= scale;
-            mY *= scale;
-            mRawX *= scale;
-            mRawY *= scale;
-            mSize *= scale;
-            mXPrecision *= scale;
-            mYPrecision *= scale;
-            if (mHistory != null) {
-                float[] history = mHistory;
-                int length = history.length;
-                for (int i = 0; i < length; i += 4) {
-                    history[i] *= scale;        // X
-                                                // history[i + 2] == pressure
-                    history[i + 1] *= scale;    // Y
-                    history[i + 3] *= scale;    // Size, TODO: square this?
-                }
+        mX *= scale;
+        mY *= scale;
+        mRawX *= scale;
+        mRawY *= scale;
+        mSize *= scale;
+        mXPrecision *= scale;
+        mYPrecision *= scale;
+        if (mHistory != null) {
+            float[] history = mHistory;
+            int length = history.length;
+            for (int i = 0; i < length; i += 4) {
+                history[i] *= scale;        // X
+                history[i + 1] *= scale;    // Y
+                // no need to scale pressure ([i+2])
+                history[i + 3] *= scale;    // Size, TODO: square this?
+            }
+        }
+    }
+
+    /**
+     * Translate the coordination of the event by given x and y.
+     *
+     * @hide
+     */
+    public void translate(float dx, float dy) {
+        mX += dx;
+        mY += dy;
+        mRawX += dx;
+        mRawY += dx;
+        if (mHistory != null) {
+            float[] history = mHistory;
+            int length = history.length;
+            for (int i = 0; i < length; i += 4) {
+                history[i] += dx;        // X
+                history[i + 1] += dy;    // Y
+                // no need to translate pressure (i+2) and size (i+3) 
             }
         }
     }
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 6519852..1e2bc1f 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.content.res.CompatibilityInfo;
+import android.content.res.CompatibilityInfo.Translator;
 import android.graphics.Canvas;
 import android.graphics.PixelFormat;
 import android.graphics.PorterDuff;
@@ -139,24 +140,21 @@
     int mFormat = -1;
     int mType = -1;
     final Rect mSurfaceFrame = new Rect();
-    private final CompatibilityInfo mCompatibilityInfo;
+    private Translator mTranslator;
 
     public SurfaceView(Context context) {
         super(context);
         setWillNotDraw(true);
-        mCompatibilityInfo = context.getResources().getCompatibilityInfo();
     }
     
     public SurfaceView(Context context, AttributeSet attrs) {
         super(context, attrs);
         setWillNotDraw(true);
-        mCompatibilityInfo = context.getResources().getCompatibilityInfo();
     }
 
     public SurfaceView(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
         setWillNotDraw(true);
-        mCompatibilityInfo = context.getResources().getCompatibilityInfo();
     }
     
     /**
@@ -259,9 +257,9 @@
     public boolean dispatchTouchEvent(MotionEvent event) {
         // SurfaceView uses pre-scaled size unless fixed size is requested. This hook
         // scales the event back to the pre-scaled coordinates for such surface.
-        if (mRequestedWidth < 0 && mCompatibilityInfo.mScalingRequired) {
+        if (mRequestedWidth < 0 && mTranslator != null) {
             MotionEvent scaledBack = MotionEvent.obtain(event);
-            scaledBack.scale(mCompatibilityInfo.mApplicationScale);
+            scaledBack.scale(mTranslator.applicationScale);
             try {
                 return super.dispatchTouchEvent(scaledBack);
             } finally {
@@ -298,15 +296,18 @@
         if (!mHaveFrame) {
             return;
         }
-        float appScale = mCompatibilityInfo.mApplicationScale;
+        mTranslator = ((ViewRoot)getRootView().getParent()).mTranslator;
+
+        float appScale = mTranslator == null ? 1.0f : mTranslator.applicationScale;
         
         int myWidth = mRequestedWidth;
         if (myWidth <= 0) myWidth = getWidth();
         int myHeight = mRequestedHeight;
         if (myHeight <= 0) myHeight = getHeight();
 
-        // Use original size for surface unless fixed size is requested.
-        if (mRequestedWidth <= 0 && mCompatibilityInfo.mScalingRequired) {
+        // Use original size if the app specified the size of the view,
+        // and let the flinger to scale up.
+        if (mRequestedWidth <= 0 && mTranslator != null && mTranslator.scalingRequired) {
             myWidth *= appScale;
             myHeight *= appScale;
         }
@@ -326,7 +327,7 @@
                     + " visible=" + visibleChanged
                     + " left=" + (mLeft != mLocation[0])
                     + " top=" + (mTop != mLocation[1]));
-            
+
             try {
                 final boolean visible = mVisible = mRequestedVisible;
                 mLeft = mLocation[0];
@@ -336,16 +337,23 @@
                 mFormat = mRequestedFormat;
                 mType = mRequestedType;
 
-                // Scaling window's layout here because mLayout is not used elsewhere.
-                mLayout.x = (int) (mLeft * appScale);
-                mLayout.y = (int) (mTop * appScale);
-                mLayout.width = (int) (getWidth() * appScale);
-                mLayout.height = (int) (getHeight() * appScale);
+                // Scaling/Translate window's layout here because mLayout is not used elsewhere.
+                
+                // Places the window relative
+                mLayout.x = mLeft;
+                mLayout.y = mTop;
+                mLayout.width = getWidth();
+                mLayout.height = getHeight();
+                if (mTranslator != null) {
+                    mTranslator.translateLayoutParamsInAppWindowToScreen(mLayout);
+                }
+                
                 mLayout.format = mRequestedFormat;
                 mLayout.flags |=WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
                               | WindowManager.LayoutParams.FLAG_SCALED
                               | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                               | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
+                              | WindowManager.LayoutParams.FLAG_NO_COMPATIBILITY_SCALING
                               ;
 
                 mLayout.memoryType = mRequestedType;
@@ -372,13 +380,6 @@
                         visible ? VISIBLE : GONE, false, mWinFrame, mContentInsets,
                         mVisibleInsets, mSurface);
 
-                if (mCompatibilityInfo.mScalingRequired) {
-                    float invertedScale = mCompatibilityInfo.mApplicationInvertedScale;
-                    mContentInsets.scale(invertedScale);
-                    mVisibleInsets.scale(invertedScale);
-                    mWinFrame.scale(invertedScale);
-                }
-
                 if (localLOGV) Log.i(TAG, "New surface: " + mSurface
                         + ", vis=" + visible + ", frame=" + mWinFrame);
                 mSurfaceFrame.left = 0;
@@ -447,24 +448,14 @@
 
     private static class MyWindow extends IWindow.Stub {
         private final WeakReference<SurfaceView> mSurfaceView;
-        private final CompatibilityInfo mCompatibilityInfo;
 
         public MyWindow(SurfaceView surfaceView) {
             mSurfaceView = new WeakReference<SurfaceView>(surfaceView);
-            mCompatibilityInfo = surfaceView.getContext().getResources().getCompatibilityInfo();
         }
 
         public void resized(int w, int h, Rect coveredInsets,
                 Rect visibleInsets, boolean reportDraw) {
             SurfaceView surfaceView = mSurfaceView.get();
-            if (mCompatibilityInfo.mScalingRequired) {
-                float scale = mCompatibilityInfo.mApplicationInvertedScale;
-                w *= scale;
-                h *= scale;
-                coveredInsets.scale(scale);
-                visibleInsets.scale(scale);
-            }
-
             if (surfaceView != null) {
                 if (localLOGV) Log.v(
                         "SurfaceView", surfaceView + " got resized: w=" +
@@ -627,9 +618,6 @@
             Canvas c = null;
             if (!mDrawingStopped && mWindow != null) {
                 Rect frame = dirty != null ? dirty : mSurfaceFrame;
-                if (mCompatibilityInfo.mScalingRequired) {
-                    frame.scale(mCompatibilityInfo.mApplicationScale);
-                }
                 try {
                     c = mSurface.lockCanvas(frame);
                 } catch (Exception e) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index f17f0e4..3bfdde8 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -6543,7 +6543,7 @@
         boolean changed = false;
 
         if (DBG) {
-            System.out.println(this + " View.setFrame(" + left + "," + top + ","
+            Log.d("View", this + " View.setFrame(" + left + "," + top + ","
                     + right + "," + bottom + ")");
         }
 
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index fe5edc2..a12b14a 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -131,13 +131,12 @@
     Rect mDirty; // will be a graphics.Region soon
     boolean mIsAnimating;
 
-    private CompatibilityInfo mCompatibilityInfo;
+    CompatibilityInfo.Translator mTranslator;
 
     final View.AttachInfo mAttachInfo;
 
     final Rect mTempRect; // used in the transaction to not thrash the heap.
     final Rect mVisRect; // used to retrieve visible rect of focused view.
-    final Point mVisPoint; // used to retrieve global offset of focused view.
 
     boolean mTraversalScheduled;
     boolean mWillDrawSoon;
@@ -225,7 +224,6 @@
         mDirty = new Rect();
         mTempRect = new Rect();
         mVisRect = new Rect();
-        mVisPoint = new Point();
         mWinFrame = new Rect();
         mWindow = new W(this, context);
         mInputMethodCallback = new InputMethodCallback(this);
@@ -394,20 +392,25 @@
             if (mView == null) {
                 mView = view;
                 mWindowAttributes.copyFrom(attrs);
-                mCompatibilityInfo = mView.getContext().getResources().getCompatibilityInfo();
+
+                CompatibilityInfo compatibilityInfo =
+                        mView.getContext().getResources().getCompatibilityInfo();
+                mTranslator = compatibilityInfo.getTranslator(attrs);
                 boolean restore = false;
-                if (mCompatibilityInfo.mScalingRequired || !mCompatibilityInfo.mExpandable) {
+                if (attrs != null && mTranslator != null) {
                     restore = true;
-                    mWindowAttributes.backup();
+                    attrs.backup();
+                    mTranslator.translateWindowLayout(attrs);
                 }
-                if (!mCompatibilityInfo.mExpandable) {
-                    adjustWindowAttributesForCompatibleMode(mWindowAttributes);
-                }
+                if (DEBUG_LAYOUT) Log.d(TAG, "WindowLayout in setView:" + attrs);
+
                 mSoftInputMode = attrs.softInputMode;
                 mWindowAttributesChanged = true;
                 mAttachInfo.mRootView = view;
-                mAttachInfo.mScalingRequired = mCompatibilityInfo.mScalingRequired;
-                mAttachInfo.mApplicationScale = mCompatibilityInfo.mApplicationScale;
+                mAttachInfo.mScalingRequired =
+                        mTranslator == null ? false : mTranslator.scalingRequired;
+                mAttachInfo.mApplicationScale =
+                        mTranslator == null ? 1.0f : mTranslator.applicationScale;
                 if (panelParentView != null) {
                     mAttachInfo.mPanelParentWindowToken
                             = panelParentView.getApplicationWindowToken();
@@ -428,15 +431,14 @@
                     mAttachInfo.mRootView = null;
                     unscheduleTraversals();
                     throw new RuntimeException("Adding window failed", e);
+                } finally {
+                    if (restore) {
+                        attrs.restore();
+                    }
                 }
 
-                if (restore) {
-                    mWindowAttributes.restore();
-                }
-
-                if (mCompatibilityInfo.mScalingRequired) {
-                    mAttachInfo.mContentInsets.scale(
-                            mCompatibilityInfo.mApplicationInvertedScale);
+                if (mTranslator != null) {
+                    mTranslator.translateRectInScreenToAppWindow(mAttachInfo.mContentInsets);
                 }
                 mPendingContentInsets.set(mAttachInfo.mContentInsets);
                 mPendingVisibleInsets.set(0, 0, 0, 0);
@@ -548,14 +550,14 @@
 
     public void invalidateChild(View child, Rect dirty) {
         checkThread();
-        if (LOCAL_LOGV) Log.v(TAG, "Invalidate child: " + dirty);
-        if (mCurScrollY != 0 || mCompatibilityInfo.mScalingRequired) {
+        if (DEBUG_DRAW) Log.v(TAG, "Invalidate child: " + dirty);
+        if (mCurScrollY != 0 || mTranslator != null) {
             mTempRect.set(dirty);
             if (mCurScrollY != 0) {
                mTempRect.offset(0, -mCurScrollY);
             }
-            if (mCompatibilityInfo.mScalingRequired) {
-                mTempRect.scale(mCompatibilityInfo.mApplicationScale);
+            if (mTranslator != null) {
+                mTranslator.translateRectInAppWindowToScreen(mTempRect);
             }
             dirty = mTempRect;
         }
@@ -574,7 +576,7 @@
         return null;
     }
 
-     public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset) {
+    public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset) {
         if (child != mView) {
             throw new RuntimeException("child is not mine, honest!");
         }
@@ -635,14 +637,14 @@
         boolean viewVisibilityChanged = mViewVisibility != viewVisibility
                 || mNewSurfaceNeeded;
 
-        float appScale = mCompatibilityInfo.mApplicationScale;
+        float appScale = mAttachInfo.mApplicationScale;
 
         WindowManager.LayoutParams params = null;
         if (mWindowAttributesChanged) {
             mWindowAttributesChanged = false;
             params = lp;
         }
-
+        Rect frame = mWinFrame;
         if (mFirst) {
             fullRedrawNeeded = true;
             mLayoutRequested = true;
@@ -667,11 +669,11 @@
             //Log.i(TAG, "Screen on initialized: " + attachInfo.mKeepScreenOn);
 
         } else {
-            desiredWindowWidth = mWinFrame.width();
-            desiredWindowHeight = mWinFrame.height();
+            desiredWindowWidth = frame.width();
+            desiredWindowHeight = frame.height();
             if (desiredWindowWidth != mWidth || desiredWindowHeight != mHeight) {
                 if (DEBUG_ORIENTATION) Log.v("ViewRoot",
-                        "View " + host + " resized to: " + mWinFrame);
+                        "View " + host + " resized to: " + frame);
                 fullRedrawNeeded = true;
                 mLayoutRequested = true;
                 windowResizesToFitContent = true;
@@ -817,7 +819,6 @@
                 }
             }
 
-            final Rect frame = mWinFrame;
             boolean initialized = false;
             boolean contentInsetsChanged = false;
             boolean visibleInsetsChanged;
@@ -890,7 +891,7 @@
             } catch (RemoteException e) {
             }
             if (DEBUG_ORIENTATION) Log.v(
-                    "ViewRoot", "Relayout returned: frame=" + mWinFrame + ", surface=" + mSurface);
+                    "ViewRoot", "Relayout returned: frame=" + frame + ", surface=" + mSurface);
 
             attachInfo.mWindowLeft = frame.left;
             attachInfo.mWindowTop = frame.top;
@@ -965,7 +966,6 @@
             if (Config.DEBUG && ViewDebug.profileLayout) {
                 startTime = SystemClock.elapsedRealtime();
             }
-
             host.layout(0, 0, host.mMeasuredWidth, host.mMeasuredHeight);
 
             if (Config.DEBUG && ViewDebug.consistencyCheckEnabled) {
@@ -992,7 +992,10 @@
                         mTmpLocation[1] + host.mBottom - host.mTop);
 
                 host.gatherTransparentRegion(mTransparentRegion);
-                mTransparentRegion.scale(appScale);
+                if (mTranslator != null) {
+                    mTranslator.translateRegionInWindowToScreen(mTransparentRegion);
+                }
+
                 if (!mTransparentRegion.equals(mPreviousTransparentRegion)) {
                     mPreviousTransparentRegion.set(mTransparentRegion);
                     // reconfigure window manager
@@ -1023,15 +1026,17 @@
                     = givenContent.bottom = givenVisible.left = givenVisible.top
                     = givenVisible.right = givenVisible.bottom = 0;
             attachInfo.mTreeObserver.dispatchOnComputeInternalInsets(insets);
-            if (mCompatibilityInfo.mScalingRequired) {
-                insets.contentInsets.scale(appScale);
-                insets.visibleInsets.scale(appScale);
+            Rect contentInsets = insets.contentInsets;
+            Rect visibleInsets = insets.visibleInsets;
+            if (mTranslator != null) {
+                contentInsets = mTranslator.getTranslatedContentInsets(contentInsets);
+                visibleInsets = mTranslator.getTranslatedVisbileInsets(visibleInsets);
             }
             if (insetsPending || !mLastGivenInsets.equals(insets)) {
                 mLastGivenInsets.set(insets);
                 try {
                     sWindowSession.setInsets(mWindow, insets.mTouchableInsets,
-                            insets.contentInsets, insets.visibleInsets);
+                            contentInsets, visibleInsets);
                 } catch (RemoteException e) {
                 }
             }
@@ -1174,8 +1179,8 @@
             mCurScrollY = yoff;
             fullRedrawNeeded = true;
         }
-        float appScale = mCompatibilityInfo.mApplicationScale;
-        boolean scalingRequired = mCompatibilityInfo.mScalingRequired;
+        float appScale = mAttachInfo.mApplicationScale;
+        boolean scalingRequired = mAttachInfo.mScalingRequired;
 
         Rect dirty = mDirty;
         if (mUseGL) {
@@ -1194,8 +1199,8 @@
                     int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
                     try {
                         canvas.translate(0, -yoff);
-                        if (scalingRequired) {
-                            canvas.scale(appScale, appScale);
+                        if (mTranslator != null) {
+                            mTranslator.translateCanvas(canvas);
                         }
                         mView.draw(canvas);
                         if (Config.DEBUG && ViewDebug.consistencyCheckEnabled) {
@@ -1246,7 +1251,6 @@
             int top = dirty.top;
             int right = dirty.right;
             int bottom = dirty.bottom;
-
             canvas = surface.lockCanvas(dirty);
 
             if (left != dirty.left || top != dirty.top || right != dirty.right ||
@@ -1302,8 +1306,8 @@
                 int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
                 try {
                     canvas.translate(0, -yoff);
-                    if (scalingRequired) {
-                        canvas.scale(appScale, appScale);
+                    if (mTranslator != null) {
+                        mTranslator.translateCanvas(canvas);
                     }
                     mView.draw(canvas);
                 } finally {
@@ -1616,10 +1620,9 @@
             } else {
                 didFinish = event.getAction() == MotionEvent.ACTION_OUTSIDE;
             }
-            if (event != null && mCompatibilityInfo.mScalingRequired) {
-                event.scale(mCompatibilityInfo.mApplicationInvertedScale);
+            if (event != null && mTranslator != null) {
+                mTranslator.translateEventInScreenToAppWindow(event);
             }
-
             try {
                 boolean handled;
                 if (mView != null && mAdded && event != null) {
@@ -1711,6 +1714,7 @@
         case RESIZED:
             Rect coveredInsets = ((Rect[])msg.obj)[0];
             Rect visibleInsets = ((Rect[])msg.obj)[1];
+
             if (mWinFrame.width() == msg.arg1 && mWinFrame.height() == msg.arg2
                     && mPendingContentInsets.equals(coveredInsets)
                     && mPendingVisibleInsets.equals(visibleInsets)) {
@@ -1745,7 +1749,7 @@
                         if (mGlWanted && !mUseGL) {
                             initializeGL();
                             if (mGlCanvas != null) {
-                                float appScale = mCompatibilityInfo.mApplicationScale;
+                                float appScale = mAttachInfo.mApplicationScale;
                                 mGlCanvas.setViewport(
                                         (int) (mWidth * appScale), (int) (mHeight * appScale));
                             }
@@ -2379,18 +2383,16 @@
 
     private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
             boolean insetsPending) throws RemoteException {
+
+        float appScale = mAttachInfo.mApplicationScale;
         boolean restore = false;
-        float appScale = mCompatibilityInfo.mApplicationScale;
-        boolean scalingRequired = mCompatibilityInfo.mScalingRequired;
-        if (params != null && !mCompatibilityInfo.mExpandable) {
+        if (params != null && mTranslator != null) {
             restore = true;
             params.backup();
-            adjustWindowAttributesForCompatibleMode(params);
+            mTranslator.translateWindowLayout(params);
         }
-        if (params != null && scalingRequired) {
-            if (!restore) params.backup();
-            restore = true;
-            params.scale(appScale);
+        if (params != null) {
+            if (DBG) Log.d(TAG, "WindowLayout in layoutWindow:" + params);
         }
         int relayoutResult = sWindowSession.relayout(
                 mWindow, params,
@@ -2401,44 +2403,16 @@
         if (restore) {
             params.restore();
         }
-        if (scalingRequired) {
-            float invertedScale = mCompatibilityInfo.mApplicationInvertedScale;
-            mPendingContentInsets.scale(invertedScale);
-            mPendingVisibleInsets.scale(invertedScale);
-            mWinFrame.scale(invertedScale);
+        
+        if (mTranslator != null) {
+            mTranslator.translateRectInScreenToAppWinFrame(mWinFrame);
+            mTranslator.translateRectInScreenToAppWindow(mPendingContentInsets);
+            mTranslator.translateRectInScreenToAppWindow(mPendingVisibleInsets);
         }
         return relayoutResult;
     }
 
     /**
-     * Adjust the window's layout parameter for compatibility mode. It replaces FILL_PARENT
-     * with the default window size, and centers if the window wanted to fill
-     * horizontally.
-     *
-     * @param attrs the window's layout params to adjust
-     */
-    private void adjustWindowAttributesForCompatibleMode(WindowManager.LayoutParams attrs) {
-        // fix app windows only
-        if (attrs.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
-            DisplayMetrics metrics = mView.getContext().getResources().getDisplayMetrics();
-            // TODO: improve gravity logic
-            if (attrs.width == ViewGroup.LayoutParams.FILL_PARENT) {
-                attrs.width = metrics.widthPixels;
-                attrs.gravity |= Gravity.CENTER_HORIZONTAL;
-                mWindowAttributesChanged = attrs == mWindowAttributes;
-            }
-            if (attrs.height == ViewGroup.LayoutParams.FILL_PARENT) {
-                attrs.height = metrics.heightPixels;
-                attrs.gravity |= Gravity.TOP;
-                mWindowAttributesChanged = attrs == mWindowAttributes;
-            }
-            if (DEBUG_LAYOUT) {
-                Log.d(TAG, "Adjusted Attributes for compatibility : " + attrs);
-            }
-        }
-    }
-
-    /**
      * {@inheritDoc}
      */
     public void playSoundEffect(int effectId) {
@@ -2541,16 +2515,14 @@
                 + " visibleInsets=" + visibleInsets.toShortString()
                 + " reportDraw=" + reportDraw);
         Message msg = obtainMessage(reportDraw ? RESIZED_REPORT :RESIZED);
-        if (mCompatibilityInfo.mScalingRequired) {
-            float invertedScale = mCompatibilityInfo.mApplicationInvertedScale;
-            coveredInsets.scale(invertedScale);
-            visibleInsets.scale(invertedScale);
-            msg.arg1 = (int) (w * invertedScale);
-            msg.arg2 = (int) (h * invertedScale);
-        } else {
-            msg.arg1 = w;
-            msg.arg2 = h;
+        if (mTranslator != null) {
+            mTranslator.translateRectInScreenToAppWindow(coveredInsets);
+            mTranslator.translateRectInScreenToAppWindow(visibleInsets);
+            w *= mTranslator.applicationInvertedScale;
+            h *= mTranslator.applicationInvertedScale;
         }
+        msg.arg1 = w;
+        msg.arg2 = h;
         msg.obj = new Rect[] { new Rect(coveredInsets), new Rect(visibleInsets) };
         sendMessage(msg);
     }
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index e295d15..bdb86d7 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -483,6 +483,12 @@
          * {@hide} */
         public static final int FLAG_SHOW_WHEN_LOCKED = 0x00080000;
 
+        /** Window flag: special flag to let a window ignore the compatibility scaling.
+         * This is used by SurfaceView to create a window that does not scale the content.
+         *
+         * {@hide} */
+        public static final int FLAG_NO_COMPATIBILITY_SCALING = 0x00100000;
+
         /** Window flag: a special option intended for system dialogs.  When
          * this flag is set, the window will demand focus unconditionally when
          * it is created.
@@ -978,8 +984,9 @@
 
         /**
          * Scale the layout params' coordinates and size.
+         * @hide
          */
-        void scale(float scale) {
+        public void scale(float scale) {
             x *= scale;
             y *= scale;
             if (width > 0) {
@@ -997,14 +1004,13 @@
         void backup() {
             int[] backup = mCompatibilityParamsBackup;
             if (backup == null) {
-                // we backup 5 elements, x, y, width, height and gravity.
-                backup = mCompatibilityParamsBackup = new int[5];
+                // we backup 4 elements, x, y, width, height
+                backup = mCompatibilityParamsBackup = new int[4];
             }
             backup[0] = x;
             backup[1] = y;
             backup[2] = width;
             backup[3] = height;
-            backup[4] = gravity;
         }
 
         /**
@@ -1018,7 +1024,6 @@
                 y = backup[1];
                 width = backup[2];
                 height = backup[3];
-                gravity = backup[4];
             }
         }
 
diff --git a/core/java/android/webkit/CacheManager.java b/core/java/android/webkit/CacheManager.java
index 4d471f7f..fb9edb9 100644
--- a/core/java/android/webkit/CacheManager.java
+++ b/core/java/android/webkit/CacheManager.java
@@ -79,6 +79,7 @@
         int httpStatusCode;
         long contentLength;
         long expires;
+        String expiresString;
         String localPath;
         String lastModified;
         String etag;
@@ -107,6 +108,13 @@
             return expires;
         }
 
+        /**
+         * @hide Pending API council approval
+         */
+        public String getExpiresString() {
+            return expiresString;
+        }
+
         public String getLastModified() {
             return lastModified;
         }
@@ -603,17 +611,18 @@
         if (location != null) ret.location = location;
 
         ret.expires = -1;
-        String expires = headers.getExpires();
-        if (expires != null) {
+        ret.expiresString = headers.getExpires();
+        if (ret.expiresString != null) {
             try {
-                ret.expires = HttpDateTime.parse(expires);
+                ret.expires = HttpDateTime.parse(ret.expiresString);
             } catch (IllegalArgumentException ex) {
                 // Take care of the special "-1" and "0" cases
-                if ("-1".equals(expires) || "0".equals(expires)) {
+                if ("-1".equals(ret.expiresString)
+                        || "0".equals(ret.expiresString)) {
                     // make it expired, but can be used for history navigation
                     ret.expires = 0;
                 } else {
-                    Log.e(LOGTAG, "illegal expires: " + expires);
+                    Log.e(LOGTAG, "illegal expires: " + ret.expiresString);
                 }
             }
         }
diff --git a/core/java/android/webkit/CallbackProxy.java b/core/java/android/webkit/CallbackProxy.java
index c407044..9a8c3c0 100644
--- a/core/java/android/webkit/CallbackProxy.java
+++ b/core/java/android/webkit/CallbackProxy.java
@@ -147,6 +147,16 @@
     }
 
     /**
+     * Get the WebChromeClient.
+     * @return the current WebChromeClient instance.
+     *
+     *@hide pending API council approval.
+     */
+    public WebChromeClient getWebChromeClient() {
+       return mWebChromeClient;
+    }
+
+    /**
      * Set the client DownloadListener.
      * @param client An implementation of DownloadListener.
      */
diff --git a/core/java/android/webkit/DebugFlags.java b/core/java/android/webkit/DebugFlags.java
index 89cb606..8e25395 100644
--- a/core/java/android/webkit/DebugFlags.java
+++ b/core/java/android/webkit/DebugFlags.java
@@ -42,6 +42,7 @@
     public static final boolean WEB_BACK_FORWARD_LIST = false;
     public static final boolean WEB_SETTINGS = false;
     public static final boolean WEB_SYNC_MANAGER = false;
+    public static final boolean WEB_TEXT_VIEW = false;
     public static final boolean WEB_VIEW = false;
     public static final boolean WEB_VIEW_CORE = false;
 
diff --git a/core/java/android/webkit/HTML5VideoViewProxy.java b/core/java/android/webkit/HTML5VideoViewProxy.java
new file mode 100644
index 0000000..973cb010
--- /dev/null
+++ b/core/java/android/webkit/HTML5VideoViewProxy.java
@@ -0,0 +1,128 @@
+/*
+ * 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.webkit;
+
+import android.content.Context;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+import android.view.View;
+import android.widget.MediaController;
+import android.widget.VideoView;
+
+import java.util.HashMap;
+
+/**
+ * <p>A View that displays Videos. Instances of this class
+ * are created on the WebCore thread. However, their code
+ * executes on the UI thread. Right now there is only one
+ * such view for fullscreen video rendering.
+ *
+ */
+class HTML5VideoViewProxy extends Handler {
+    // Logging tag.
+    private static final String LOGTAG = "HTML5VideoViewProxy";
+
+    // Message Ids
+    private static final int INIT              = 100;
+    private static final int PLAY              = 101;
+
+    // The singleton instance.
+    private static HTML5VideoViewProxy sInstance;
+    // The VideoView driven via this proxy.
+    private VideoView mVideoView;
+    // The context object used to initialize the VideoView and the
+    // MediaController.
+    private Context mContext;
+
+    /**
+     * Private constructor.
+     * @param context is the application context.
+     */
+    private HTML5VideoViewProxy(Context context) {
+        // This handler is for the main (UI) thread.
+        super(Looper.getMainLooper());
+        // Save the context object.
+        mContext = context;
+        // Send a message to the UI thread to create the VideoView.
+        // This need to be done on the UI thread, or else the
+        // event Handlers used by the VideoView and MediaController
+        // will be attached to the wrong thread.
+        Message message = obtainMessage(INIT);
+        sendMessage(message);
+    }
+
+    @Override
+    public void handleMessage(Message msg) {
+        // This executes on the UI thread.
+        switch (msg.what) {
+            case INIT:
+                // Create the video view and set a default controller.
+                mVideoView = new VideoView(mContext);
+                mVideoView.setMediaController(new MediaController(mContext));
+                break;
+            case PLAY:
+                // Check if the fullscreen video view is currently playing.
+                // If it is, ignore the message.
+                if (!mVideoView.isPlaying()) {
+                    HashMap<String, Object> map =
+                        (HashMap<String, Object>) msg.obj;
+                    String url = (String) map.get("url");
+                    WebView webview = (WebView) map.get("webview");
+                    WebChromeClient client = webview.getWebChromeClient();
+                    if (client != null) {
+                        mVideoView.setVideoURI(Uri.parse(url));
+                        mVideoView.start();
+                        client.onShowCustomView(mVideoView);
+                    }
+                }
+                break;
+        }
+    }
+
+    /**
+     * Play a video stream.
+     * @param url is the URL of the video stream.
+     * @param webview is the WebViewCore that is requesting the playback.
+     */
+    public void play(String url, WebViewCore webviewCore) {
+        // We need to know the webview that is requesting the playback.
+        Message message = obtainMessage(PLAY);
+        HashMap<String, Object> map = new HashMap();
+        map.put("url", url);
+        map.put("webview", webviewCore.getWebView());
+        message.obj = map;
+        sendMessage(message);
+    }
+
+    /**
+     * The factory for HTML5VideoViewProxy instances. Right now,
+     * it only produces a singleton.
+     * @param webViewCore is the WebViewCore that is requesting the proxy.
+     *
+     * @return the HTML5VideoViewProxy singleton.
+     */
+    public static HTML5VideoViewProxy getInstance(WebViewCore webViewCore) {
+        if (sInstance == null) {
+            sInstance = new HTML5VideoViewProxy(webViewCore.getWebView().getContext());
+        }
+        return sInstance;
+    }
+}
diff --git a/core/java/android/webkit/LoadListener.java b/core/java/android/webkit/LoadListener.java
index 08854f7..a87a5c2 100644
--- a/core/java/android/webkit/LoadListener.java
+++ b/core/java/android/webkit/LoadListener.java
@@ -949,7 +949,7 @@
         final int nativeResponse = nativeCreateResponse(
                 mUrl, statusCode, mStatusText,
                 mMimeType, mContentLength, mEncoding,
-                mCacheResult == null ? 0 : mCacheResult.expires / 1000);
+                mCacheResult == null ? null : mCacheResult.expiresString);
         if (mHeaders != null) {
             mHeaders.getHeaders(new Headers.HeaderCallback() {
                     public void header(String name, String value) {
@@ -1460,12 +1460,12 @@
      * @param expectedLength An estimate of the content length or the length
      *                       given by the server.
      * @param encoding HTTP encoding.
-     * @param expireTime HTTP expires converted to seconds since the epoch.
+     * @param expireTime HTTP expires.
      * @return The native response pointer.
      */
     private native int nativeCreateResponse(String url, int statusCode,
             String statusText, String mimeType, long expectedLength,
-            String encoding, long expireTime);
+            String encoding, String expireTime);
 
     /**
      * Add a response header to the native object.
diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java
index 754b1d9..19e39ed 100644
--- a/core/java/android/webkit/WebChromeClient.java
+++ b/core/java/android/webkit/WebChromeClient.java
@@ -18,6 +18,7 @@
 
 import android.graphics.Bitmap;
 import android.os.Message;
+import android.view.View;
 
 public class WebChromeClient {
 
@@ -44,6 +45,23 @@
     public void onReceivedIcon(WebView view, Bitmap icon) {}
 
     /**
+     * Notify the host application that the current page would
+     * like to show a custom View.
+     * @param view is the View object to be shown.
+     *
+     * @hide pending council approval
+     */
+    public void onShowCustomView(View view) {}
+
+    /**
+     * Notify the host application that the current page would
+     * like to hide its custom view.
+     *
+     * @hide pending council approval
+     */
+    public void onHideCustomView() {}
+
+    /**
      * Request the host application to create a new Webview. The host
      * application should handle placement of the new WebView in the view
      * system. The default behavior returns null.
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index f57c647..0ee3a60 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.
@@ -149,6 +168,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;
@@ -232,13 +252,13 @@
 
     // User agent strings.
     private static final String DESKTOP_USERAGENT =
-            "Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en)"
-            + " AppleWebKit/528.5+ (KHTML, like Gecko) Version/3.1.2"
-            + " Safari/525.20.1";
+            "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_7; en-us)"
+            + " AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0"
+            + " Safari/530.17";
     private static final String IPHONE_USERAGENT = 
-            "Mozilla/5.0 (iPhone; U; CPU iPhone 2_1 like Mac OS X; en)"
-            + " AppleWebKit/528.5+ (KHTML, like Gecko) Version/3.1.2"
-            + " Mobile/5F136 Safari/525.20.1";
+            "Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_0 like Mac OS X; en-us)"
+            + " AppleWebKit/528.18 (KHTML, like Gecko) Version/4.0"
+            + " Mobile/7A341 Safari/528.16";
     private static Locale sLocale;
     private static Object sLockForLocaleSettings;
     
@@ -246,9 +266,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);
 
@@ -456,6 +477,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/WebTextView.java b/core/java/android/webkit/WebTextView.java
index 4a8fa3c..d1904b7 100644
--- a/core/java/android/webkit/WebTextView.java
+++ b/core/java/android/webkit/WebTextView.java
@@ -24,13 +24,15 @@
 import android.text.Spannable;
 import android.text.TextUtils;
 import android.text.method.MovementMethod;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputMethodManager;
+import android.util.Log;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.InputConnection;
 import android.widget.AbsoluteLayout.LayoutParams;
 import android.widget.ArrayAdapter;
 import android.widget.AutoCompleteTextView;
@@ -45,6 +47,8 @@
  */
 /* package */ class WebTextView extends AutoCompleteTextView {
 
+    static final String LOGTAG = "webtextview";
+
     private WebView         mWebView;
     private boolean         mSingle;
     private int             mWidthSpec;
@@ -82,6 +86,7 @@
         setImeOptions(EditorInfo.IME_ACTION_NONE);
         // Allow webkit's drawing to show through
         setWillNotDraw(true);
+        setCursorVisible(false);
     }
 
     @Override
@@ -265,9 +270,25 @@
         return ptr == mNodePointer;
     }
 
+    @Override public InputConnection onCreateInputConnection(
+            EditorInfo outAttrs) {
+        InputConnection connection = super.onCreateInputConnection(outAttrs);
+        if (mWebView != null) {
+            // Use the name of the textfield + the url.  Use backslash as an
+            // arbitrary separator.
+            outAttrs.fieldName = mWebView.nativeFocusCandidateName() + "\\"
+                    + mWebView.getUrl();
+        }
+        return connection;
+    }
+
     @Override
     protected void onSelectionChanged(int selStart, int selEnd) {
         if (mWebView != null) {
+            if (DebugFlags.WEB_TEXT_VIEW) {
+                Log.v(LOGTAG, "onSelectionChanged selStart=" + selStart
+                        + " selEnd=" + selEnd);
+            }
             mWebView.setSelection(selStart, selEnd);
         }
     }
@@ -313,6 +334,10 @@
         } else {
             // This corrects the selection which may have been affected by the
             // trackball or auto-correct.
+            if (DebugFlags.WEB_TEXT_VIEW) {
+                Log.v(LOGTAG, "onTextChanged start=" + start
+                        + " start + before=" + (start + before));
+            }
             mWebView.setSelection(start, start + before);
         }
         if (!cannotUseKeyEvents) {
@@ -542,6 +567,10 @@
         } else if (start > length) {
             start = length;
         }
+        if (DebugFlags.WEB_TEXT_VIEW) {
+            Log.v(LOGTAG, "setText start=" + start
+                    + " end=" + end);
+        }
         Selection.setSelection(span, start, end);
     }
 
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 186e1d1..8f50247 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -329,9 +329,18 @@
 
     /**
      * The minimum elapsed time before sending another ACTION_MOVE event to
-     * WebViewCore
+     * WebViewCore. This really should be tuned for each type of the devices.
+     * For example in Google Map api test case, it takes Dream device at least
+     * 150ms to do a full cycle in the WebViewCore by processing a touch event,
+     * triggering the layout and drawing the picture. While the same process
+     * takes 60+ms on the current high speed device. If we make
+     * TOUCH_SENT_INTERVAL too small, there will be multiple touch events sent
+     * to WebViewCore queue and the real layout and draw events will be pushed
+     * to further, which slows down the refresh rate. Choose 50 to favor the
+     * current high speed devices. For Dream like devices, 100 is a better
+     * choice. Maybe make this in the buildspec later.
      */
-    private static final int TOUCH_SENT_INTERVAL = 100;
+    private static final int TOUCH_SENT_INTERVAL = 50;
 
     /**
      * Helper class to get velocity for fling
@@ -449,6 +458,8 @@
     static final int WEBCORE_INITIALIZED_MSG_ID         = 16;
     static final int UPDATE_TEXTFIELD_TEXT_MSG_ID       = 17;
     static final int DID_FIRST_LAYOUT_MSG_ID            = 18;
+    static final int MOVE_OUT_OF_PLUGIN                 = 19;
+    static final int CLEAR_TEXT_ENTRY                   = 20;
 
     static final int UPDATE_CLIPBOARD                   = 22;
     static final int LONG_PRESS_CENTER                  = 23;
@@ -476,8 +487,8 @@
         "WEBCORE_INITIALIZED_MSG_ID", //     = 16;
         "UPDATE_TEXTFIELD_TEXT_MSG_ID", //   = 17;
         "DID_FIRST_LAYOUT_MSG_ID", //        = 18;
-        "19",
-        "20",
+        "MOVE_OUT_OF_PLUGIN", //             = 19;
+        "CLEAR_TEXT_ENTRY", //               = 20;
         "21", //                             = 21;
         "UPDATE_CLIPBOARD", //               = 22;
         "LONG_PRESS_CENTER", //              = 23;
@@ -502,7 +513,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;
@@ -745,7 +756,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;
@@ -754,6 +765,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;
@@ -2318,6 +2346,16 @@
     }
 
     /**
+     * Gets the chrome handler.
+     * @return the current WebChromeClient instance.
+     *
+     * @hide API council approval.
+     */
+    public WebChromeClient getWebChromeClient() {
+        return mCallbackProxy.getWebChromeClient();
+    }
+
+    /**
      * Set the Picture listener. This is an interface used to receive
      * notifications of a new Picture.
      * @param listener An implementation of WebView.PictureListener.
@@ -3072,6 +3110,9 @@
             mWebTextView.setInPassword(nativeFocusCandidateIsPassword());
             if (null == text) {
                 mWebTextView.setText("", 0, 0);
+                if (DebugFlags.WEB_VIEW) {
+                    Log.v(LOGTAG, "rebuildWebTextView null == text");
+                }
             } else {
                 // Change to true to enable the old style behavior, where
                 // entering a textfield/textarea always set the selection to the
@@ -3086,8 +3127,14 @@
                 } else if (isTextField) {
                     int length = text.length();
                     mWebTextView.setText(text, length, length);
+                    if (DebugFlags.WEB_VIEW) {
+                        Log.v(LOGTAG, "rebuildWebTextView length=" + length);
+                    }
                 } else {
                     mWebTextView.setText(text, 0, 0);
+                    if (DebugFlags.WEB_VIEW) {
+                        Log.v(LOGTAG, "rebuildWebTextView !isTextField");
+                    }
                 }
             }
             mWebTextView.requestFocus();
@@ -3129,7 +3176,7 @@
     public boolean onKeyDown(int keyCode, KeyEvent event) {
         if (DebugFlags.WEB_VIEW) {
             Log.v(LOGTAG, "keyDown at " + System.currentTimeMillis()
-                    + ", " + event);
+                    + ", " + event + ", unicode=" + event.getUnicodeChar());
         }
 
         if (mNativeClass == 0) {
@@ -3175,7 +3222,7 @@
                 && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
             // always handle the navigation keys in the UI thread
             switchOutDrawHistory();
-            if (navHandledKey(keyCode, 1, false, event.getEventTime())) {
+            if (navHandledKey(keyCode, 1, false, event.getEventTime(), false)) {
                 playSoundEffect(keyCodeToSoundsEffect(keyCode));
                 return true;
             }
@@ -3235,17 +3282,18 @@
             }
         }
 
-        if (nativeCursorWantsKeyEvents() && !nativeCursorMatchesFocus()) {
+        if (nativeCursorIsPlugin()) {
+            nativeUpdatePluginReceivesEvents();
+            invalidate();
+        } else if (nativeCursorIsTextInput()) {
             // This message will put the node in focus, for the DOM's notion
-            // of focus
+            // of focus, and make the focuscontroller active
             mWebViewCore.sendMessage(EventHub.CLICK);
-            if (nativeCursorIsTextInput()) {
-                // This will bring up the WebTextView and put it in focus, for
-                // our view system's notion of focus
-                rebuildWebTextView();
-                // Now we need to pass the event to it
-                return mWebTextView.onKeyDown(keyCode, event);
-            }
+            // This will bring up the WebTextView and put it in focus, for
+            // our view system's notion of focus
+            rebuildWebTextView();
+            // Now we need to pass the event to it
+            return mWebTextView.onKeyDown(keyCode, event);
         }
 
         // TODO: should we pass all the keys to DOM or check the meta tag
@@ -3264,7 +3312,7 @@
     public boolean onKeyUp(int keyCode, KeyEvent event) {
         if (DebugFlags.WEB_VIEW) {
             Log.v(LOGTAG, "keyUp at " + System.currentTimeMillis()
-                    + ", " + event);
+                    + ", " + event + ", unicode=" + event.getUnicodeChar());
         }
 
         if (mNativeClass == 0) {
@@ -4162,7 +4210,7 @@
                         + " mTrackballRemainsX=" + mTrackballRemainsX
                         + " mTrackballRemainsY=" + mTrackballRemainsY);
             }
-            if (navHandledKey(selectKeyCode, count, false, time)) {
+            if (navHandledKey(selectKeyCode, count, false, time, false)) {
                 playSoundEffect(keyCodeToSoundsEffect(selectKeyCode));
             }
             mTrackballRemainsX = mTrackballRemainsY = 0;
@@ -4238,8 +4286,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);
@@ -4444,7 +4492,7 @@
                         return result;
                 }
                 if (mNativeClass != 0 && !nativeHasCursorNode()) {
-                    navHandledKey(fakeKeyDirection, 1, true, 0);
+                    navHandledKey(fakeKeyDirection, 1, true, 0, true);
                 }
             }
         }
@@ -4622,9 +4670,11 @@
                     break;
                 }
                 case SWITCH_TO_LONGPRESS: {
-                    mTouchMode = TOUCH_DONE_MODE;
-                    performLongClick();
-                    rebuildWebTextView();
+                    if (!mPreventDrag) {
+                        mTouchMode = TOUCH_DONE_MODE;
+                        performLongClick();
+                        rebuildWebTextView();
+                    }
                     break;
                 }
                 case SWITCH_TO_CLICK:
@@ -4639,13 +4689,15 @@
                         break;
                     }
                     nativeSetFollowedLink(true);
-                    mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE,
-                            cursorData());
+                    nativeUpdatePluginReceivesEvents();
+                    WebViewCore.CursorData data = cursorData();
+                    mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE, data);
                     playSoundEffect(SoundEffectConstants.CLICK);
                     boolean isTextInput = nativeCursorIsTextInput();
                     if (isTextInput || !mCallbackProxy.uiOverrideUrlLoading(
                                 nativeCursorText())) {
-                        mWebViewCore.sendMessage(EventHub.CLICK);
+                        mWebViewCore.sendMessage(EventHub.CLICK, data.mFrame,
+                                nativeCursorNodePointer());
                     }
                     if (isTextInput) {
                         rebuildWebTextView();
@@ -4769,7 +4821,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  {
@@ -4791,6 +4843,11 @@
                     }
                     setNewZoomScale(scale, false);
                     break;
+                case MOVE_OUT_OF_PLUGIN:
+                    if (nativePluginEatsNavKey()) {
+                        navHandledKey(msg.arg1, 1, false, 0, true);
+                    }
+                    break;
                 case UPDATE_TEXT_ENTRY_MSG_ID:
                     // this is sent after finishing resize in WebViewCore. Make
                     // sure the text edit box is still on the  screen.
@@ -4799,6 +4856,9 @@
                     }
                     rebuildWebTextView();
                     break;
+                case CLEAR_TEXT_ENTRY:
+                    clearTextEntry();
+                    break;
                 case INVAL_RECT_MSG_ID: {
                     Rect r = (Rect)msg.obj;
                     if (r == null) {
@@ -5118,12 +5178,23 @@
                 new WebViewCore.CursorData(frame, node, x, y));
     }
 
-    // called by JNI
-    private void sendMoveMouseIfLatest(boolean setFocusControllerInactive) {
-        if (setFocusControllerInactive) {
+    /*
+     * Send a mouse move event to the webcore thread.
+     *
+     * @param removeFocus Pass true if the "mouse" cursor is now over a node
+     *                    which wants key events, but it is not the focus. This
+     *                    will make the visual appear as though nothing is in
+     *                    focus.  Remove the WebTextView, if present, and stop
+     *                    drawing the blinking caret.
+     * called by JNI
+     */
+    private void sendMoveMouseIfLatest(boolean removeFocus) {
+        if (removeFocus) {
+            clearTextEntry();
             setFocusControllerInactive();
         }
-        mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE_IF_LATEST, cursorData());
+        mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE_IF_LATEST,
+                cursorData());
     }
 
     // called by JNI
@@ -5176,11 +5247,21 @@
     }
 
     // return true if the key was handled
-    private boolean navHandledKey(int keyCode, int count, boolean noScroll
-            , long time) {
+    private boolean navHandledKey(int keyCode, int count, boolean noScroll,
+            long time, boolean ignorePlugin) {
         if (mNativeClass == 0) {
             return false;
         }
+        if (ignorePlugin == false && nativePluginEatsNavKey()) {
+            KeyEvent event = new KeyEvent(time, time, KeyEvent.ACTION_DOWN
+                , keyCode, count, (mShiftIsPressed ? KeyEvent.META_SHIFT_ON : 0)
+                | (false ? KeyEvent.META_ALT_ON : 0) // FIXME
+                | (false ? KeyEvent.META_SYM_ON : 0) // FIXME
+                , 0, 0, 0);
+            mWebViewCore.sendMessage(EventHub.KEY_DOWN, event);
+            mWebViewCore.sendMessage(EventHub.KEY_UP, event);
+            return true;
+        }
         mLastCursorTime = time;
         mLastCursorBounds = nativeGetCursorRingBounds();
         boolean keyHandled
@@ -5263,6 +5344,7 @@
     /* package */ native boolean nativeCursorMatchesFocus();
     private native boolean  nativeCursorIntersects(Rect visibleRect);
     private native boolean  nativeCursorIsAnchor();
+    private native boolean  nativeCursorIsPlugin();
     private native boolean  nativeCursorIsTextInput();
     private native Point    nativeCursorPosition();
     private native String   nativeCursorText();
@@ -5286,7 +5368,7 @@
     private native boolean  nativeFocusCandidateIsTextField();
     private native boolean  nativeFocusCandidateIsTextInput();
     private native int      nativeFocusCandidateMaxLength();
-    private native String   nativeFocusCandidateName();
+    /* package */ native String   nativeFocusCandidateName();
     private native Rect     nativeFocusCandidateNodeBounds();
     /* package */ native int nativeFocusCandidatePointer();
     private native String   nativeFocusCandidateText();
@@ -5306,6 +5388,7 @@
     private native int      nativeMoveGeneration();
     private native void     nativeMoveSelection(int x, int y,
             boolean extendSelection);
+    private native boolean  nativePluginEatsNavKey();
     // Like many other of our native methods, you must make sure that
     // mNativeClass is not null before calling this method.
     private native void     nativeRecordButtons(boolean focused,
@@ -5319,5 +5402,5 @@
     // we always want to pass in our generation number.
     private native void     nativeUpdateCachedTextfield(String updatedText,
             int generation);
-
+    private native void     nativeUpdatePluginReceivesEvents();
 }
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 4ad9a1a..b75ba941 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -136,7 +136,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();
@@ -346,9 +346,10 @@
     private native void nativeSplitContent();
 
     private native boolean nativeKey(int keyCode, int unichar,
-            int repeatCount, boolean isShift, boolean isAlt, boolean isDown);
+            int repeatCount, boolean isShift, boolean isAlt, boolean isSym,
+            boolean isDown);
 
-    private native boolean nativeClick();
+    private native void nativeClick(int framePtr, int nodePtr);
 
     private native void nativeSendListBoxChoices(boolean[] choices, int size);
 
@@ -719,9 +720,11 @@
                 @Override
                 public void handleMessage(Message msg) {
                     if (DebugFlags.WEB_VIEW_CORE) {
-                        Log.v(LOGTAG, msg.what < LOAD_URL || msg.what
+                        Log.v(LOGTAG, (msg.what < LOAD_URL || msg.what
                                 > FREE_MEMORY ? Integer.toString(msg.what)
-                                : HandlerDebugString[msg.what - LOAD_URL]);
+                                : HandlerDebugString[msg.what - LOAD_URL])
+                                + " arg1=" + msg.arg1 + " arg2=" + msg.arg2
+                                + " obj=" + msg.obj);
                     }
                     switch (msg.what) {
                         case WEBKIT_DRAW:
@@ -803,7 +806,7 @@
                             break;
 
                         case CLICK:
-                            nativeClick();
+                            nativeClick(msg.arg1, msg.arg2);
                             break;
 
                         case VIEW_SIZE_CHANGED:
@@ -1260,7 +1263,19 @@
         int keyCode = evt.getKeyCode();
         if (!nativeKey(keyCode, evt.getUnicodeChar(),
                 evt.getRepeatCount(), evt.isShiftPressed(), evt.isAltPressed(),
+                evt.isSymPressed(),
                 isDown) && keyCode != KeyEvent.KEYCODE_ENTER) {
+            if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
+                    && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
+                if (DebugFlags.WEB_VIEW_CORE) {
+                    Log.v(LOGTAG, "key: arrow unused by plugin: " + keyCode);
+                }
+                if (mWebView != null && evt.isDown()) {
+                    Message.obtain(mWebView.mPrivateHandler,
+                            WebView.MOVE_OUT_OF_PLUGIN, keyCode).sendToTarget();
+                }
+                return;
+            }
             // bubble up the event handling
             // but do not bubble up the ENTER key, which would open the search
             // bar without any text.
@@ -1618,6 +1633,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) {
@@ -1722,6 +1751,13 @@
         }
     }
 
+    // called by JNI
+    private void clearTextEntry() {
+        if (mWebView == null) return;
+        Message.obtain(mWebView.mPrivateHandler,
+                WebView.CLEAR_TEXT_ENTRY).sendToTarget();
+    }
+
     // these must be in document space (i.e. not scaled/zoomed).
     private native void nativeSetScrollOffset(int gen, int dx, int dy);
 
diff --git a/core/java/android/webkit/WebViewDatabase.java b/core/java/android/webkit/WebViewDatabase.java
index 1004e30..e6d89e3 100644
--- a/core/java/android/webkit/WebViewDatabase.java
+++ b/core/java/android/webkit/WebViewDatabase.java
@@ -48,7 +48,8 @@
     // 6 -> 7 Change cache localPath from int to String
     // 7 -> 8 Move cache to its own db
     // 8 -> 9 Store both scheme and host when storing passwords
-    private static final int CACHE_DATABASE_VERSION = 1;
+    private static final int CACHE_DATABASE_VERSION = 2;
+    // 1 -> 2 Add expires String
 
     private static WebViewDatabase mInstance = null;
 
@@ -107,6 +108,8 @@
 
     private static final String CACHE_EXPIRES_COL = "expires";
 
+    private static final String CACHE_EXPIRES_STRING_COL = "expiresstring";
+
     private static final String CACHE_MIMETYPE_COL = "mimetype";
 
     private static final String CACHE_ENCODING_COL = "encoding";
@@ -150,6 +153,7 @@
     private static int mCacheLastModifyColIndex;
     private static int mCacheETagColIndex;
     private static int mCacheExpiresColIndex;
+    private static int mCacheExpiresStringColIndex;
     private static int mCacheMimeTypeColIndex;
     private static int mCacheEncodingColIndex;
     private static int mCacheHttpStatusColIndex;
@@ -220,6 +224,8 @@
                         .getColumnIndex(CACHE_ETAG_COL);
                 mCacheExpiresColIndex = mCacheInserter
                         .getColumnIndex(CACHE_EXPIRES_COL);
+                mCacheExpiresStringColIndex = mCacheInserter
+                        .getColumnIndex(CACHE_EXPIRES_STRING_COL);
                 mCacheMimeTypeColIndex = mCacheInserter
                         .getColumnIndex(CACHE_MIMETYPE_COL);
                 mCacheEncodingColIndex = mCacheInserter
@@ -320,6 +326,7 @@
                     + " TEXT, " + CACHE_FILE_PATH_COL + " TEXT, "
                     + CACHE_LAST_MODIFY_COL + " TEXT, " + CACHE_ETAG_COL
                     + " TEXT, " + CACHE_EXPIRES_COL + " INTEGER, "
+                    + CACHE_EXPIRES_STRING_COL + " TEXT, "
                     + CACHE_MIMETYPE_COL + " TEXT, " + CACHE_ENCODING_COL
                     + " TEXT," + CACHE_HTTP_STATUS_COL + " INTEGER, "
                     + CACHE_LOCATION_COL + " TEXT, " + CACHE_CONTENTLENGTH_COL
@@ -537,7 +544,7 @@
         }
 
         Cursor cursor = mCacheDatabase.rawQuery("SELECT filepath, lastmodify, etag, expires, "
-                    + "mimetype, encoding, httpstatus, location, contentlength "
+                    + "expiresstring, mimetype, encoding, httpstatus, location, contentlength "
                     + "FROM cache WHERE url = ?",
                 new String[] { url });
 
@@ -548,11 +555,12 @@
                 ret.lastModified = cursor.getString(1);
                 ret.etag = cursor.getString(2);
                 ret.expires = cursor.getLong(3);
-                ret.mimeType = cursor.getString(4);
-                ret.encoding = cursor.getString(5);
-                ret.httpStatusCode = cursor.getInt(6);
-                ret.location = cursor.getString(7);
-                ret.contentLength = cursor.getLong(8);
+                ret.expiresString = cursor.getString(4);
+                ret.mimeType = cursor.getString(5);
+                ret.encoding = cursor.getString(6);
+                ret.httpStatusCode = cursor.getInt(7);
+                ret.location = cursor.getString(8);
+                ret.contentLength = cursor.getLong(9);
                 return ret;
             }
         } finally {
@@ -591,6 +599,7 @@
         mCacheInserter.bind(mCacheLastModifyColIndex, c.lastModified);
         mCacheInserter.bind(mCacheETagColIndex, c.etag);
         mCacheInserter.bind(mCacheExpiresColIndex, c.expires);
+        mCacheInserter.bind(mCacheExpiresStringColIndex, c.expiresString);
         mCacheInserter.bind(mCacheMimeTypeColIndex, c.mimeType);
         mCacheInserter.bind(mCacheEncodingColIndex, c.encoding);
         mCacheInserter.bind(mCacheHttpStatusColIndex, c.httpStatusCode);
diff --git a/core/java/android/webkit/gears/ApacheHttpRequestAndroid.java b/core/java/android/webkit/gears/ApacheHttpRequestAndroid.java
index 74d27ed..b3d7f69 100644
--- a/core/java/android/webkit/gears/ApacheHttpRequestAndroid.java
+++ b/core/java/android/webkit/gears/ApacheHttpRequestAndroid.java
@@ -38,7 +38,6 @@
 import java.io.OutputStream;
 import java.io.IOException;
 import java.lang.StringBuilder;
-import java.util.Date;
 import java.util.Map;
 import java.util.HashMap;
 import java.util.Iterator;
@@ -57,7 +56,6 @@
 import org.apache.http.impl.client.DefaultHttpClient;
 import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
 import org.apache.http.conn.ssl.StrictHostnameVerifier;
-import org.apache.http.impl.cookie.DateUtils;
 import org.apache.http.util.CharArrayBuffer;
 
 import java.util.concurrent.locks.Condition;
@@ -863,12 +861,9 @@
         mResponseHeaders = new HashMap<String, String[]>();
         String contentLength = Long.toString(cacheResult.getContentLength());
         setResponseHeader(KEY_CONTENT_LENGTH, contentLength);
-        long expires = cacheResult.getExpires();
-        if (expires >= 0) {
-            // "Expires" header is valid and finite. Milliseconds since 1970
-            // epoch, formatted as RFC-1123.
-            String expiresString = DateUtils.formatDate(new Date(expires));
-            setResponseHeader(KEY_EXPIRES, expiresString);
+        String expires = cacheResult.getExpiresString();
+        if (expires != null) {
+            setResponseHeader(KEY_EXPIRES, expires);
         }
         String lastModified = cacheResult.getLastModified();
         if (lastModified != null) {
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index bd6edfb..0c2cd55 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -990,7 +990,7 @@
         
         int bottomEdge = displayFrame.bottom;
         if (ignoreBottomDecorations) {
-            bottomEdge = WindowManagerImpl.getDefault().getDefaultDisplay().getHeight();
+            bottomEdge = anchor.getContext().getResources().getDisplayMetrics().heightPixels;
         }
         final int distanceToBottom = bottomEdge - (anchorPos[1] + anchor.getHeight()) - yOffset;
         final int distanceToTop = anchorPos[1] - displayFrame.top + yOffset;
diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java
index 955475e4..12bb01c 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(MeasureSpec.EXACTLY, myHeight);
+        } else {
+            childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+        }
         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);
             }
 
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 9479f9e..d8ed4f0 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -3681,12 +3681,13 @@
 
     @Override
     protected boolean isPaddingOffsetRequired() {
-        return mShadowRadius != 0;
+        return mShadowRadius != 0 || mDrawables != null;
     }
 
     @Override
     protected int getLeftPaddingOffset() {
-        return (int) Math.min(0, mShadowDx - mShadowRadius);
+        return getCompoundPaddingLeft() - mPaddingLeft +
+                (int) Math.min(0, mShadowDx - mShadowRadius);
     }
 
     @Override
@@ -3701,7 +3702,8 @@
 
     @Override
     protected int getRightPaddingOffset() {
-        return (int) Math.max(0, mShadowDx + mShadowRadius);
+        return -(getCompoundPaddingRight() - mPaddingRight) +
+                (int) Math.max(0, mShadowDx + mShadowRadius);
     }
 
     @Override
@@ -6665,9 +6667,10 @@
             } else if (getLineCount() == 1) {
                 switch (mGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
                     case Gravity.LEFT:
-                        return (mLayout.getLineRight(0) - mScrollX - (mRight - mLeft) -
-                                getCompoundPaddingLeft() - getCompoundPaddingRight()) /
-                                getHorizontalFadingEdgeLength();
+                        final int textWidth = (mRight - mLeft) - getCompoundPaddingLeft() -
+                                getCompoundPaddingRight();
+                        final float lineWidth = mLayout.getLineWidth(0);
+                        return (lineWidth - textWidth) / getHorizontalFadingEdgeLength();
                     case Gravity.RIGHT:
                         return 0.0f;
                     case Gravity.CENTER_HORIZONTAL:
diff --git a/core/java/com/android/internal/backup/IBackupTransport.aidl b/core/java/com/android/internal/backup/IBackupTransport.aidl
index 84ed729..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.
@@ -54,63 +68,70 @@
     long requestBackupTime();
 
     /**
-     * Establish a connection to the back-end data repository, if necessary.  If the transport
-     * needs to initialize state that is not tied to individual applications' backup operations,
-     * this is where it should be done.
-     *
-     * @return Zero on success; a nonzero error code on failure.
-     */
-    int startSession();
-
-    /**
-     * Send one application's data to the backup destination.
+     * Send one application's data to the backup destination.  The transport may send
+     * the data immediately, or may buffer it.  After this is called, {@link #finishBackup}
+     * must be called to ensure the data is sent and recorded successfully.
      *
      * @param packageInfo The identity of the application whose data is being backed up.
      *   This specifically includes the signature list for the package.
      * @param data The data stream that resulted from invoking the application's
      *   BackupService.doBackup() method.  This may be a pipe rather than a file on
      *   persistent media, so it may not be seekable.
-     * @return Zero on success; a nonzero error code on failure.
+     * @return false if errors occurred (the backup should be aborted and rescheduled),
+     *   true if everything is OK so far (but {@link #finishBackup} must be called).
      */
-    int performBackup(in PackageInfo packageInfo, in ParcelFileDescriptor data);
+    boolean performBackup(in PackageInfo packageInfo, in ParcelFileDescriptor inFd);
+
+    /**
+     * Finish sending application data to the backup destination.  This must be
+     * called after {@link #performBackup} to ensure that all data is sent.  Only
+     * when this method returns true can the backup be assumed to have succeeded.
+     *
+     * @return false if errors occurred (the backup should be aborted and rescheduled),
+     *   true if everything is OK so far (but {@link #finishBackup} must be called).
+     */
+    boolean finishBackup();
 
     /**
      * Get the set of backups currently available over this transport.
      *
-     * @return Descriptions of the set of restore images available for this device.
+     * @return Descriptions of the set of restore images available for this device,
+     *   or null if an error occurred (the attempt should be rescheduled).
      **/
     RestoreSet[] getAvailableRestoreSets();
 
     /**
-     * Get the set of applications from a given restore image.
+     * Start restoring application data from backup.  After calling this function,
+     * alternate calls to {@link #nextRestorePackage} and {@link #nextRestoreData}
+     * to walk through the actual application data.
      *
      * @param token A backup token as returned by {@link #getAvailableRestoreSets}.
-     * @return An array of PackageInfo objects describing all of the applications
-     *   available for restore from this restore image.  This should include the list
-     *   of signatures for each package so that the Backup Manager can filter using that
-     *   information.
+     * @param packages List of applications to restore (if data is available).
+     *   Application data will be restored in the order given.
+     * @return false if errors occurred (the restore should be aborted and rescheduled),
+     *   true if everything is OK so far (go ahead and call {@link #nextRestorePackage}).
      */
-    PackageInfo[] getAppSet(int token);
+    boolean startRestore(long token, in PackageInfo[] packages);
 
     /**
-     * Retrieve one application's data from the backing store.
-     *
-     * @param token The backup record from which a restore is being requested.
-     * @param packageInfo The identity of the application whose data is being restored.
-     *   This must include the signature list for the package; it is up to the transport
-     *   to verify that the requested app's signatures match the saved backup record
-     *   because the transport cannot necessarily trust the client device.
-     * @param data An open, writable file into which the backup image should be stored.
-     * @return Zero on success; a nonzero error code on failure.
+     * Get the package name of the next application with data in the backup store.
+     * @return The name of one of the packages supplied to {@link #startRestore},
+     *   or "" (the empty string) if no more backup data is available,
+     *   or null if an error occurred (the restore should be aborted and rescheduled).
      */
-    int getRestoreData(int token, in PackageInfo packageInfo, in ParcelFileDescriptor data);
+    String nextRestorePackage();
 
     /**
-     * Terminate the backup session, closing files, freeing memory, and cleaning up whatever
-     * other state the transport required.
-     *
-     * @return Zero on success; a nonzero error code on failure.  Even on failure, the session
-     *         is torn down and must be restarted if another backup is attempted.
+     * Get the data for the application returned by {@link #nextRestorePackage}.
+     * @param data An open, writable file into which the backup data should be stored.
+     * @return false if errors occurred (the restore should be aborted and rescheduled),
+     *   true if everything is OK so far (go ahead and call {@link #nextRestorePackage}).
      */
-    int endSession();
+    boolean getRestoreData(in ParcelFileDescriptor outFd);
+
+    /**
+     * End a restore session (aborting any in-process data transfer as necessary),
+     * freeing any resources and connections used during the restore process.
+     */
+    void finishRestore();
 }
diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java
index 3dd71f3..c5d9d403 100644
--- a/core/java/com/android/internal/backup/LocalTransport.java
+++ b/core/java/com/android/internal/backup/LocalTransport.java
@@ -30,14 +30,14 @@
     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");
-    private FileFilter mDirFileFilter = new FileFilter() {
-        public boolean accept(File f) {
-            return f.isDirectory();
-        }
-    };
+    private PackageInfo[] mRestorePackages = null;
+    private int mRestorePackage = -1;  // Index into mRestorePackages
 
 
     public LocalTransport(Context context) {
@@ -46,26 +46,19 @@
         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;
     }
 
-    public int startSession() throws RemoteException {
-        if (DEBUG) Log.v(TAG, "session started");
-        mDataDir.mkdirs();
-        return 0;
-    }
-
-    public int endSession() throws RemoteException {
-        if (DEBUG) Log.v(TAG, "session ended");
-        return 0;
-    }
-
-    public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor data)
+    public boolean performBackup(PackageInfo packageInfo, ParcelFileDescriptor data)
             throws RemoteException {
         if (DEBUG) Log.v(TAG, "performBackup() pkg=" + packageInfo.packageName);
-        int err = 0;
 
         File packageDir = new File(mDataDir, packageInfo.packageName);
         packageDir.mkdirs();
@@ -101,9 +94,8 @@
                     try {
                         entity.write(buf, 0, dataSize);
                     } catch (IOException e) {
-                        Log.e(TAG, "Unable to update key file "
-                                + entityFile.getAbsolutePath());
-                        err = -1;
+                        Log.e(TAG, "Unable to update key file " + entityFile.getAbsolutePath());
+                        return false;
                     } finally {
                         entity.close();
                     }
@@ -111,14 +103,17 @@
                     entityFile.delete();
                 }
             }
+            return true;
         } catch (IOException e) {
             // oops, something went wrong.  abort the operation and return error.
-            Log.v(TAG, "Exception reading backup input:");
-            e.printStackTrace();
-            err = -1;
+            Log.v(TAG, "Exception reading backup input:", e);
+            return false;
         }
+    }
 
-        return err;
+    public boolean finishBackup() throws RemoteException {
+        if (DEBUG) Log.v(TAG, "finishBackup()");
+        return true;
     }
 
     // Restore handling
@@ -129,72 +124,66 @@
         return array;
     }
 
-    public PackageInfo[] getAppSet(int token) throws android.os.RemoteException {
-        if (DEBUG) Log.v(TAG, "getting app set " + token);
-        // the available packages are the extant subdirs of mDatadir
-        File[] packageDirs = mDataDir.listFiles(mDirFileFilter);
-        ArrayList<PackageInfo> packages = new ArrayList<PackageInfo>();
-        for (File dir : packageDirs) {
-            try {
-                PackageInfo pkg = mPackageManager.getPackageInfo(dir.getName(),
-                        PackageManager.GET_SIGNATURES);
-                if (pkg != null) {
-                    packages.add(pkg);
-                }
-            } catch (NameNotFoundException e) {
-                // restore set contains data for a package not installed on the
-                // phone -- just ignore it.
-            }
-        }
-
-        if (DEBUG) {
-            Log.v(TAG, "Built app set of " + packages.size() + " entries:");
-            for (PackageInfo p : packages) {
-                Log.v(TAG, "    + " + p.packageName);
-            }
-        }
-
-        PackageInfo[] result = new PackageInfo[packages.size()];
-        return packages.toArray(result);
+    public boolean startRestore(long token, PackageInfo[] packages) {
+        if (DEBUG) Log.v(TAG, "start restore " + token);
+        mRestorePackages = packages;
+        mRestorePackage = -1;
+        return true;
     }
 
-    public int getRestoreData(int token, PackageInfo packageInfo, ParcelFileDescriptor outFd)
-            throws android.os.RemoteException {
-        if (DEBUG) Log.v(TAG, "getting restore data " + token + " : " + packageInfo.packageName);
-        // we only support one hardcoded restore set
-        if (token != 0) return -1;
+    public String nextRestorePackage() {
+        if (mRestorePackages == null) throw new IllegalStateException("startRestore not called");
+        while (++mRestorePackage < mRestorePackages.length) {
+            String name = mRestorePackages[mRestorePackage].packageName;
+            if (new File(mDataDir, name).isDirectory()) {
+                if (DEBUG) Log.v(TAG, "  nextRestorePackage() = " + name);
+                return name;
+            }
+        }
 
-        // the data for a given package is at a known location
-        File packageDir = new File(mDataDir, packageInfo.packageName);
+        if (DEBUG) Log.v(TAG, "  no more packages to restore");
+        return "";
+    }
+
+    public boolean getRestoreData(ParcelFileDescriptor outFd) {
+        if (mRestorePackages == null) throw new IllegalStateException("startRestore not called");
+        if (mRestorePackage < 0) throw new IllegalStateException("nextRestorePackage not called");
+        File packageDir = new File(mDataDir, mRestorePackages[mRestorePackage].packageName);
 
         // The restore set is the concatenation of the individual record blobs,
         // each of which is a file in the package's directory
         File[] blobs = packageDir.listFiles();
-        if (DEBUG) Log.v(TAG, "   found " + blobs.length + " key files");
-        int err = 0;
-        if (blobs != null && blobs.length > 0) {
-            BackupDataOutput out = new BackupDataOutput(outFd.getFileDescriptor());
-            try {
-                for (File f : blobs) {
-                    copyToRestoreData(f, out);
-                }
-            } catch (Exception e) {
-                Log.e(TAG, "Unable to read backup records");
-                err = -1;
-            }
+        if (blobs == null) {
+            Log.e(TAG, "Error listing directory: " + packageDir);
+            return false;  // nextRestorePackage() ensures the dir exists, so this is an error
         }
-        return err;
+
+        // We expect at least some data if the directory exists in the first place
+        if (DEBUG) Log.v(TAG, "  getRestoreData() found " + blobs.length + " key files");
+        BackupDataOutput out = new BackupDataOutput(outFd.getFileDescriptor());
+        try {
+            for (File f : blobs) {
+                FileInputStream in = new FileInputStream(f);
+                try {
+                    int size = (int) f.length();
+                    byte[] buf = new byte[size];
+                    in.read(buf);
+                    String key = new String(Base64.decode(f.getName()));
+                    if (DEBUG) Log.v(TAG, "    ... key=" + key + " size=" + size);
+                    out.writeEntityHeader(key, size);
+                    out.writeEntityData(buf, size);
+                } finally {
+                    in.close();
+                }
+            }
+            return true;
+        } catch (IOException e) {
+            Log.e(TAG, "Unable to read backup records", e);
+            return false;
+        }
     }
 
-    private void copyToRestoreData(File f, BackupDataOutput out) throws IOException {
-        FileInputStream in = new FileInputStream(f);
-        int size = (int) f.length();
-        byte[] buf = new byte[size];
-        in.read(buf);
-        String key = new String(Base64.decode(f.getName()));
-        if (DEBUG) Log.v(TAG, "   ... copy to stream: key=" + key
-                + " size=" + size);
-        out.writeEntityHeader(key, size);
-        out.writeEntityData(buf, size);
+    public void finishRestore() {
+        if (DEBUG) Log.v(TAG, "finishRestore()");
     }
 }
diff --git a/core/java/com/android/internal/backup/SystemBackupAgent.java b/core/java/com/android/internal/backup/SystemBackupAgent.java
new file mode 100644
index 0000000..6b396d7
--- /dev/null
+++ b/core/java/com/android/internal/backup/SystemBackupAgent.java
@@ -0,0 +1,35 @@
+/*
+ * 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.internal.backup;
+
+import android.backup.AbsoluteFileBackupHelper;
+import android.backup.BackupHelperAgent;
+
+/**
+ * Backup agent for various system-managed data
+ */
+public class SystemBackupAgent extends BackupHelperAgent {
+    // the set of files that we back up whole, as absolute paths
+    String[] mFiles = {
+            /* WallpaperService.WALLPAPER_FILE */
+            "/data/data/com.android.settings/files/wallpaper",
+            };
+
+    public void onCreate() {
+        addHelper("system_files", new AbsoluteFileBackupHelper(this, mFiles));
+    }
+}
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/jni/Android.mk b/core/jni/Android.mk
index 0ecf678..73eea15 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -23,6 +23,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 \
@@ -121,7 +122,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 f8a4df0..c322b17 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -156,6 +156,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;
 
@@ -1241,6 +1242,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..24d529b
--- /dev/null
+++ b/core/jni/android_backup_BackupHelperDispatcher.cpp
@@ -0,0 +1,251 @@
+/*
+ * 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 (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);
+
+    keyPrefix.unlockBuffer(flattenedHeader.nameLength);
+
+    remainingHeader -= flattenedHeader.nameLength;
+
+    LOGD("remainingHeader=%d", remainingHeader);
+
+    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;
+
+    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_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_Process.cpp b/core/jni/android_util_Process.cpp
index 1c9ee7d..770c755 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 0c90769..db31818 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1000,6 +1000,7 @@
                  android:hasCode="false"
                  android:label="@string/android_system_label"
                  android:allowClearUserData="false"
+                 android:backupAgent="com.android.internal.backup.SystemBackupAgent"
                  android:icon="@drawable/ic_launcher_android">
         <activity android:name="com.android.internal.app.ChooserActivity"
                 android:theme="@style/Theme.Dialog.Alert"
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 88464f7..612e6f4 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1325,7 +1325,7 @@
 
     <!-- Do not translate.  WebView User Agent string -->
     <string name="web_user_agent"><xliff:g id="x">Mozilla/5.0 (Linux; U; Android %s)
-        AppleWebKit/528.5+ (KHTML, like Gecko) Version/3.1.2 Mobile Safari/525.20.1</xliff:g></string>
+        AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0 Mobile Safari/530.17</xliff:g></string>
 
     <!-- Title for a JavaScript dialog. "The page at <url of current page> says:" -->
     <string name="js_dialog_title">The page at \'<xliff:g id="title">%s</xliff:g>\' says:</string>
diff --git a/include/tts/TtsEngine.h b/include/tts/TtsEngine.h
index 8486532..ca50a5e 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
diff --git a/libs/ui/Android.mk b/libs/ui/Android.mk
index c6fd070..3952c0e 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/location/java/com/android/internal/location/GpsLocationProvider.java b/location/java/com/android/internal/location/GpsLocationProvider.java
index edd1ea0..883e5f5 100755
--- a/location/java/com/android/internal/location/GpsLocationProvider.java
+++ b/location/java/com/android/internal/location/GpsLocationProvider.java
@@ -38,6 +38,7 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
+import android.provider.Settings;
 import android.util.Config;
 import android.util.Log;
 import android.util.SparseIntArray;
@@ -183,7 +184,7 @@
     // number of fixes we have received since we started navigating
     private int mFixCount;
 
-    private int mPositionMode = GPS_POSITION_MODE_STANDALONE;
+    private boolean mAgpsConfigured;
 
     // true if we started navigation
     private boolean mStarted;
@@ -355,8 +356,7 @@
                 try {
                     int port = Integer.parseInt(portString);
                     native_set_agps_server(AGPS_TYPE_SUPL, host, port);
-                    // use MS-Based position mode if SUPL support is enabled
-                    mPositionMode = GPS_POSITION_MODE_MS_BASED;
+                    mAgpsConfigured = true;
                 } catch (NumberFormatException e) {
                     Log.e(TAG, "unable to parse SUPL_PORT: " + portString);
                 }
@@ -368,8 +368,7 @@
                 try {
                     int port = Integer.parseInt(portString);
                     native_set_agps_server(AGPS_TYPE_C2K, host, port);
-                    // use MS-Based position mode if SUPL support is enabled
-                    mPositionMode = GPS_POSITION_MODE_MS_BASED;
+                    mAgpsConfigured = true;
                 } catch (NumberFormatException e) {
                     Log.e(TAG, "unable to parse C2K_PORT: " + portString);
                 }
@@ -719,7 +718,15 @@
         if (!mStarted) {
             if (DEBUG) Log.d(TAG, "startNavigating");
             mStarted = true;
-            if (!native_start(mPositionMode, false, mFixInterval)) {
+            int positionMode;
+            if (mAgpsConfigured && Settings.Secure.getInt(mContext.getContentResolver(),
+                    Settings.Secure.ASSISTED_GPS_ENABLED, 0) != 0) {
+                positionMode = GPS_POSITION_MODE_MS_BASED;
+            } else {
+                positionMode = GPS_POSITION_MODE_STANDALONE;
+            }
+
+            if (!native_start(positionMode, false, mFixInterval)) {
                 mStarted = false;
                 Log.e(TAG, "native_start failed in startNavigating()");
                 return;
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/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index c283418..87f4c40 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -36,6 +36,7 @@
          user opt-in via Setup Wizard or Settings.  
     -->
     <string name="def_location_providers_allowed">gps</string>
+    <bool name="assisted_gps_enabled">true</bool>
     <!--  0 == mobile, 1 == wifi. -->
     <integer name="def_network_preference">1</integer>
     <bool name="def_usb_mass_storage_enabled">true</bool>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index f861400..602f3e1 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -64,7 +64,7 @@
 
     private static final String TAG = "SettingsProvider";
     private static final String DATABASE_NAME = "settings.db";
-    private static final int DATABASE_VERSION = 34;
+    private static final int DATABASE_VERSION = 35;
 
     private Context mContext;
 
@@ -386,6 +386,21 @@
             upgradeVersion = 34;
         }
 
+        if (upgradeVersion == 34) {
+            db.beginTransaction();
+            try {
+                String value =
+                        mContext.getResources().getBoolean(R.bool.assisted_gps_enabled) ? "1" : "0";
+                db.execSQL("INSERT OR IGNORE INTO secure(name,value) values('" +
+                        Settings.Secure.ASSISTED_GPS_ENABLED + "','" + value + "');");
+                db.setTransactionSuccessful();
+            } finally {
+                db.endTransaction();
+            }
+
+            upgradeVersion = 35;
+        }
+
         if (upgradeVersion != currentVersion) {
             Log.w(TAG, "Got stuck trying to upgrade from version " + upgradeVersion
                     + ", must wipe the settings provider");
@@ -592,6 +607,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);
 
@@ -638,6 +668,9 @@
         loadStringSetting(stmt, Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
                 R.string.def_location_providers_allowed);
 
+        loadBooleanSetting(stmt, Settings.Secure.ASSISTED_GPS_ENABLED,
+                R.bool.assisted_gps_enabled);
+
         loadIntegerSetting(stmt, Settings.Secure.NETWORK_PREFERENCE,
                 R.integer.def_network_preference);
 
diff --git a/packages/TtsService/jni/android_tts_SynthProxy.cpp b/packages/TtsService/jni/android_tts_SynthProxy.cpp
index 0dafcc1..8537cae 100644
--- a/packages/TtsService/jni/android_tts_SynthProxy.cpp
+++ b/packages/TtsService/jni/android_tts_SynthProxy.cpp
@@ -204,11 +204,19 @@
             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.");
+        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
@@ -289,8 +297,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);
 }
 
 
@@ -559,6 +591,10 @@
         "(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
diff --git a/packages/TtsService/src/android/tts/SynthProxy.java b/packages/TtsService/src/android/tts/SynthProxy.java
index 3bdff37..a8eaaa43 100755
--- a/packages/TtsService/src/android/tts/SynthProxy.java
+++ b/packages/TtsService/src/android/tts/SynthProxy.java
@@ -73,6 +73,13 @@
     public void setLanguage(String language, String country, String variant) {
         native_setLanguage(mJniData, language, country, variant);
     }
+    
+    /**
+     * 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
@@ -149,6 +156,9 @@
 
     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);
     
diff --git a/packages/TtsService/src/android/tts/TtsService.java b/packages/TtsService/src/android/tts/TtsService.java
index 6fa3141..3c931b8 100755
--- a/packages/TtsService/src/android/tts/TtsService.java
+++ b/packages/TtsService/src/android/tts/TtsService.java
@@ -89,6 +89,8 @@
         }
     }
 
+    private static final int MAX_SPEECH_ITEM_CHAR_LENGTH = 4000;
+
     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 +110,6 @@
     private final ReentrantLock synthesizerLock = new ReentrantLock();
 
     private SynthProxy nativeSynth;
-
     @Override
     public void onCreate() {
         super.onCreate();
@@ -145,13 +146,11 @@
 
 
     private void setDefaultSettings() {
-
         // TODO handle default language
         setLanguage("eng", "USA", "");
 
         // speech rate
         setSpeechRate(getDefaultRate());
-
     }
 
 
@@ -424,6 +423,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 +444,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.SPEECH);
+                splitItems.add(splitItem);
+                start = end;
+                end = start + MAX_SPEECH_ITEM_CHAR_LENGTH - 1;
+            }
+            splitText = currentSpeechItem.mText.substring(start);
+            splitItem = new SpeechItem(splitText, null, SpeechItem.SPEECH);
+            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 +480,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;
             }
 
@@ -465,8 +492,7 @@
             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.
+                    currentSpeechItem = splitCurrentTextIfNeeded(currentSpeechItem);
                     speakInternalOnly(currentSpeechItem.mText,
                             currentSpeechItem.mParams);
                 } else {
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 197404e..bc2eaed 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -47,6 +47,7 @@
 import android.util.SparseArray;
 
 import android.backup.IBackupManager;
+import android.backup.IRestoreObserver;
 import android.backup.IRestoreSession;
 import android.backup.BackupManager;
 import android.backup.RestoreSet;
@@ -76,12 +77,12 @@
     private static final boolean DEBUG = true;
 
     // Default time to wait after data changes before we back up the data
-    private static final long COLLECTION_INTERVAL = 1000;
-    //private static final long COLLECTION_INTERVAL = 3 * 60 * 1000;
+    private static final long COLLECTION_INTERVAL = 3 * 60 * 1000;
 
     private static final int MSG_RUN_BACKUP = 1;
     private static final int MSG_RUN_FULL_BACKUP = 2;
     private static final int MSG_RUN_RESTORE = 3;
+    private static final String RESTORE_OBSERVER_KEY = "_resOb";
 
     // Timeout interval for deciding that a bind or clear-data has taken too long
     static final long TIMEOUT_INTERVAL = 10 * 1000;
@@ -110,8 +111,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
@@ -134,7 +135,18 @@
     private IBackupTransport mLocalTransport, mGoogleTransport;
     private RestoreSession mActiveRestoreSession;
 
-    private File mStateDir;
+    private class RestoreParams {
+        public IBackupTransport transport;
+        public IRestoreObserver observer;
+
+        RestoreParams(IBackupTransport _transport, IRestoreObserver _obs) {
+            transport = _transport;
+            observer = _obs;
+        }
+    }
+
+    // Where we keep our journal files and other bookkeeping
+    private File mBaseStateDir;
     private File mDataDir;
     private File mJournalDir;
     private File mJournal;
@@ -146,13 +158,12 @@
         mActivityManager = ActivityManagerNative.getDefault();
 
         // Set up our bookkeeping
-        mStateDir = new File(Environment.getDataDirectory(), "backup");
-        mStateDir.mkdirs();
+        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
@@ -165,8 +176,7 @@
         // 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;
+        mTransportId = BackupManager.TRANSPORT_GOOGLE;
         mLocalTransport = new LocalTransport(context);  // This is actually pretty cheap
         mGoogleTransport = null;
 
@@ -338,8 +348,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, token)).start();
                 break;
             }
             }
@@ -351,29 +361,29 @@
     void addPackageParticipantsLocked(String packageName) {
         // Look for apps that define the android:backupAgent attribute
         if (DEBUG) Log.v(TAG, "addPackageParticipantsLocked: " + packageName);
-        List<ApplicationInfo> targetApps = allAgentApps();
+        List<PackageInfo> targetApps = allAgentPackages();
         addPackageParticipantsLockedInner(packageName, targetApps);
     }
 
     private void addPackageParticipantsLockedInner(String packageName,
-            List<ApplicationInfo> targetApps) {
+            List<PackageInfo> targetPkgs) {
         if (DEBUG) {
-            Log.v(TAG, "Adding " + targetApps.size() + " backup participants:");
-            for (ApplicationInfo a : targetApps) {
-                Log.v(TAG, "    " + a + " agent=" + a.backupAgentName);
+            Log.v(TAG, "Adding " + targetPkgs.size() + " backup participants:");
+            for (PackageInfo p : targetPkgs) {
+                Log.v(TAG, "    " + p + " agent=" + p.applicationInfo.backupAgentName
+                        + " uid=" + p.applicationInfo.uid);
             }
         }
 
-        for (ApplicationInfo app : targetApps) {
-            if (packageName == null || app.packageName.equals(packageName)) {
-                int uid = app.uid;
+        for (PackageInfo pkg : targetPkgs) {
+            if (packageName == null || pkg.packageName.equals(packageName)) {
+                int uid = pkg.applicationInfo.uid;
                 HashSet<ApplicationInfo> set = mBackupParticipants.get(uid);
                 if (set == null) {
                     set = new HashSet<ApplicationInfo>();
                     mBackupParticipants.put(uid, set);
                 }
-                set.add(app);
-                backUpPackageManagerData();
+                set.add(pkg.applicationInfo);
             }
         }
     }
@@ -382,67 +392,66 @@
     // 'packageName' is null, *all* participating apps will be removed.
     void removePackageParticipantsLocked(String packageName) {
         if (DEBUG) Log.v(TAG, "removePackageParticipantsLocked: " + packageName);
-        List<ApplicationInfo> allApps = null;
+        List<PackageInfo> allApps = null;
         if (packageName != null) {
-            allApps = new ArrayList<ApplicationInfo>();
+            allApps = new ArrayList<PackageInfo>();
             try {
-                ApplicationInfo app = mPackageManager.getApplicationInfo(packageName, 0);
-                allApps.add(app);
+                int flags = PackageManager.GET_SIGNATURES;
+                allApps.add(mPackageManager.getPackageInfo(packageName, flags));
             } catch (Exception e) {
-                // just skip it
+                // just skip it (???)
             }
         } else {
             // all apps with agents
-            allApps = allAgentApps();
+            allApps = allAgentPackages();
         }
         removePackageParticipantsLockedInner(packageName, allApps);
     }
 
     private void removePackageParticipantsLockedInner(String packageName,
-            List<ApplicationInfo> agents) {
+            List<PackageInfo> agents) {
         if (DEBUG) {
             Log.v(TAG, "removePackageParticipantsLockedInner (" + packageName
                     + ") removing " + agents.size() + " entries");
-            for (ApplicationInfo a : agents) {
-                Log.v(TAG, "    - " + a);
+            for (PackageInfo p : agents) {
+                Log.v(TAG, "    - " + p);
             }
         }
-        for (ApplicationInfo app : agents) {
-            if (packageName == null || app.packageName.equals(packageName)) {
-                int uid = app.uid;
+        for (PackageInfo pkg : agents) {
+            if (packageName == null || pkg.packageName.equals(packageName)) {
+                int uid = pkg.applicationInfo.uid;
                 HashSet<ApplicationInfo> set = mBackupParticipants.get(uid);
                 if (set != null) {
                     // Find the existing entry with the same package name, and remove it.
                     // We can't just remove(app) because the instances are different.
                     for (ApplicationInfo entry: set) {
-                        if (entry.packageName.equals(app.packageName)) {
+                        if (entry.packageName.equals(pkg.packageName)) {
                             set.remove(entry);
-                            backUpPackageManagerData();
                             break;
                         }
                     }
                     if (set.size() == 0) {
-                        mBackupParticipants.delete(uid);                    }
+                        mBackupParticipants.delete(uid);
+                    }
                 }
             }
         }
     }
 
     // Returns the set of all applications that define an android:backupAgent attribute
-    private List<ApplicationInfo> allAgentApps() {
+    private List<PackageInfo> allAgentPackages() {
         // !!! TODO: cache this and regenerate only when necessary
-        List<ApplicationInfo> allApps = mPackageManager.getInstalledApplications(0);
-        int N = allApps.size();
-        if (N > 0) {
-            for (int a = N-1; a >= 0; a--) {
-                ApplicationInfo app = allApps.get(a);
-                if (((app.flags&ApplicationInfo.FLAG_ALLOW_BACKUP) == 0)
-                        || app.backupAgentName == null) {
-                    allApps.remove(a);
-                }
+        int flags = PackageManager.GET_SIGNATURES;
+        List<PackageInfo> packages = mPackageManager.getInstalledPackages(flags);
+        int N = packages.size();
+        for (int a = N-1; a >= 0; a--) {
+            ApplicationInfo app = packages.get(a).applicationInfo;
+            if (((app.flags&ApplicationInfo.FLAG_ALLOW_BACKUP) == 0)
+                    || app.backupAgentName == null) {
+                packages.remove(a);
             }
         }
-        return allApps;
+        return packages;
     }
 
     // Reset the given package's known backup participants.  Unlike add/remove, the update
@@ -455,19 +464,11 @@
         if (DEBUG) Log.v(TAG, "updatePackageParticipantsLocked: " + packageName);
 
         // brute force but small code size
-        List<ApplicationInfo> allApps = allAgentApps();
+        List<PackageInfo> allApps = allAgentPackages();
         removePackageParticipantsLockedInner(packageName, allApps);
         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) {
         mBackupHandler.removeMessages(MSG_RUN_BACKUP);
@@ -565,6 +566,7 @@
         private static final String TAG = "PerformBackupThread";
         IBackupTransport mTransport;
         ArrayList<BackupRequest> mQueue;
+        File mStateDir;
         File mJournal;
 
         public PerformBackupThread(IBackupTransport transport, ArrayList<BackupRequest> queue,
@@ -572,52 +574,43 @@
             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");
 
-            // start up the transport
-            try {
-                mTransport.startSession();
-            } catch (Exception e) {
-                Log.e(TAG, "Error session transport");
-                e.printStackTrace();
-                return;
-            }
-
-            // The transport is up and running.  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, allAgentApps());
-                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);
 
             // Finally, tear down the transport
             try {
-                mTransport.endSession();
-            } catch (Exception e) {
-                Log.e(TAG, "Error ending transport");
-                e.printStackTrace();
+                if (!mTransport.finishBackup()) {
+                    // STOPSHIP TODO: handle errors
+                    Log.e(TAG, "Backup failure in finishBackup()");
+                }
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error in finishBackup()", e);
             }
 
             if (!mJournal.delete()) {
@@ -712,24 +705,18 @@
                     if (DEBUG) Log.v(TAG, "doBackup() success; calling transport");
                     backupData =
                         ParcelFileDescriptor.open(backupDataName, ParcelFileDescriptor.MODE_READ_ONLY);
-                    int error = transport.performBackup(packInfo, backupData);
+                    if (!transport.performBackup(packInfo, backupData)) {
+                        // STOPSHIP TODO: handle errors
+                        Log.e(TAG, "Backup failure in performBackup()");
+                    }
 
                     // !!! TODO: After successful transport, delete the now-stale data
                     // and juggle the files so that next time the new state is passed
                     //backupDataName.delete();
                     newStateName.renameTo(savedStateName);
                 }
-            } catch (NameNotFoundException e) {
-                Log.e(TAG, "Package not found on backup: " + packageName);
-            } catch (FileNotFoundException fnf) {
-                Log.w(TAG, "File not found on backup: ");
-                fnf.printStackTrace();
-            } catch (RemoteException e) {
-                Log.d(TAG, "Remote target " + request.appInfo.packageName + " threw during backup:");
-                e.printStackTrace();
             } catch (Exception e) {
-                Log.w(TAG, "Final exception guard in backup: ");
-                e.printStackTrace();
+                Log.e(TAG, "Error backing up " + packageName, e);
             }
         }
     }
@@ -737,25 +724,6 @@
 
     // ----- Restore handling -----
 
-    // Is the given package restorable on this device?  Returns the on-device app's
-    // ApplicationInfo struct if it is; null if not.
-    //
-    // !!! TODO: also consider signatures
-    PackageInfo isRestorable(PackageInfo packageInfo) {
-        if (packageInfo.packageName != null) {
-            try {
-                PackageInfo app = mPackageManager.getPackageInfo(packageInfo.packageName,
-                        PackageManager.GET_SIGNATURES);
-                if ((app.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) {
-                    return app;
-                }
-            } catch (Exception e) {
-                // doesn't exist on this device, or other error -- just ignore it.
-            }
-        }
-        return null;
-    }
-
     private boolean signaturesMatch(Signature[] storedSigs, Signature[] deviceSigs) {
         // Allow unsigned apps, but not signed on one device and unsigned on the other
         // !!! TODO: is this the right policy?
@@ -792,8 +760,10 @@
 
     class PerformRestoreThread extends Thread {
         private IBackupTransport mTransport;
+        private IRestoreObserver mObserver;
         private int mToken;
         private RestoreSet mImage;
+        private File mStateDir;
 
         class RestoreRequest {
             public PackageInfo app;
@@ -805,9 +775,18 @@
             }
         }
 
-        PerformRestoreThread(IBackupTransport transport, int restoreSetToken) {
+        PerformRestoreThread(IBackupTransport transport, IRestoreObserver observer,
+                int 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
@@ -816,130 +795,177 @@
             /**
              * Restore sequence:
              *
-             * 1. start up the transport session
-             * 2. get the restore set description for our identity
-             * 3. for each app in the restore set:
+             * 1. get the restore set description for our identity
+             * 2. for each app in the restore set:
              *    3.a. if it's restorable on this device, add it to the restore queue
-             * 4. for each app in the restore queue:
-             *    4.a. clear the app data
-             *    4.b. get the restore data for the app from the transport
-             *    4.c. launch the backup agent for the app
-             *    4.d. agent.doRestore() with the data from the server
-             *    4.e. unbind the agent [and kill the app?]
-             * 5. shut down the transport
+             * 3. for each app in the restore queue:
+             *    3.a. clear the app data
+             *    3.b. get the restore data for the app from the transport
+             *    3.c. launch the backup agent for the app
+             *    3.d. agent.doRestore() with the data from the server
+             *    3.e. unbind the agent [and kill the app?]
+             * 4. shut down the transport
              */
 
-            int err = -1;
+            int error = -1; // assume error
+
+            // build the set of apps to restore
             try {
-                err = mTransport.startSession();
-            } catch (Exception e) {
-                Log.e(TAG, "Error starting transport for restore");
-                e.printStackTrace();
-            }
+                RestoreSet[] images = mTransport.getAvailableRestoreSets();
+                if (images == null) {
+                    // STOPSHIP TODO: Handle the failure somehow?
+                    Log.e(TAG, "Error getting restore sets");
+                    return;
+                }
 
-            if (err == 0) {
-                // build the set of apps to restore
-                try {
-                    RestoreSet[] images = mTransport.getAvailableRestoreSets();
-                    if (images.length > 0) {
-                        // !!! TODO: pick out the set for this token
-                        mImage = images[0];
+                if (images.length == 0) {
+                    Log.i(TAG, "No restore sets available");
+                    return;
+                }
 
-                        // Pull the Package Manager metadata from the restore set first
-                        PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
-                                mPackageManager, allAgentApps());
-                        PackageInfo pmApp = new PackageInfo();
-                        pmApp.packageName = PACKAGE_MANAGER_SENTINEL;
-                        // !!! TODO: version currently ignored when 'restoring' the PM metadata
-                        processOneRestore(pmApp, 0,
-                                IBackupAgent.Stub.asInterface(pmAgent.onBind()));
+                mImage = images[0];
 
-                        // build the set of apps we will attempt to restore
-                        PackageInfo[] packages = mTransport.getAppSet(mImage.token);
-                        HashSet<RestoreRequest> appsToRestore = new HashSet<RestoreRequest>();
-                        for (PackageInfo pkg: packages) {
-                            // get the real PackageManager idea of the package
-                            PackageInfo app = isRestorable(pkg);
-                            if (app != null) {
-                                // Validate against the backed-up signature block, too
-                                Metadata info = pmAgent.getRestoredMetadata(app.packageName);
-                                if (info != null) {
-                                    if (app.versionCode >= info.versionCode) {
-                                        if (DEBUG) Log.v(TAG, "Restore version "
-                                                + info.versionCode
-                                                + " compatible with app version "
-                                                + app.versionCode);
-                                        if (signaturesMatch(info.signatures, app.signatures)) {
-                                            appsToRestore.add(
-                                                    new RestoreRequest(app, info.versionCode));
-                                        } else {
-                                            Log.w(TAG, "Sig mismatch restoring "
-                                                    + app.packageName);
-                                        }
-                                    } else {
-                                        Log.i(TAG, "Restore set for " + app.packageName
-                                                + " is too new [" + info.versionCode
-                                                + "] for installed app version "
-                                                + app.versionCode);
-                                    }
-                                } else {
-                                    Log.d(TAG, "Unable to get metadata for "
-                                            + app.packageName);
-                                }
-                            }
+                // Get the list of all packages which have backup enabled.
+                // (Include the Package Manager metadata pseudo-package first.)
+                ArrayList<PackageInfo> restorePackages = new ArrayList<PackageInfo>();
+                PackageInfo omPackage = new PackageInfo();
+                omPackage.packageName = PACKAGE_MANAGER_SENTINEL;
+                restorePackages.add(omPackage);
+
+                List<PackageInfo> agentPackages = allAgentPackages();
+                restorePackages.addAll(agentPackages);
+
+                // 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;
+                    }
+                }
+
+                // 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]))) {
+                    // STOPSHIP TODO: Handle the failure somehow?
+                    Log.e(TAG, "Error starting restore operation");
+                    return;
+                }
+
+                String packageName = mTransport.nextRestorePackage();
+                if (packageName == null) {
+                    // STOPSHIP TODO: Handle the failure somehow?
+                    Log.e(TAG, "Error getting first restore package");
+                    return;
+                } else if (packageName.equals("")) {
+                    Log.i(TAG, "No restore data available");
+                    return;
+                } else if (!packageName.equals(PACKAGE_MANAGER_SENTINEL)) {
+                    Log.e(TAG, "Expected restore data for \"" + PACKAGE_MANAGER_SENTINEL
+                          + "\", found only \"" + packageName + "\"");
+                    return;
+                }
+
+                // Pull the Package Manager metadata from the restore set first
+                PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
+                        mPackageManager, agentPackages);
+                processOneRestore(omPackage, 0, IBackupAgent.Stub.asInterface(pmAgent.onBind()));
+
+                int count = 0;
+                for (;;) {
+                    packageName = mTransport.nextRestorePackage();
+                    if (packageName == null) {
+                        // STOPSHIP TODO: Handle the failure somehow?
+                        Log.e(TAG, "Error getting next restore package");
+                        return;
+                    } else if (packageName.equals("")) {
+                        break;
+                    }
+
+                    if (mObserver != null) {
+                        ++count;
+                        try {
+                            mObserver.onUpdate(count);
+                        } catch (RemoteException e) {
+                            Log.d(TAG, "Restore observer died in onUpdate");
+                            mObserver = null;
                         }
-
-                        // now run the restore queue
-                        doQueuedRestores(appsToRestore);
                     }
-                } catch (RemoteException e) {
-                    // can't happen; transports run locally
-                }
 
-                // done; shut down the transport
-                try {
-                    mTransport.endSession();
-                } catch (Exception e) {
-                    Log.e(TAG, "Error ending transport for restore");
-                    e.printStackTrace();
-                }
-            }
+                    Metadata metaInfo = pmAgent.getRestoredMetadata(packageName);
+                    if (metaInfo == null) {
+                        Log.e(TAG, "Missing metadata for " + packageName);
+                        continue;
+                    }
 
-            // even if the initial session startup failed, report that we're done here
-        }
+                    int flags = PackageManager.GET_SIGNATURES;
+                    PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName, flags);
+                    if (metaInfo.versionCode > packageInfo.versionCode) {
+                        Log.w(TAG, "Package " + packageName
+                                + " restore version [" + metaInfo.versionCode
+                                + "] is too new for installed version ["
+                                + packageInfo.versionCode + "]");
+                        continue;
+                    }
 
-        // restore each app in the queue
-        void doQueuedRestores(HashSet<RestoreRequest> appsToRestore) {
-            for (RestoreRequest req : appsToRestore) {
-                PackageInfo app = req.app;
-                Log.d(TAG, "starting agent for restore of " + app);
+                    if (!signaturesMatch(metaInfo.signatures, packageInfo.signatures)) {
+                        Log.w(TAG, "Signature mismatch restoring " + packageName);
+                        continue;
+                    }
 
-                try {
-                    // Remove the app's data first
-                    clearApplicationDataSynchronous(app.packageName);
+                    if (DEBUG) Log.v(TAG, "Package " + packageName
+                            + " restore version [" + metaInfo.versionCode
+                            + "] is compatible with installed version ["
+                            + packageInfo.versionCode + "]");
 
-                    // Now perform the restore into the clean app
-                    IBackupAgent agent = bindToAgentSynchronous(app.applicationInfo,
+                    // Now perform the actual restore
+                    clearApplicationDataSynchronous(packageName);
+                    IBackupAgent agent = bindToAgentSynchronous(
+                            packageInfo.applicationInfo,
                             IApplicationThread.BACKUP_MODE_RESTORE);
-                    if (agent != null) {
-                        processOneRestore(app, req.storedAppVersion, agent);
+                    if (agent == null) {
+                        Log.w(TAG, "Can't find backup agent for " + packageName);
+                        continue;
                     }
 
-                    // unbind even on timeout, just in case
-                    mActivityManager.unbindBackupAgent(app.applicationInfo);
-                } catch (SecurityException ex) {
-                    // Try for the next one.
-                    Log.d(TAG, "error in bind", ex);
-                } catch (RemoteException e) {
-                    // can't happen
+                    try {
+                        processOneRestore(packageInfo, metaInfo.versionCode, agent);
+                    } finally {
+                        // unbind even on timeout or failure, just in case
+                        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);
+            } catch (RemoteException e) {
+                // STOPSHIP TODO: Handle the failure somehow?
+                Log.e(TAG, "Error restoring data", e);
+            } finally {
+                try {
+                    mTransport.finishRestore();
+                } 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");
+                    }
+                }
             }
         }
 
-        // Do the guts of a restore of one application, derived from the 'mImage'
-        // restore set via the 'mTransport' transport.
-        void processOneRestore(PackageInfo app, int storedAppVersion, IBackupAgent agent) {
+        // Do the guts of a restore of one application, using mTransport.getRestoreData().
+        void processOneRestore(PackageInfo app, int appVersionCode, IBackupAgent agent) {
             // !!! TODO: actually run the restore through mTransport
             final String packageName = app.packageName;
 
@@ -954,11 +980,12 @@
 
                 // Run the transport's restore pass
                 // Run the target's backup pass
-                int err = -1;
                 try {
-                    err = mTransport.getRestoreData(mImage.token, app, backupData);
-                } catch (RemoteException e) {
-                    // can't happen
+                    if (!mTransport.getRestoreData(backupData)) {
+                        // STOPSHIP TODO: Handle this error somehow?
+                        Log.e(TAG, "Error getting restore data for " + packageName);
+                        return;
+                    }
                 } finally {
                     backupData.close();
                 }
@@ -973,30 +1000,18 @@
                 backupData = ParcelFileDescriptor.open(backupDataName,
                             ParcelFileDescriptor.MODE_READ_ONLY);
 
-                boolean success = false;
                 try {
-                    agent.doRestore(backupData, storedAppVersion, newState);
-                    success = true;
-                } catch (Exception e) {
-                    Log.e(TAG, "Restore failed for " + packageName);
-                    e.printStackTrace();
+                    agent.doRestore(backupData, appVersionCode, newState);
                 } finally {
                     newState.close();
                     backupData.close();
                 }
 
                 // if everything went okay, remember the recorded state now
-                if (success) {
-                    File savedStateName = new File(mStateDir, packageName);
-                    newStateName.renameTo(savedStateName);
-                }
-            } catch (FileNotFoundException fnfe) {
-                Log.v(TAG, "Couldn't open file for restore: " + fnfe);
-            } catch (IOException ioe) {
-                Log.e(TAG, "Unable to process restore file: " + ioe);
+                File savedStateName = new File(mStateDir, packageName);
+                newStateName.renameTo(savedStateName);
             } catch (Exception e) {
-                Log.e(TAG, "Final exception guard in restore:");
-                e.printStackTrace();
+                Log.e(TAG, "Error restoring data for " + packageName, e);
             }
         }
     }
@@ -1183,14 +1198,15 @@
             }
         }
 
-        public int performRestore(int token) throws android.os.RemoteException {
+        public int performRestore(int 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);
+                        Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
+                        msg.obj = new RestoreParams(mRestoreTransport, observer);
                         msg.arg1 = token;
                         mBackupHandler.sendMessage(msg);
                         return 0;
@@ -1206,7 +1222,7 @@
             mContext.enforceCallingPermission("android.permission.BACKUP",
                     "endRestoreSession");
 
-            mRestoreTransport.endSession();
+            mRestoreTransport.finishRestore();
             mRestoreTransport = null;
             synchronized(BackupManagerService.this) {
                 if (BackupManagerService.this.mActiveRestoreSession == this) {
diff --git a/services/java/com/android/server/PackageManagerBackupAgent.java b/services/java/com/android/server/PackageManagerBackupAgent.java
index d620eb1..66fb86d 100644
--- a/services/java/com/android/server/PackageManagerBackupAgent.java
+++ b/services/java/com/android/server/PackageManagerBackupAgent.java
@@ -57,7 +57,7 @@
     // is stored using the package name as a key)
     private static final String GLOBAL_METADATA_KEY = "@meta@";
 
-    private List<ApplicationInfo> mAllApps;
+    private List<PackageInfo> mAllPackages;
     private PackageManager mPackageManager;
     private HashMap<String, Metadata> mRestoredSignatures;
 
@@ -73,9 +73,9 @@
 
     // We're constructed with the set of applications that are participating
     // in backup.  This set changes as apps are installed & removed.
-    PackageManagerBackupAgent(PackageManager packageMgr, List<ApplicationInfo> apps) {
+    PackageManagerBackupAgent(PackageManager packageMgr, List<PackageInfo> packages) {
         mPackageManager = packageMgr;
-        mAllApps = apps;
+        mAllPackages = packages;
         mRestoredSignatures = null;
     }
 
@@ -118,8 +118,8 @@
 
             // For each app we have on device, see if we've backed it up yet.  If not,
             // write its signature block to the output, keyed on the package name.
-            for (ApplicationInfo app : mAllApps) {
-                String packName = app.packageName;
+            for (PackageInfo pkg : mAllPackages) {
+                String packName = pkg.packageName;
                 if (!existing.contains(packName)) {
                     // We haven't stored this app's signatures yet, so we do that now
                     try {
@@ -186,7 +186,7 @@
         }
 
         // Finally, write the new state blob -- just the list of all apps we handled
-        writeStateFile(mAllApps, newState);
+        writeStateFile(mAllPackages, newState);
     }
 
     // "Restore" here is a misnomer.  What we're really doing is reading back the
@@ -327,7 +327,7 @@
     }
 
     // Util: write out our new backup state file
-    private void writeStateFile(List<ApplicationInfo> apps, ParcelFileDescriptor stateFile) {
+    private void writeStateFile(List<PackageInfo> pkgs, ParcelFileDescriptor stateFile) {
         FileOutputStream outstream = new FileOutputStream(stateFile.getFileDescriptor());
         DataOutputStream out = new DataOutputStream(outstream);
 
@@ -338,8 +338,8 @@
             out.write(metaNameBuf);
 
             // now write all the app names too
-            for (ApplicationInfo app : apps) {
-                byte[] pkgNameBuf = app.packageName.getBytes();
+            for (PackageInfo pkg : pkgs) {
+                byte[] pkgNameBuf = pkg.packageName.getBytes();
                 out.writeInt(pkgNameBuf.length);
                 out.write(pkgNameBuf);
             }
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index d2e5555..857bfaa 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -968,35 +968,7 @@
 
             if (Config.LOGV) Log.v(TAG, "getActivityInfo " + component + ": " + a);
             if (a != null && mSettings.isEnabledLP(a.info, flags)) {
-                ActivityInfo ainfo = PackageParser.generateActivityInfo(a, flags);
-                if (ainfo != null) {
-                    ApplicationInfo appInfo = getApplicationInfo(component.getPackageName(),
-                            PackageManager.GET_SUPPORTS_DENSITIES);
-                    if (appInfo != null &&
-                            (appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) == 0) {
-                        // Check if the screen size is same as what the application expect.
-                        CompatibilityInfo info = new CompatibilityInfo(appInfo);
-                        DisplayMetrics metrics = new DisplayMetrics();
-                        metrics.setTo(mMetrics);
-                        int orientation = mMetrics.widthPixels > mMetrics.heightPixels ?
-                                Configuration.ORIENTATION_LANDSCAPE :
-                                Configuration.ORIENTATION_PORTRAIT;
-                        metrics.updateMetrics(info, orientation);
-                        if (!info.mExpandable) {
-                            // Don't allow an app that cannot expand to handle rotation.
-                            ainfo.configChanges &= ~ ActivityInfo.CONFIG_ORIENTATION;
-                        } else {
-                            appInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS;
-                        }
-                        if (DEBUG_SETTINGS) {
-                            Log.d(TAG, "component=" + component +
-                                    ", expandable:" +
-                                    ((appInfo.flags &
-                                            ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) != 0));
-                        }
-                    }
-                }
-                return ainfo;
+                return PackageParser.generateActivityInfo(a, flags);
             }
             if (mResolveComponentName.equals(component)) {
                 return mResolveActivity;
diff --git a/services/java/com/android/server/WallpaperService.java b/services/java/com/android/server/WallpaperService.java
index 5532894..d921baf 100644
--- a/services/java/com/android/server/WallpaperService.java
+++ b/services/java/com/android/server/WallpaperService.java
@@ -18,8 +18,10 @@
 
 import static android.os.FileObserver.*;
 import static android.os.ParcelFileDescriptor.*;
+
 import android.app.IWallpaperService;
 import android.app.IWallpaperServiceCallback;
+import android.backup.BackupManager;
 import android.content.Context;
 import android.content.Intent;
 import android.content.SharedPreferences;
@@ -154,7 +156,16 @@
     public ParcelFileDescriptor setWallpaper() {
         checkPermission(android.Manifest.permission.SET_WALLPAPER);
         try {
-            return ParcelFileDescriptor.open(WALLPAPER_FILE, MODE_CREATE|MODE_READ_WRITE);
+            ParcelFileDescriptor fd = ParcelFileDescriptor.open(WALLPAPER_FILE,
+                    MODE_CREATE|MODE_READ_WRITE);
+
+            // changing the wallpaper means we'll need to back up the new one
+            long origId = Binder.clearCallingIdentity();
+            BackupManager bm = new BackupManager(mContext);
+            bm.dataChanged();
+            Binder.restoreCallingIdentity(origId);
+
+            return fd;
         } catch (FileNotFoundException e) {
             if (Config.LOGD) Log.d(TAG, "Error setting wallpaper", e);
         }
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index 969fbfe..e91798b 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -1349,39 +1349,42 @@
                         level = 0;
                     }
 
+                    /*
+                     * The formatting of the results returned by
+                     * wpa_supplicant is intended to make the fields
+                     * line up nicely when printed,
+                     * not to make them easy to parse. So we have to
+                     * apply some heuristics to figure out which field
+                     * is the SSID and which field is the flags.
+                     */
+                    String ssid;
+                    String flags;
+                    if (result.length == 4) {
+                        if (result[3].charAt(0) == '[') {
+                            flags = result[3];
+                            ssid = "";
+                        } else {
+                            flags = "";
+                            ssid = result[3];
+                        }
+                    } else if (result.length == 5) {
+                        flags = result[3];
+                        ssid = result[4];
+                    } else {
+                        // Here, we must have 3 fields: no flags and ssid
+                        // set
+                        flags = "";
+                        ssid = "";
+                    }
+
                     // bssid is the hash key
                     scanResult = mScanResultCache.get(bssid);
                     if (scanResult != null) {
                         scanResult.level = level;
+                        scanResult.SSID = ssid;
+                        scanResult.capabilities = flags;
+                        scanResult.frequency = frequency;
                     } else {
-                        /*
-                         * The formatting of the results returned by
-                         * wpa_supplicant is intended to make the fields
-                         * line up nicely when printed,
-                         * not to make them easy to parse. So we have to
-                         * apply some heuristics to figure out which field
-                         * is the SSID and which field is the flags.
-                         */
-                        String ssid;
-                        String flags;
-                        if (result.length == 4) {
-                            if (result[3].charAt(0) == '[') {
-                                flags = result[3];
-                                ssid = "";
-                            } else {
-                                flags = "";
-                                ssid = result[3];
-                            }
-                        } else if (result.length == 5) {
-                            flags = result[3];
-                            ssid = result[4];
-                        } else {
-                            // Here, we must have 3 fields: no flags and ssid
-                            // set
-                            flags = "";
-                            ssid = "";
-                        }
-
                         // Do not add scan results that have no SSID set
                         if (0 < ssid.trim().length()) {
                             scanResult =
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 5236a91..8cdc1dc 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -1524,8 +1524,7 @@
                 }
             }
             
-            final BatteryStatsImpl bstats =
-                    (BatteryStatsImpl) mBatteryStatsService.getActiveStatistics();
+            final BatteryStatsImpl bstats = mBatteryStatsService.getActiveStatistics();
             synchronized(bstats) {
                 synchronized(mPidsSelfLocked) {
                     if (haveNewCpuStats) {
@@ -1540,7 +1539,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);
                                     }
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/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..b02c692 100644
--- a/telephony/java/com/android/internal/telephony/Phone.java
+++ b/telephony/java/com/android/internal/telephony/Phone.java
@@ -119,8 +119,11 @@
     static final String APN_TYPE_MMS = "mms";
     /** APN type for SUPL assisted GPS */
     static final String APN_TYPE_SUPL = "supl";
+    /** APN type for default data traffic, when requested using startUsingNetworkFeature */
+    static final String APN_TYPE_DEFAULT_FEATURE = "default-feature";
 
     // "Features" accessible through the connectivity manager
+    static final String FEATURE_ENABLE_DEFAULT = "enableDEFAULT";
     static final String FEATURE_ENABLE_MMS = "enableMMS";
     static final String FEATURE_ENABLE_SUPL = "enableSUPL";
 
@@ -831,7 +834,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 c34f26e..f6665aa 100644
--- a/telephony/java/com/android/internal/telephony/PhoneBase.java
+++ b/telephony/java/com/android/internal/telephony/PhoneBase.java
@@ -651,6 +651,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);
     }
@@ -658,7 +663,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/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/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..25a5f51 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -130,7 +130,8 @@
     private static int APN_DEFAULT_ID = 0;
     private static int APN_MMS_ID = 1;
     private static int APN_SUPL_ID = 2;
-    private static int APN_NUM_TYPES = 3;
+    private static int APN_DEFAULT_FEATURE_ID = 3;
+    private static int APN_NUM_TYPES = 4;
 
     private boolean[] dataEnabled = new boolean[APN_NUM_TYPES];
 
@@ -317,7 +318,8 @@
     /**
      * Ensure that we are connected to an APN of the specified type.
      * @param type the APN type (currently the only valid values
-     * are {@link Phone#APN_TYPE_MMS} and {@link Phone#APN_TYPE_SUPL})
+     * are {@link Phone#APN_TYPE_MMS}, {@link Phone#APN_TYPE_SUPL}
+     * and {@link Phone#APN_TYPE_DEFAULT_FEATURE})
      * @return the result of the operation. Success is indicated by
      * a return value of either {@code Phone.APN_ALREADY_ACTIVE} or
      * {@code Phone.APN_REQUEST_STARTED}. In the latter case, a broadcast
@@ -325,7 +327,13 @@
      * the APN has been established.
      */
     protected int enableApnType(String type) {
-        if (!TextUtils.equals(type, Phone.APN_TYPE_MMS) &&
+        /* FIXME: APN_TYPE_DEFAULT_FEATURE is used to request the default APN.
+         * Due to the way mRequestedApnType is used, we needed to add 
+         * a different APN_TYPE for this rather than using APN_TYPE_DEFAULT.
+         */
+
+        if (!TextUtils.equals(type, Phone.APN_TYPE_DEFAULT_FEATURE) &&
+                !TextUtils.equals(type, Phone.APN_TYPE_MMS) &&
                 !TextUtils.equals(type, Phone.APN_TYPE_SUPL)) {
             return Phone.APN_REQUEST_FAILED;
         }
@@ -363,12 +371,14 @@
      * use of the default APN has not been explicitly disabled, we are connected
      * to the default APN.
      * @param type the APN type. The only valid values are currently
-     * {@link Phone#APN_TYPE_MMS} and {@link Phone#APN_TYPE_SUPL}.
+     * {@link Phone#APN_TYPE_MMS} {@link Phone#APN_TYPE_SUPL} and
+     * {@link Phone#APN_TYPE_DEFAULT_FEATURE}).
      * @return
      */
     protected int disableApnType(String type) {
         Log.d(LOG_TAG, "disableApnType("+type+")");
-        if ((TextUtils.equals(type, Phone.APN_TYPE_MMS) ||
+        if ((TextUtils.equals(type, Phone.APN_TYPE_DEFAULT_FEATURE) ||
+                TextUtils.equals(type, Phone.APN_TYPE_MMS) ||
                 TextUtils.equals(type, Phone.APN_TYPE_SUPL))
                 && isEnabled(type)) {
             removeMessages(EVENT_RESTORE_DEFAULT_APN);
@@ -419,6 +429,9 @@
 
     private boolean isApnTypeActive(String type) {
         // TODO: to support simultaneous, mActiveApn can be a List instead.
+        if (TextUtils.equals(type, Phone.APN_TYPE_DEFAULT_FEATURE)) {
+            type = Phone.APN_TYPE_DEFAULT;
+        }
         return mActiveApn != null && mActiveApn.canHandleType(type);
     }
 
@@ -440,6 +453,8 @@
             return dataEnabled[APN_MMS_ID];
         } else if (TextUtils.equals(apnType, Phone.APN_TYPE_SUPL)) {
             return dataEnabled[APN_SUPL_ID];
+        } else if (TextUtils.equals(apnType, Phone.APN_TYPE_DEFAULT_FEATURE)) {
+            return dataEnabled[APN_DEFAULT_FEATURE_ID];
         } else {
             return false;
         }
@@ -453,10 +468,13 @@
             dataEnabled[APN_MMS_ID] = enable;
         } else if (TextUtils.equals(apnType, Phone.APN_TYPE_SUPL)) {
             dataEnabled[APN_SUPL_ID] = enable;
+        } else if (TextUtils.equals(apnType, Phone.APN_TYPE_DEFAULT_FEATURE)) {
+            dataEnabled[APN_DEFAULT_FEATURE_ID] = enable;
         }
         Log.d(LOG_TAG, "dataEnabled[DEFAULT_APN]=" + dataEnabled[APN_DEFAULT_ID] +
                 " dataEnabled[MMS_APN]=" + dataEnabled[APN_MMS_ID] +
-                " dataEnabled[SUPL_APN]=" + dataEnabled[APN_SUPL_ID]);
+                " dataEnabled[SUPL_APN]=" + dataEnabled[APN_SUPL_ID] +
+                " dataEnabled[APN_DEFAULT_FEATURE_ID]=" + dataEnabled[APN_DEFAULT_FEATURE_ID]);
     }
 
     /**
@@ -484,7 +502,9 @@
             // Don't tear down if there is an active APN and it handles MMS or SUPL.
             // TODO: This isn't very general.
             if ((isApnTypeActive(Phone.APN_TYPE_MMS) && isEnabled(Phone.APN_TYPE_MMS)) ||
-                (isApnTypeActive(Phone.APN_TYPE_SUPL) && isEnabled(Phone.APN_TYPE_SUPL))) {
+                (isApnTypeActive(Phone.APN_TYPE_SUPL) && isEnabled(Phone.APN_TYPE_SUPL)) ||
+                (isApnTypeActive(Phone.APN_TYPE_DEFAULT_FEATURE) &&
+                    isEnabled(Phone.APN_TYPE_DEFAULT_FEATURE))) {
                 return false;
             }
             Message msg = obtainMessage(EVENT_CLEAN_UP_CONNECTION);
@@ -514,7 +534,8 @@
      * {@code true} otherwise.
      */
     public boolean getAnyDataEnabled() {
-        return dataEnabled[APN_DEFAULT_ID] || dataEnabled[APN_MMS_ID] || dataEnabled[APN_SUPL_ID];
+        return dataEnabled[APN_DEFAULT_ID] || dataEnabled[APN_MMS_ID] ||
+                dataEnabled[APN_SUPL_ID] || dataEnabled[APN_DEFAULT_FEATURE_ID];
     }
 
     /**
@@ -1289,7 +1310,8 @@
              * rather an app that inadvertantly fails to reset to the
              * default APN, or that dies before doing so.
              */
-            if (dataEnabled[APN_MMS_ID] || dataEnabled[APN_SUPL_ID]) {
+            if (dataEnabled[APN_MMS_ID] || dataEnabled[APN_SUPL_ID] ||
+                    dataEnabled[APN_DEFAULT_FEATURE_ID]) {
                 removeMessages(EVENT_RESTORE_DEFAULT_APN);
                 sendMessageDelayed(obtainMessage(EVENT_RESTORE_DEFAULT_APN),
                         getRestoreDefaultApnDelay());
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/test-runner/android/test/RenamingDelegatingContext.java b/test-runner/android/test/RenamingDelegatingContext.java
index 3f64340..d780502 100644
--- a/test-runner/android/test/RenamingDelegatingContext.java
+++ b/test-runner/android/test/RenamingDelegatingContext.java
@@ -136,6 +136,11 @@
             return false;
         }
     }
+    
+    @Override
+    public File getDatabasePath(String name) {
+        return mFileContext.getDatabasePath(renamedFileName(name));
+    }
 
     @Override
     public String[] databaseList() {
diff --git a/tests/backup/src/com/android/backuptest/BackupTestAgent.java b/tests/backup/src/com/android/backuptest/BackupTestAgent.java
index c6acc66..6acd90c 100644
--- a/tests/backup/src/com/android/backuptest/BackupTestAgent.java
+++ b/tests/backup/src/com/android/backuptest/BackupTestAgent.java
@@ -23,6 +23,8 @@
 {
     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"));
     }
 }
 
diff --git a/tests/backup/test_backup.sh b/tests/backup/test_backup.sh
new file mode 100755
index 0000000..6ef5dff
--- /dev/null
+++ b/tests/backup/test_backup.sh
@@ -0,0 +1,22 @@
+#!/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 ; \
+           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..f3d581e
--- /dev/null
+++ b/tests/backup/test_restore.sh
@@ -0,0 +1,46 @@
+#!/bin/bash
+
+function check_file
+{
+    data=$(adb shell cat /data/data/com.android.backuptest/files/$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 --- Erased files
+adb shell "ls -l /data/data/com.android.backuptest/files"
+echo ---
+
+echo
+echo
+echo
+
+# run the restore
+adb shell bmgr restore 0
+
+echo
+echo
+echo
+
+# check the results
+check_file file.txt "first file"
+check_file another_file.txt "asdf"
+check_file 3.txt "3"
+check_file empty.txt ""
+
+echo
+echo
+echo
+echo --- Restored files
+adb shell "ls -l /data/data/com.android.backuptest/files"
+echo ---
+echo
diff --git a/tools/localize/Perforce.cpp b/tools/localize/Perforce.cpp
index a7f301e..ae11231 100644
--- a/tools/localize/Perforce.cpp
+++ b/tools/localize/Perforce.cpp
@@ -1,6 +1,7 @@
 #include "Perforce.h"
 #include "log.h"
 #include <string.h>
+#include <cstdio>
 #include <stdlib.h>
 #include <sstream>
 #include <sys/types.h>
diff --git a/tools/localize/SourcePos.cpp b/tools/localize/SourcePos.cpp
index dd54f3a..184bfe0a 100644
--- a/tools/localize/SourcePos.cpp
+++ b/tools/localize/SourcePos.cpp
@@ -1,6 +1,7 @@
 #include "SourcePos.h"
 
 #include <stdarg.h>
+#include <cstdio>
 #include <set>
 #include <cstdio>
 
diff --git a/tools/localize/file_utils.cpp b/tools/localize/file_utils.cpp
index 84081f5..775ce2f 100644
--- a/tools/localize/file_utils.cpp
+++ b/tools/localize/file_utils.cpp
@@ -1,4 +1,5 @@
 #include <string.h>
+#include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include "file_utils.h"
diff --git a/tools/localize/localize_test.cpp b/tools/localize/localize_test.cpp
index 931ea95..1d0ac9a 100644
--- a/tools/localize/localize_test.cpp
+++ b/tools/localize/localize_test.cpp
@@ -2,6 +2,7 @@
 #include "XLIFFFile.h"
 #include "ValuesFile.h"
 #include "localize.h"
+#include <stdio.h>
 
 int pseudolocalize_xliff(XLIFFFile* xliff, bool expand);
 
diff --git a/tools/localize/merge_res_and_xliff.cpp b/tools/localize/merge_res_and_xliff.cpp
index 58a6554..1fdaa0e 100644
--- a/tools/localize/merge_res_and_xliff.cpp
+++ b/tools/localize/merge_res_and_xliff.cpp
@@ -3,6 +3,7 @@
 #include "file_utils.h"
 #include "Perforce.h"
 #include "log.h"
+#include <stdio.h>
 
 static set<StringResource>::const_iterator
 find_id(const set<StringResource>& s, const string& id, int index)
diff --git a/tools/localize/merge_res_and_xliff_test.cpp b/tools/localize/merge_res_and_xliff_test.cpp
index f638a74..6fe2629 100644
--- a/tools/localize/merge_res_and_xliff_test.cpp
+++ b/tools/localize/merge_res_and_xliff_test.cpp
@@ -1,6 +1,6 @@
 #include <cstdio>
 #include "merge_res_and_xliff.h"
-
+#include <stdio.h>
 
 int
 merge_test()
diff --git a/tools/localize/xmb.cpp b/tools/localize/xmb.cpp
index 236705f..d8f6ff0 100644
--- a/tools/localize/xmb.cpp
+++ b/tools/localize/xmb.cpp
@@ -7,6 +7,7 @@
 #include "XLIFFFile.h"
 
 #include <map>
+#include <cstdio>
 
 using namespace std;
 
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;
     }