Merge "Fix issue 2909189: System property ro.audio.silent no longer mutes system." into gingerbread
diff --git a/Android.mk b/Android.mk
index b6f25047..b1f43f8 100644
--- a/Android.mk
+++ b/Android.mk
@@ -121,6 +121,7 @@
 	core/java/android/os/storage/IMountService.aidl \
 	core/java/android/os/storage/IMountServiceListener.aidl \
 	core/java/android/os/storage/IMountShutdownObserver.aidl \
+	core/java/android/os/storage/IObbActionListener.aidl \
 	core/java/android/os/INetworkManagementService.aidl \
 	core/java/android/os/INetStatService.aidl \
 	core/java/android/os/IPermissionController.aidl \
diff --git a/api/current.xml b/api/current.xml
index 841aa21..65a8a8e 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -26460,6 +26460,17 @@
 <parameter name="holder" type="android.view.SurfaceHolder">
 </parameter>
 </method>
+<field name="KEY_NATIVE_SAVED_STATE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android:native_state&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="META_DATA_LIB_NAME"
  type="java.lang.String"
  transient="false"
@@ -129520,6 +129531,8 @@
 >
 <parameter name="filename" type="java.lang.String">
 </parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
 </method>
 <method name="mountObb"
  return="boolean"
@@ -129550,6 +129563,8 @@
 </parameter>
 <parameter name="force" type="boolean">
 </parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
 </method>
 </class>
 </package>
diff --git a/core/java/android/app/NativeActivity.java b/core/java/android/app/NativeActivity.java
index eaf0675..4dc88b3 100644
--- a/core/java/android/app/NativeActivity.java
+++ b/core/java/android/app/NativeActivity.java
@@ -10,6 +10,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.res.AssetManager;
+import android.content.res.Configuration;
 import android.graphics.PixelFormat;
 import android.os.Build;
 import android.os.Bundle;
@@ -32,12 +33,27 @@
 
 /**
  * Convenience for implementing an activity that will be implemented
- * purely in native code.  That is, a game (or game-like thing).
+ * purely in native code.  That is, a game (or game-like thing).  There
+ * is no need to derive from this class; you can simply declare it in your
+ * manifest, and use the NDK APIs from there.
+ *
+ * <p>A typical manifest would look like:
+ *
+ * {@sample development/ndk/platforms/android-9/samples/native-activity/AndroidManifest.xml
+ *      manifest}
+ *
+ * <p>A very simple example of native code that is run by NativeActivity
+ * follows.  This reads input events from the user and uses OpenGLES to
+ * draw into the native activity's window.
+ *
+ * {@sample development/ndk/platforms/android-9/samples/native-activity/jni/main.c all}
  */
 public class NativeActivity extends Activity implements SurfaceHolder.Callback2,
         InputQueue.Callback, OnGlobalLayoutListener {
     public static final String META_DATA_LIB_NAME = "android.app.lib_name";
     
+    public static final String KEY_NATIVE_SAVED_STATE = "android:native_state";
+
     private NativeContentView mNativeContentView;
     private InputMethodManager mIMM;
     private InputMethodCallback mInputMethodCallback;
@@ -59,14 +75,15 @@
     
     private native int loadNativeCode(String path, MessageQueue queue,
             String internalDataPath, String externalDataPath, int sdkVersion,
-            AssetManager assetMgr);
+            AssetManager assetMgr, byte[] savedState);
     private native void unloadNativeCode(int handle);
     
     private native void onStartNative(int handle);
     private native void onResumeNative(int handle);
-    private native void onSaveInstanceStateNative(int handle);
+    private native byte[] onSaveInstanceStateNative(int handle);
     private native void onPauseNative(int handle);
     private native void onStopNative(int handle);
+    private native void onConfigurationChangedNative(int handle);
     private native void onLowMemoryNative(int handle);
     private native void onWindowFocusChangedNative(int handle, boolean focused);
     private native void onSurfaceCreatedNative(int handle, Surface surface);
@@ -165,10 +182,13 @@
             throw new IllegalArgumentException("Unable to find native library: " + libname);
         }
         
+        byte[] nativeSavedState = savedInstanceState != null
+                ? savedInstanceState.getByteArray(KEY_NATIVE_SAVED_STATE) : null;
+
         mNativeHandle = loadNativeCode(path, Looper.myQueue(),
                  getFilesDir().toString(),
                  Environment.getExternalStorageAppFilesDirectory(ai.packageName).toString(),
-                 Build.VERSION.SDK_INT, getAssets());
+                 Build.VERSION.SDK_INT, getAssets(), nativeSavedState);
         
         if (mNativeHandle == 0) {
             throw new IllegalArgumentException("Unable to load native library: " + path);
@@ -206,7 +226,10 @@
     @Override
     protected void onSaveInstanceState(Bundle outState) {
         super.onSaveInstanceState(outState);
-        onSaveInstanceStateNative(mNativeHandle);
+        byte[] state = onSaveInstanceStateNative(mNativeHandle);
+        if (state != null) {
+            outState.putByteArray(KEY_NATIVE_SAVED_STATE, state);
+        }
     }
 
     @Override
@@ -222,6 +245,14 @@
     }
 
     @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        if (!mDestroyed) {
+            onConfigurationChangedNative(mNativeHandle);
+        }
+    }
+
+    @Override
     public void onLowMemory() {
         super.onLowMemory();
         if (!mDestroyed) {
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 0608cc0..9bb3b75 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -1288,7 +1288,7 @@
                 height = mMetrics.widthPixels;
             }
             int keyboardHidden = mConfiguration.keyboardHidden;
-            if (keyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO
+            if (keyboardHidden == Configuration.KEYBOARDHIDDEN_NO
                     && mConfiguration.hardKeyboardHidden
                             == Configuration.HARDKEYBOARDHIDDEN_YES) {
                 keyboardHidden = Configuration.KEYBOARDHIDDEN_SOFT;
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 19e578f..f72de67 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -1166,6 +1166,14 @@
          */
         public static final String FOCUS_MODE_EDOF = "edof";
 
+        /**
+         * Continuous auto focus mode. The camera continuously tries to focus.
+         * This is ideal for shooting video or shooting photo of moving object.
+         * Auto focus starts when the parameter is set. Applications should not
+         * call {@link #autoFocus(AutoFocusCallback)} in this mode.
+         */
+        public static final String FOCUS_MODE_CONTINUOUS = "continuous";
+
         // Indices for focus distance array.
         /**
          * The array index of near focus distance for use with
@@ -1186,16 +1194,6 @@
         public static final int FOCUS_DISTANCE_FAR_INDEX = 2;
 
         /**
-         * Continuous focus mode. The camera continuously tries to focus. This
-         * is ideal for shooting video or shooting photo of moving object.
-         * Continuous focus starts when {@link #autoFocus(AutoFocusCallback)} is
-         * called. Continuous focus stops when {@link #cancelAutoFocus()} is
-         * called. AutoFocusCallback will be only called once as soon as the
-         * picture is in focus.
-         */
-        public static final String FOCUS_MODE_CONTINUOUS = "continuous";
-
-        /**
          * The camera determines the exposure by giving more weight to the
          * central part of the scene.
          */
@@ -1942,15 +1940,15 @@
         /**
          * Gets the current focus mode setting.
          *
-         * @return current focus mode. If the camera does not support
-         *         auto-focus, this should return {@link #FOCUS_MODE_FIXED}. If
-         *         the focus mode is not FOCUS_MODE_FIXED or {@link
-         *         #FOCUS_MODE_INFINITY}, applications should call {@link
-         *         #autoFocus(AutoFocusCallback)} to start the focus.
+         * @return current focus mode. This method will always return a non-null
+         *         value. Applications should call {@link
+         *         #autoFocus(AutoFocusCallback)} to start the focus if focus
+         *         mode is FOCUS_MODE_AUTO or FOCUS_MODE_MACRO.
          * @see #FOCUS_MODE_AUTO
          * @see #FOCUS_MODE_INFINITY
          * @see #FOCUS_MODE_MACRO
          * @see #FOCUS_MODE_FIXED
+         * @see #FOCUS_MODE_CONTINUOUS
          */
         public String getFocusMode() {
             return get(KEY_FOCUS_MODE);
@@ -2152,8 +2150,14 @@
          * #autoFocus(AutoFocusCallback)}, {@link #cancelAutoFocus}, or {@link
          * #startPreview()}. Applications can call {@link #getParameters()}
          * and this method anytime to get the latest focus distances. If the
-         * focus mode is FOCUS_MODE_CONTINUOUS and autofocus has started, focus
-         * distances may change from time to time.
+         * focus mode is FOCUS_MODE_CONTINUOUS, focus distances may change from
+         * time to time.
+         *
+         * This method is intended to estimate the distance between the camera
+         * and the subject. After autofocus, the subject distance may be within
+         * near and far focus distance. However, the precision depends on the
+         * camera hardware, autofocus algorithm, the focus area, and the scene.
+         * The error can be large and it should be only used as a reference.
          *
          * Far focus distance >= optimal focus distance >= near focus distance.
          * If the focus distance is infinity, the value will be
diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java
index 69b3540..a9d7342 100644
--- a/core/java/android/os/Looper.java
+++ b/core/java/android/os/Looper.java
@@ -180,6 +180,11 @@
         return mThread;
     }
     
+    /** @hide */
+    public MessageQueue getQueue() {
+        return mQueue;
+    }
+    
     public void dump(Printer pw, String prefix) {
         pw.println(prefix + this);
         pw.println(prefix + "mRun=" + mRun);
diff --git a/core/java/android/os/storage/IMountService.aidl b/core/java/android/os/storage/IMountService.aidl
index ca7efe7..5c69214 100644
--- a/core/java/android/os/storage/IMountService.aidl
+++ b/core/java/android/os/storage/IMountService.aidl
@@ -19,6 +19,7 @@
 
 import android.os.storage.IMountServiceListener;
 import android.os.storage.IMountShutdownObserver;
+import android.os.storage.IObbActionListener;
 
 /** WARNING! Update IMountService.h and IMountService.cpp if you change this file.
  * In particular, the ordering of the methods below must match the 
@@ -156,14 +157,20 @@
     /**
      * Mounts an Opaque Binary Blob (OBB) with the specified decryption key and only
      * allows the calling process's UID access to the contents.
+     *
+     * MountService will call back to the supplied IObbActionListener to inform
+     * it of the terminal state of the call.
      */
-    int mountObb(String filename, String key);
+    void mountObb(String filename, String key, IObbActionListener token);
 
     /**
      * Unmounts an Opaque Binary Blob (OBB). When the force flag is specified, any
      * program using it will be forcibly killed to unmount the image.
+     *
+     * MountService will call back to the supplied IObbActionListener to inform
+     * it of the terminal state of the call.
      */
-    int unmountObb(String filename, boolean force);
+    void unmountObb(String filename, boolean force, IObbActionListener token);
 
     /**
      * Checks whether the specified Opaque Binary Blob (OBB) is mounted somewhere.
diff --git a/core/java/android/os/storage/IObbActionListener.aidl b/core/java/android/os/storage/IObbActionListener.aidl
new file mode 100644
index 0000000..78d7a9e
--- /dev/null
+++ b/core/java/android/os/storage/IObbActionListener.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2010 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.os.storage;
+
+/**
+ * Callback class for receiving events from MountService about
+ * Opaque Binary Blobs (OBBs).
+ *
+ * @hide - Applications should use android.os.storage.StorageManager
+ * to interact with OBBs.
+ */
+interface IObbActionListener {
+    /**
+     * Return from an OBB action result.
+     *
+     * @param filename the path to the OBB the operation was performed on
+     * @param returnCode status of the operation
+     */
+    void onObbResult(String filename, String status);
+}
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 96bf2d5..cb1794f 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -16,28 +16,14 @@
 
 package android.os.storage;
 
-import android.content.Context;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.Looper;
-import android.os.Parcelable;
-import android.os.ParcelFileDescriptor;
-import android.os.Process;
-import android.os.RemoteException;
 import android.os.Handler;
+import android.os.Looper;
 import android.os.Message;
+import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.os.storage.IMountService;
-import android.os.storage.IMountServiceListener;
 import android.util.Log;
-import android.util.SparseArray;
 
-import java.io.FileDescriptor;
-import java.io.IOException;
 import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
 
 /**
  * StorageManager is the interface to the systems storage service.
@@ -87,6 +73,17 @@
     }
 
     /**
+     * Binder listener for OBB action results.
+     */
+    private final ObbActionBinderListener mObbActionListener = new ObbActionBinderListener();
+    private class ObbActionBinderListener extends IObbActionListener.Stub {
+        @Override
+        public void onObbResult(String filename, String status) throws RemoteException {
+            Log.i(TAG, "filename = " + filename + ", result = " + status);
+        }
+    }
+
+    /**
      * Private base class for messages sent between the callback thread
      * and the target looper handler.
      */
@@ -299,12 +296,23 @@
     }
 
     /**
-     * Mount an OBB file.
+     * Mount an Opaque Binary Blob (OBB) file. If a <code>key</code> is
+     * specified, it is supplied to the mounting process to be used in any
+     * encryption used in the OBB.
+     * <p>
+     * <em>Note:</em> you can only mount OBB files for which the OBB tag on the
+     * file matches a package ID that is owned by the calling program's UID.
+     * That is, shared UID applications can obtain access to any other
+     * application's OBB that shares its UID.
+     * 
+     * @param filename the path to the OBB file
+     * @param key decryption key
+     * @return whether the mount call was successfully queued or not
      */
     public boolean mountObb(String filename, String key) {
         try {
-            return mMountService.mountObb(filename, key)
-                    == StorageResultCode.OperationSucceeded;
+            mMountService.mountObb(filename, key, mObbActionListener);
+            return true;
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to mount OBB", e);
         }
@@ -313,12 +321,24 @@
     }
 
     /**
-     * Mount an OBB file.
+     * Unmount an Opaque Binary Blob (OBB) file. If the <code>force</code> flag
+     * is true, it will kill any application needed to unmount the given OBB.
+     * <p>
+     * <em>Note:</em> you can only mount OBB files for which the OBB tag on the
+     * file matches a package ID that is owned by the calling program's UID.
+     * That is, shared UID applications can obtain access to any other
+     * application's OBB that shares its UID.
+     * 
+     * @param filename path to the OBB file
+     * @param force whether to kill any programs using this in order to unmount
+     *            it
+     * @return whether the unmount call was successfully queued or not
+     * @throws IllegalArgumentException when OBB is not already mounted
      */
-    public boolean unmountObb(String filename, boolean force) {
+    public boolean unmountObb(String filename, boolean force) throws IllegalArgumentException {
         try {
-            return mMountService.unmountObb(filename, force)
-                    == StorageResultCode.OperationSucceeded;
+            mMountService.unmountObb(filename, force, mObbActionListener);
+            return true;
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to mount OBB", e);
         }
@@ -326,7 +346,13 @@
         return false;
     }
 
-    public boolean isObbMounted(String filename) {
+    /**
+     * Check whether an Opaque Binary Blob (OBB) is mounted or not.
+     * 
+     * @param filename path to OBB image
+     * @return true if OBB is mounted; false if not mounted or on error
+     */
+    public boolean isObbMounted(String filename) throws IllegalArgumentException {
         try {
             return mMountService.isObbMounted(filename);
         } catch (RemoteException e) {
@@ -337,13 +363,21 @@
     }
 
     /**
-     * Check the mounted path of an OBB file.
+     * Check the mounted path of an Opaque Binary Blob (OBB) file. This will
+     * give you the path to where you can obtain access to the internals of the
+     * OBB.
+     * 
+     * @param filename path to OBB image
+     * @return absolute path to mounted OBB image data or <code>null</code> if
+     *         not mounted or exception encountered trying to read status
      */
     public String getMountedObbPath(String filename) {
         try {
             return mMountService.getMountedObbPath(filename);
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to find mounted path for OBB", e);
+        } catch (IllegalArgumentException e) {
+            Log.d(TAG, "Couldn't read OBB file", e);
         }
 
         return null;
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index d6b9212..e86e3bf 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -28,6 +28,7 @@
 import android.view.KeyEvent;
 import android.view.InputEvent;
 import android.view.MotionEvent;
+import android.view.InputChannel;
 
 /**
  * System private interface to the window manager.
@@ -119,6 +120,7 @@
     int getKeycodeStateForDevice(int devid, int sw);
     int getTrackballKeycodeState(int sw);
     int getDPadKeycodeState(int sw);
+    InputChannel monitorInput(String inputChannelName);
     
     // Report whether the hardware supports the given keys; returns true if successful
     boolean hasKeys(in int[] keycodes, inout boolean[] keyExists);
diff --git a/core/java/android/view/InputQueue.java b/core/java/android/view/InputQueue.java
index 13d8104..43c957a 100644
--- a/core/java/android/view/InputQueue.java
+++ b/core/java/android/view/InputQueue.java
@@ -132,9 +132,9 @@
             synchronized (sLock) {
                 FinishedCallback callback = sRecycleHead;
                 if (callback != null) {
-                    callback.mRecycleNext = null;
                     sRecycleHead = callback.mRecycleNext;
                     sRecycleCount -= 1;
+                    callback.mRecycleNext = null;
                 } else {
                     callback = new FinishedCallback();
                 }
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index 9223e17..ed10e41 100755
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -156,7 +156,7 @@
     //  those new codes.  This is intended to maintain a consistent
     //  set of key code definitions across all Android devices.
    
-    private static final int LAST_KEYCODE           = KEYCODE_SWITCH_CHARSET;
+    private static final int LAST_KEYCODE           = KEYCODE_BUTTON_MODE;
     
     /**
      * @deprecated There are now more than MAX_KEYCODE keycodes.
diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java
index ff34f4a..0999598 100644
--- a/core/java/android/view/ScaleGestureDetector.java
+++ b/core/java/android/view/ScaleGestureDetector.java
@@ -312,7 +312,7 @@
      * MotionEvent has no getRawX(int) method; simulate it pending future API approval. 
      */
     private static float getRawX(MotionEvent event, int pointerIndex) {
-        float offset = event.getX() - event.getRawX();
+        float offset = event.getRawX() - event.getX();
         return event.getX(pointerIndex) + offset;
     }
     
@@ -320,7 +320,7 @@
      * MotionEvent has no getRawY(int) method; simulate it pending future API approval. 
      */
     private static float getRawY(MotionEvent event, int pointerIndex) {
-        float offset = event.getY() - event.getRawY();
+        float offset = event.getRawY() - event.getY();
         return event.getY(pointerIndex) + offset;
     }
 
diff --git a/core/java/android/view/VelocityTracker.java b/core/java/android/view/VelocityTracker.java
index 068e7b6..fb88c71 100644
--- a/core/java/android/view/VelocityTracker.java
+++ b/core/java/android/view/VelocityTracker.java
@@ -33,14 +33,15 @@
  * and {@link #getXVelocity()}.
  */
 public final class VelocityTracker implements Poolable<VelocityTracker> {
-    static final String TAG = "VelocityTracker";
-    static final boolean DEBUG = false;
-    static final boolean localLOGV = DEBUG || Config.LOGV;
+    private static final String TAG = "VelocityTracker";
+    private static final boolean DEBUG = false;
+    private static final boolean localLOGV = DEBUG || Config.LOGV;
 
-    static final int NUM_PAST = 10;
-    static final int MAX_AGE_MILLISECONDS = 200;
+    private static final int NUM_PAST = 10;
+    private static final int MAX_AGE_MILLISECONDS = 200;
+    
+    private static final int POINTER_POOL_CAPACITY = 20;
 
-    static final VelocityTracker[] mPool = new VelocityTracker[1];
     private static final Pool<VelocityTracker> sPool = Pools.synchronizedPool(
             Pools.finitePool(new PoolableManager<VelocityTracker>() {
                 public VelocityTracker newInstance() {
@@ -48,16 +49,19 @@
                 }
 
                 public void onAcquired(VelocityTracker element) {
-                    element.clear();
                 }
 
                 public void onReleased(VelocityTracker element) {
+                    element.clear();
                 }
             }, 2));
     
-    private static final int INITIAL_POINTERS = 5;
+    private static Pointer sRecycledPointerListHead;
+    private static int sRecycledPointerCount;
     
-    private static final class PointerData {
+    private static final class Pointer {
+        public Pointer next;
+        
         public int id;
         public float xVelocity;
         public float yVelocity;
@@ -65,11 +69,13 @@
         public final float[] pastX = new float[NUM_PAST];
         public final float[] pastY = new float[NUM_PAST];
         public final long[] pastTime = new long[NUM_PAST]; // uses Long.MIN_VALUE as a sentinel
+        
+        public int generation;
     }
     
-    private PointerData[] mPointers = new PointerData[INITIAL_POINTERS];
-    private int mNumPointers;
+    private Pointer mPointerListHead; // sorted by id in increasing order
     private int mLastTouchIndex;
+    private int mGeneration;
 
     private VelocityTracker mNext;
 
@@ -115,7 +121,9 @@
      * Reset the velocity tracker back to its initial state.
      */
     public void clear() {
-        mNumPointers = 0;
+        releasePointerList(mPointerListHead);
+        
+        mPointerListHead = null;
         mLastTouchIndex = 0;
     }
     
@@ -134,56 +142,62 @@
         final int lastTouchIndex = mLastTouchIndex;
         final int nextTouchIndex = (lastTouchIndex + 1) % NUM_PAST;
         final int finalTouchIndex = (nextTouchIndex + historySize) % NUM_PAST;
+        final int generation = mGeneration++;
         
-        if (pointerCount < mNumPointers) {
-            final PointerData[] pointers = mPointers;
-            int i = mNumPointers;
-            while (--i >= 0) {
-                final PointerData pointerData = pointers[i];
-                if (ev.findPointerIndex(pointerData.id) == -1) {
-                    // Pointer went up.
-                    // Shuffle pointers down to fill the hole.  Place the old pointer data at
-                    // the end so we can recycle it if more pointers are added later.
-                    mNumPointers -= 1;
-                    final int remaining = mNumPointers - i;
-                    if (remaining != 0) {
-                        System.arraycopy(pointers, i + 1, pointers, i, remaining);
-                        pointers[mNumPointers] = pointerData;
-                    }
-                }
-            }
-        }
-        
+        mLastTouchIndex = finalTouchIndex;
+
+        // Update pointer data.
+        Pointer previousPointer = null;
         for (int i = 0; i < pointerCount; i++){
             final int pointerId = ev.getPointerId(i);
-            PointerData pointerData = getPointerData(pointerId);
-            if (pointerData == null) {
-                // Pointer went down.
-                // Add a new entry.  Write a sentinel at the end of the pastTime trace so we
-                // will be able to tell where the trace started.
-                final PointerData[] oldPointers = mPointers;
-                final int newPointerIndex = mNumPointers;
-                if (newPointerIndex < oldPointers.length) {
-                    pointerData = oldPointers[newPointerIndex];
-                    if (pointerData == null) {
-                        pointerData = new PointerData();
-                        oldPointers[newPointerIndex] = pointerData;
-                    }
-                } else {
-                    final PointerData[] newPointers = new PointerData[newPointerIndex * 2];
-                    System.arraycopy(oldPointers, 0, newPointers, 0, newPointerIndex);
-                    mPointers = newPointers;
-                    pointerData = new PointerData();
-                    newPointers[newPointerIndex] = pointerData;
-                }
-                pointerData.id = pointerId;
-                pointerData.pastTime[lastTouchIndex] = Long.MIN_VALUE;
-                mNumPointers += 1;
+            
+            // Find the pointer data for this pointer id.
+            // This loop is optimized for the common case where pointer ids in the event
+            // are in sorted order.  However, we check for this case explicitly and
+            // perform a full linear scan from the start if needed.
+            Pointer nextPointer;
+            if (previousPointer == null || pointerId < previousPointer.id) {
+                previousPointer = null;
+                nextPointer = mPointerListHead;
+            } else {
+                nextPointer = previousPointer.next;
             }
             
-            final float[] pastX = pointerData.pastX;
-            final float[] pastY = pointerData.pastY;
-            final long[] pastTime = pointerData.pastTime;
+            final Pointer pointer;
+            for (;;) {
+                if (nextPointer != null) {
+                    final int nextPointerId = nextPointer.id;
+                    if (nextPointerId == pointerId) {
+                        pointer = nextPointer;
+                        break;
+                    }
+                    if (nextPointerId < pointerId) {
+                        nextPointer = nextPointer.next;
+                        continue;
+                    }
+                }
+                
+                // Pointer went down.  Add it to the list.
+                // Write a sentinel at the end of the pastTime trace so we will be able to
+                // tell when the trace started.
+                pointer = obtainPointer();
+                pointer.id = pointerId;
+                pointer.pastTime[lastTouchIndex] = Long.MIN_VALUE;
+                pointer.next = nextPointer;
+                if (previousPointer == null) {
+                    mPointerListHead = pointer;
+                } else {
+                    previousPointer.next = pointer;
+                }
+                break;
+            }
+            
+            pointer.generation = generation;
+            previousPointer = pointer;
+            
+            final float[] pastX = pointer.pastX;
+            final float[] pastY = pointer.pastY;
+            final long[] pastTime = pointer.pastTime;
             
             for (int j = 0; j < historySize; j++) {
                 final int touchIndex = (nextTouchIndex + j) % NUM_PAST;
@@ -196,7 +210,23 @@
             pastTime[finalTouchIndex] = ev.getEventTime();
         }
         
-        mLastTouchIndex = finalTouchIndex;
+        // Find removed pointers.
+        previousPointer = null;
+        for (Pointer pointer = mPointerListHead; pointer != null; ) {
+            final Pointer nextPointer = pointer.next;
+            if (pointer.generation != generation) {
+                // Pointer went up.  Remove it from the list.
+                if (previousPointer == null) {
+                    mPointerListHead = nextPointer;
+                } else {
+                    previousPointer.next = nextPointer;
+                }
+                releasePointer(pointer);
+            } else {
+                previousPointer = pointer;
+            }
+            pointer = nextPointer;
+        }
     }
 
     /**
@@ -223,13 +253,10 @@
      * must be positive.
      */
     public void computeCurrentVelocity(int units, float maxVelocity) {
-        final int numPointers = mNumPointers;
-        final PointerData[] pointers = mPointers;
         final int lastTouchIndex = mLastTouchIndex;
         
-        for (int p = 0; p < numPointers; p++) {
-            final PointerData pointerData = pointers[p];
-            final long[] pastTime = pointerData.pastTime;
+        for (Pointer pointer = mPointerListHead; pointer != null; pointer = pointer.next) {
+            final long[] pastTime = pointer.pastTime;
             
             // Search backwards in time for oldest acceptable time.
             // Stop at the beginning of the trace as indicated by the sentinel time Long.MIN_VALUE.
@@ -253,8 +280,8 @@
             }
             
             // Kind-of stupid.
-            final float[] pastX = pointerData.pastX;
-            final float[] pastY = pointerData.pastY;
+            final float[] pastX = pointer.pastX;
+            final float[] pastY = pointer.pastY;
             
             final float oldestX = pastX[oldestTouchIndex];
             final float oldestY = pastY[oldestTouchIndex];
@@ -290,11 +317,11 @@
                 accumY = maxVelocity;
             }
             
-            pointerData.xVelocity = accumX;
-            pointerData.yVelocity = accumY;
+            pointer.xVelocity = accumX;
+            pointer.yVelocity = accumY;
             
             if (localLOGV) {
-                Log.v(TAG, "[" + p + "] Pointer " + pointerData.id
+                Log.v(TAG, "Pointer " + pointer.id
                     + ": Y velocity=" + accumX +" X velocity=" + accumY + " N=" + numTouches);
             }
         }
@@ -307,8 +334,8 @@
      * @return The previously computed X velocity.
      */
     public float getXVelocity() {
-        PointerData pointerData = getPointerData(0);
-        return pointerData != null ? pointerData.xVelocity : 0;
+        Pointer pointer = getPointer(0);
+        return pointer != null ? pointer.xVelocity : 0;
     }
     
     /**
@@ -318,8 +345,8 @@
      * @return The previously computed Y velocity.
      */
     public float getYVelocity() {
-        PointerData pointerData = getPointerData(0);
-        return pointerData != null ? pointerData.yVelocity : 0;
+        Pointer pointer = getPointer(0);
+        return pointer != null ? pointer.yVelocity : 0;
     }
     
     /**
@@ -330,8 +357,8 @@
      * @return The previously computed X velocity.
      */
     public float getXVelocity(int id) {
-        PointerData pointerData = getPointerData(id);
-        return pointerData != null ? pointerData.xVelocity : 0;
+        Pointer pointer = getPointer(id);
+        return pointer != null ? pointer.xVelocity : 0;
     }
     
     /**
@@ -342,19 +369,68 @@
      * @return The previously computed Y velocity.
      */
     public float getYVelocity(int id) {
-        PointerData pointerData = getPointerData(id);
-        return pointerData != null ? pointerData.yVelocity : 0;
+        Pointer pointer = getPointer(id);
+        return pointer != null ? pointer.yVelocity : 0;
     }
     
-    private final PointerData getPointerData(int id) {
-        final PointerData[] pointers = mPointers;
-        final int numPointers = mNumPointers;
-        for (int p = 0; p < numPointers; p++) {
-            PointerData pointerData = pointers[p];
-            if (pointerData.id == id) {
-                return pointerData;
+    private final Pointer getPointer(int id) {
+        for (Pointer pointer = mPointerListHead; pointer != null; pointer = pointer.next) {
+            if (pointer.id == id) {
+                return pointer;
             }
         }
         return null;
     }
+    
+    private static final Pointer obtainPointer() {
+        synchronized (sPool) {
+            if (sRecycledPointerCount != 0) {
+                Pointer element = sRecycledPointerListHead;
+                sRecycledPointerCount -= 1;
+                sRecycledPointerListHead = element.next;
+                element.next = null;
+                return element;
+            }
+        }
+        return new Pointer();
+    }
+    
+    private static final void releasePointer(Pointer pointer) {
+        synchronized (sPool) {
+            if (sRecycledPointerCount < POINTER_POOL_CAPACITY) {
+                pointer.next = sRecycledPointerListHead;
+                sRecycledPointerCount += 1;
+                sRecycledPointerListHead = pointer;
+            }
+        }
+    }
+    
+    private static final void releasePointerList(Pointer pointer) {
+        if (pointer != null) {
+            synchronized (sPool) {
+                int count = sRecycledPointerCount;
+                if (count >= POINTER_POOL_CAPACITY) {
+                    return;
+                }
+                
+                Pointer tail = pointer;
+                for (;;) {
+                    count += 1;
+                    if (count >= POINTER_POOL_CAPACITY) {
+                        break;
+                    }
+                    
+                    Pointer next = tail.next;
+                    if (next == null) {
+                        break;
+                    }
+                    tail = next;
+                }
+
+                tail.next = sRecycledPointerListHead;
+                sRecycledPointerCount = count;
+                sRecycledPointerListHead = pointer;
+            }
+        }
+    }
 }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 8007710..b8623e7 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1358,14 +1358,14 @@
      * Width as measured during measure pass.
      * {@hide}
      */
-    @ViewDebug.ExportedProperty
+    @ViewDebug.ExportedProperty(category = "measurement")
     protected int mMeasuredWidth;
 
     /**
      * Height as measured during measure pass.
      * {@hide}
      */
-    @ViewDebug.ExportedProperty
+    @ViewDebug.ExportedProperty(category = "measurement")
     protected int mMeasuredHeight;
 
     /**
@@ -1575,28 +1575,28 @@
      * to the left edge of this view.
      * {@hide}
      */
-    @ViewDebug.ExportedProperty
+    @ViewDebug.ExportedProperty(category = "layout")
     protected int mLeft;
     /**
      * The distance in pixels from the left edge of this view's parent
      * to the right edge of this view.
      * {@hide}
      */
-    @ViewDebug.ExportedProperty
+    @ViewDebug.ExportedProperty(category = "layout")
     protected int mRight;
     /**
      * The distance in pixels from the top edge of this view's parent
      * to the top edge of this view.
      * {@hide}
      */
-    @ViewDebug.ExportedProperty
+    @ViewDebug.ExportedProperty(category = "layout")
     protected int mTop;
     /**
      * The distance in pixels from the top edge of this view's parent
      * to the bottom edge of this view.
      * {@hide}
      */
-    @ViewDebug.ExportedProperty
+    @ViewDebug.ExportedProperty(category = "layout")
     protected int mBottom;
 
     /**
@@ -1604,14 +1604,14 @@
      * horizontally.
      * {@hide}
      */
-    @ViewDebug.ExportedProperty
+    @ViewDebug.ExportedProperty(category = "scrolling")
     protected int mScrollX;
     /**
      * The offset, in pixels, by which the content of this view is scrolled
      * vertically.
      * {@hide}
      */
-    @ViewDebug.ExportedProperty
+    @ViewDebug.ExportedProperty(category = "scrolling")
     protected int mScrollY;
 
     /**
@@ -1619,28 +1619,28 @@
      * left edge of this view and the left edge of its content.
      * {@hide}
      */
-    @ViewDebug.ExportedProperty
+    @ViewDebug.ExportedProperty(category = "padding")
     protected int mPaddingLeft;
     /**
      * The right padding in pixels, that is the distance in pixels between the
      * right edge of this view and the right edge of its content.
      * {@hide}
      */
-    @ViewDebug.ExportedProperty
+    @ViewDebug.ExportedProperty(category = "padding")
     protected int mPaddingRight;
     /**
      * The top padding in pixels, that is the distance in pixels between the
      * top edge of this view and the top edge of its content.
      * {@hide}
      */
-    @ViewDebug.ExportedProperty
+    @ViewDebug.ExportedProperty(category = "padding")
     protected int mPaddingTop;
     /**
      * The bottom padding in pixels, that is the distance in pixels between the
      * bottom edge of this view and the bottom edge of its content.
      * {@hide}
      */
-    @ViewDebug.ExportedProperty
+    @ViewDebug.ExportedProperty(category = "padding")
     protected int mPaddingBottom;
 
     /**
@@ -1651,13 +1651,13 @@
     /**
      * Cache the paddingRight set by the user to append to the scrollbar's size.
      */
-    @ViewDebug.ExportedProperty
+    @ViewDebug.ExportedProperty(category = "padding")
     int mUserPaddingRight;
 
     /**
      * Cache the paddingBottom set by the user to append to the scrollbar's size.
      */
-    @ViewDebug.ExportedProperty
+    @ViewDebug.ExportedProperty(category = "padding")
     int mUserPaddingBottom;
 
     /**
@@ -1764,14 +1764,14 @@
      * The minimum height of the view. We'll try our best to have the height
      * of this view to at least this amount.
      */
-    @ViewDebug.ExportedProperty
+    @ViewDebug.ExportedProperty(category = "measurement")
     private int mMinHeight;
 
     /**
      * The minimum width of the view. We'll try our best to have the width
      * of this view to at least this amount.
      */
-    @ViewDebug.ExportedProperty
+    @ViewDebug.ExportedProperty(category = "measurement")
     private int mMinWidth;
 
     /**
@@ -2602,7 +2602,7 @@
      *
      * @return True if this view has or contains focus, false otherwise.
      */
-    @ViewDebug.ExportedProperty
+    @ViewDebug.ExportedProperty(category = "focus")
     public boolean hasFocus() {
         return (mPrivateFlags & FOCUSED) != 0;
     }
@@ -2780,7 +2780,7 @@
      *
      * @return True if this view has focus, false otherwise.
      */
-    @ViewDebug.ExportedProperty
+    @ViewDebug.ExportedProperty(category = "focus")
     public boolean isFocused() {
         return (mPrivateFlags & FOCUSED) != 0;
     }
@@ -3191,7 +3191,7 @@
      *
      * @return true if this view has nothing to draw, false otherwise
      */
-    @ViewDebug.ExportedProperty
+    @ViewDebug.ExportedProperty(category = "drawing")
     public boolean willNotDraw() {
         return (mViewFlags & DRAW_MASK) == WILL_NOT_DRAW;
     }
@@ -3214,7 +3214,7 @@
      *
      * @return true if this view does not cache its drawing, false otherwise
      */
-    @ViewDebug.ExportedProperty
+    @ViewDebug.ExportedProperty(category = "drawing")
     public boolean willNotCacheDrawing() {
         return (mViewFlags & WILL_NOT_CACHE_DRAWING) == WILL_NOT_CACHE_DRAWING;
     }
@@ -3357,7 +3357,7 @@
      * @return True if this view can take focus, or false otherwise.
      * @attr ref android.R.styleable#View_focusable
      */
-    @ViewDebug.ExportedProperty
+    @ViewDebug.ExportedProperty(category = "focus")
     public final boolean isFocusable() {
         return FOCUSABLE == (mViewFlags & FOCUSABLE_MASK);
     }
@@ -4666,7 +4666,7 @@
      *
      * @return The width of your view, in pixels.
      */
-    @ViewDebug.ExportedProperty
+    @ViewDebug.ExportedProperty(category = "layout")
     public final int getWidth() {
         return mRight - mLeft;
     }
@@ -4676,7 +4676,7 @@
      *
      * @return The height of your view, in pixels.
      */
-    @ViewDebug.ExportedProperty
+    @ViewDebug.ExportedProperty(category = "layout")
     public final int getHeight() {
         return mBottom - mTop;
     }
@@ -5162,7 +5162,7 @@
      *
      * @return True if this View is guaranteed to be fully opaque, false otherwise.
      */
-    @ViewDebug.ExportedProperty
+    @ViewDebug.ExportedProperty(category = "drawing")
     public boolean isOpaque() {
         return (mPrivateFlags & OPAQUE_MASK) == OPAQUE_MASK;
     }
@@ -6247,7 +6247,7 @@
      * @see #setDrawingCacheEnabled(boolean)
      * @see #getDrawingCache()
      */
-    @ViewDebug.ExportedProperty
+    @ViewDebug.ExportedProperty(category = "drawing")
     public boolean isDrawingCacheEnabled() {
         return (mViewFlags & DRAWING_CACHE_ENABLED) == DRAWING_CACHE_ENABLED;
     }
@@ -8116,7 +8116,7 @@
      * @return the offset of the baseline within the widget's bounds or -1
      *         if baseline alignment is not supported
      */
-    @ViewDebug.ExportedProperty
+    @ViewDebug.ExportedProperty(category = "layout")
     public int getBaseline() {
         return -1;
     }
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 09939a6..2ca08ea 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -255,6 +255,14 @@
          * @see #deepExport()
          */
         String prefix() default "";
+
+        /**
+         * Specifies the category the property falls into, such as measurement,
+         * layout, drawing, etc.
+         *
+         * @return the category as String
+         */
+        String category() default "";
     }
 
     /**
@@ -1368,9 +1376,12 @@
                 // TODO: This should happen on the UI thread
                 Object methodValue = method.invoke(view, (Object[]) null);
                 final Class<?> returnType = method.getReturnType();
+                final ExportedProperty property = sAnnotations.get(method);
+                String categoryPrefix =
+                        property.category().length() != 0 ? property.category() + ":" : "";
 
                 if (returnType == int.class) {
-                    final ExportedProperty property = sAnnotations.get(method);
+
                     if (property.resolveId() && context != null) {
                         final int id = (Integer) methodValue;
                         methodValue = resolveId(context, id);
@@ -1378,7 +1389,8 @@
                         final FlagToString[] flagsMapping = property.flagMapping();
                         if (flagsMapping.length > 0) {
                             final int intValue = (Integer) methodValue;
-                            final String valuePrefix = prefix + method.getName() + '_';
+                            final String valuePrefix =
+                                    categoryPrefix + prefix + method.getName() + '_';
                             exportUnrolledFlags(out, flagsMapping, intValue, valuePrefix);
                         }
 
@@ -1402,21 +1414,22 @@
                         }
                     }
                 } else if (returnType == int[].class) {
-                    final ExportedProperty property = sAnnotations.get(method);
                     final int[] array = (int[]) methodValue;
-                    final String valuePrefix = prefix + method.getName() + '_';
+                    final String valuePrefix = categoryPrefix + prefix + method.getName() + '_';
                     final String suffix = "()";
 
                     exportUnrolledArray(context, out, property, array, valuePrefix, suffix);
+
+                    // Probably want to return here, same as for fields.
+                    return;
                 } else if (!returnType.isPrimitive()) {
-                    final ExportedProperty property = sAnnotations.get(method);
                     if (property.deepExport()) {
                         dumpViewProperties(context, methodValue, out, prefix + property.prefix());
                         continue;
                     }
                 }
 
-                writeEntry(out, prefix, method.getName(), "()", methodValue);
+                writeEntry(out, categoryPrefix + prefix, method.getName(), "()", methodValue);
             } catch (IllegalAccessException e) {
             } catch (InvocationTargetException e) {
             }
@@ -1436,9 +1449,12 @@
             try {
                 Object fieldValue = null;
                 final Class<?> type = field.getType();
+                final ExportedProperty property = sAnnotations.get(field);
+                String categoryPrefix =
+                        property.category().length() != 0 ? property.category() + ":" : "";
 
                 if (type == int.class) {
-                    final ExportedProperty property = sAnnotations.get(field);
+
                     if (property.resolveId() && context != null) {
                         final int id = field.getInt(view);
                         fieldValue = resolveId(context, id);
@@ -1446,7 +1462,8 @@
                         final FlagToString[] flagsMapping = property.flagMapping();
                         if (flagsMapping.length > 0) {
                             final int intValue = field.getInt(view);
-                            final String valuePrefix = prefix + field.getName() + '_';
+                            final String valuePrefix =
+                                    categoryPrefix + prefix + field.getName() + '_';
                             exportUnrolledFlags(out, flagsMapping, intValue, valuePrefix);
                         }
 
@@ -1468,9 +1485,8 @@
                         }
                     }
                 } else if (type == int[].class) {
-                    final ExportedProperty property = sAnnotations.get(field);
                     final int[] array = (int[]) field.get(view);
-                    final String valuePrefix = prefix + field.getName() + '_';
+                    final String valuePrefix = categoryPrefix + prefix + field.getName() + '_';
                     final String suffix = "";
 
                     exportUnrolledArray(context, out, property, array, valuePrefix, suffix);
@@ -1478,10 +1494,9 @@
                     // We exit here!
                     return;
                 } else if (!type.isPrimitive()) {
-                    final ExportedProperty property = sAnnotations.get(field);
                     if (property.deepExport()) {
-                        dumpViewProperties(context, field.get(view), out,
-                                prefix + property.prefix());
+                        dumpViewProperties(context, field.get(view), out, prefix
+                                + property.prefix());
                         continue;
                     }
                 }
@@ -1490,7 +1505,7 @@
                     fieldValue = field.get(view);
                 }
 
-                writeEntry(out, prefix, field.getName(), "", fieldValue);
+                writeEntry(out, categoryPrefix + prefix, field.getName(), "", fieldValue);
             } catch (IllegalAccessException e) {
             }
         }
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index e7b6c50..7159929 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -363,7 +363,7 @@
      * @return one of {@link #FOCUS_BEFORE_DESCENDANTS}, {@link #FOCUS_AFTER_DESCENDANTS},
      *   {@link #FOCUS_BLOCK_DESCENDANTS}.
      */
-    @ViewDebug.ExportedProperty(mapping = {
+    @ViewDebug.ExportedProperty(category = "focus", mapping = {
         @ViewDebug.IntToString(from = FOCUS_BEFORE_DESCENDANTS, to = "FOCUS_BEFORE_DESCENDANTS"),
         @ViewDebug.IntToString(from = FOCUS_AFTER_DESCENDANTS, to = "FOCUS_AFTER_DESCENDANTS"),
         @ViewDebug.IntToString(from = FOCUS_BLOCK_DESCENDANTS, to = "FOCUS_BLOCK_DESCENDANTS")
@@ -2764,7 +2764,7 @@
      * @see #setChildrenDrawnWithCacheEnabled(boolean)
      * @see View#setDrawingCacheEnabled(boolean)
      */
-    @ViewDebug.ExportedProperty
+    @ViewDebug.ExportedProperty(category = "drawing")
     public boolean isAlwaysDrawnWithCacheEnabled() {
         return (mGroupFlags & FLAG_ALWAYS_DRAWN_WITH_CACHE) == FLAG_ALWAYS_DRAWN_WITH_CACHE;
     }
@@ -2799,7 +2799,7 @@
      * @see #setAlwaysDrawnWithCacheEnabled(boolean)
      * @see #setChildrenDrawnWithCacheEnabled(boolean)
      */
-    @ViewDebug.ExportedProperty
+    @ViewDebug.ExportedProperty(category = "drawing")
     protected boolean isChildrenDrawnWithCacheEnabled() {
         return (mGroupFlags & FLAG_CHILDREN_DRAWN_WITH_CACHE) == FLAG_CHILDREN_DRAWN_WITH_CACHE;
     }
@@ -2831,7 +2831,7 @@
      * @see #setChildrenDrawingOrderEnabled(boolean)
      * @see #getChildDrawingOrder(int, int)
      */
-    @ViewDebug.ExportedProperty
+    @ViewDebug.ExportedProperty(category = "drawing")
     protected boolean isChildrenDrawingOrderEnabled() {
         return (mGroupFlags & FLAG_USE_CHILD_DRAWING_ORDER) == FLAG_USE_CHILD_DRAWING_ORDER;
     }
@@ -2868,7 +2868,7 @@
      *         {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE}
      *         and {@link #PERSISTENT_ALL_CACHES}
      */
-    @ViewDebug.ExportedProperty(mapping = {
+    @ViewDebug.ExportedProperty(category = "drawing", mapping = {
         @ViewDebug.IntToString(from = PERSISTENT_NO_CACHE,        to = "NONE"),
         @ViewDebug.IntToString(from = PERSISTENT_ALL_CACHES,      to = "ANIMATION"),
         @ViewDebug.IntToString(from = PERSISTENT_SCROLLING_CACHE, to = "SCROLLING"),
@@ -3501,7 +3501,7 @@
          * constants FILL_PARENT (replaced by MATCH_PARENT ,
          * in API Level 8) or WRAP_CONTENT. or an exact size.
          */
-        @ViewDebug.ExportedProperty(mapping = {
+        @ViewDebug.ExportedProperty(category = "layout", mapping = {
             @ViewDebug.IntToString(from = MATCH_PARENT, to = "MATCH_PARENT"),
             @ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT")
         })
@@ -3512,7 +3512,7 @@
          * constants FILL_PARENT (replaced by MATCH_PARENT ,
          * in API Level 8) or WRAP_CONTENT. or an exact size.
          */
-        @ViewDebug.ExportedProperty(mapping = {
+        @ViewDebug.ExportedProperty(category = "layout", mapping = {
             @ViewDebug.IntToString(from = MATCH_PARENT, to = "MATCH_PARENT"),
             @ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT")
         })
@@ -3637,25 +3637,25 @@
         /**
          * The left margin in pixels of the child.
          */
-        @ViewDebug.ExportedProperty
+        @ViewDebug.ExportedProperty(category = "layout")
         public int leftMargin;
 
         /**
          * The top margin in pixels of the child.
          */
-        @ViewDebug.ExportedProperty
+        @ViewDebug.ExportedProperty(category = "layout")
         public int topMargin;
 
         /**
          * The right margin in pixels of the child.
          */
-        @ViewDebug.ExportedProperty
+        @ViewDebug.ExportedProperty(category = "layout")
         public int rightMargin;
 
         /**
          * The bottom margin in pixels of the child.
          */
-        @ViewDebug.ExportedProperty
+        @ViewDebug.ExportedProperty(category = "layout")
         public int bottomMargin;
 
         /**
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index 7ce04cf..57c9055 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -52,7 +52,6 @@
 import android.media.AudioManager;
 
 import java.lang.ref.WeakReference;
-import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.util.ArrayList;
@@ -1869,7 +1868,7 @@
             if (LOCAL_LOGV) Log.v(
                 TAG, "Dispatching key "
                 + msg.obj + " to " + mView);
-            deliverKeyEvent((KeyEvent)msg.obj, true);
+            deliverKeyEvent((KeyEvent)msg.obj, msg.arg1 != 0);
             break;
         case DISPATCH_POINTER: {
             MotionEvent event = (MotionEvent) msg.obj;
@@ -2021,9 +2020,14 @@
     }
     
     private void finishKeyEvent(KeyEvent event) {
+        if (LOCAL_LOGV) Log.v(TAG, "Telling window manager key is finished");
+
         if (mFinishedCallback != null) {
             mFinishedCallback.run();
             mFinishedCallback = null;
+        } else {
+            Slog.w(TAG, "Attempted to tell the input queue that the current key event "
+                    + "is finished but there is no key event actually in progress.");
         }
     }
     
@@ -2483,8 +2487,6 @@
                 ? mView.dispatchKeyEventPreIme(event) : true;
         if (handled) {
             if (sendDone) {
-                if (LOCAL_LOGV) Log.v(
-                    TAG, "Telling window manager key is finished");
                 finishKeyEvent(event);
             }
             return;
@@ -2516,8 +2518,6 @@
                 deliverKeyEventToViewHierarchy(event, sendDone);
                 return;
             } else if (sendDone) {
-                if (LOCAL_LOGV) Log.v(
-                        TAG, "Telling window manager key is finished");
                 finishKeyEvent(event);
             } else {
                 Log.w(TAG, "handleFinishedEvent(seq=" + seq
@@ -2591,8 +2591,6 @@
 
         } finally {
             if (sendDone) {
-                if (LOCAL_LOGV) Log.v(
-                    TAG, "Telling window manager key is finished");
                 finishKeyEvent(event);
             }
             // Let the exception fall through -- the looper will catch
@@ -2776,9 +2774,14 @@
     
     private final InputHandler mInputHandler = new InputHandler() {
         public void handleKey(KeyEvent event, Runnable finishedCallback) {
+            if (mFinishedCallback != null) {
+                Slog.w(TAG, "Received a new key event from the input queue but there is "
+                        + "already an unfinished key event in progress.");
+            }
+
             mFinishedCallback = finishedCallback;
 
-            dispatchKey(event);
+            dispatchKey(event, true);
         }
 
         public void handleMotion(MotionEvent event, Runnable finishedCallback) {
@@ -2789,9 +2792,13 @@
     };
 
     public void dispatchKey(KeyEvent event) {
-        if (event.getAction() == KeyEvent.ACTION_DOWN) {
-            //noinspection ConstantConditions
-            if (false && event.getKeyCode() == KeyEvent.KEYCODE_CAMERA) {
+        dispatchKey(event, false);
+    }
+
+    private void dispatchKey(KeyEvent event, boolean sendDone) {
+        //noinspection ConstantConditions
+        if (false && event.getAction() == KeyEvent.ACTION_DOWN) {
+            if (event.getKeyCode() == KeyEvent.KEYCODE_CAMERA) {
                 if (Config.LOGD) Log.d("keydisp",
                         "===================================================");
                 if (Config.LOGD) Log.d("keydisp", "Focused view Hierarchy is:");
@@ -2804,6 +2811,7 @@
 
         Message msg = obtainMessage(DISPATCH_KEY);
         msg.obj = event;
+        msg.arg1 = sendDone ? 1 : 0;
 
         if (LOCAL_LOGV) Log.v(
             TAG, "sending key " + event + " to " + mView);
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 33757f0..659f9cd 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -779,11 +779,6 @@
      */
     public void enableScreenAfterBoot();
     
-    /**
-     * Called every time the window manager is dispatching a pointer event.
-     */
-    public void dispatchedPointerEventLw(MotionEvent ev, int targetX, int targetY);
-    
     public void setCurrentOrientationLw(int newOrientation);
     
     /**
diff --git a/core/java/android/webkit/WebViewDatabase.java b/core/java/android/webkit/WebViewDatabase.java
index b18419d..7acd9ba 100644
--- a/core/java/android/webkit/WebViewDatabase.java
+++ b/core/java/android/webkit/WebViewDatabase.java
@@ -727,6 +727,9 @@
     }
 
     long getCacheTotalSize() {
+        if (mCacheDatabase == null) {
+            return 0;
+        }
         long size = 0;
         Cursor cursor = null;
         final String query = "SELECT SUM(contentlength) as sum FROM cache";
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 6cfeb68..c970ae6 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -3830,7 +3830,7 @@
          * View type for this view, as returned by
          * {@link android.widget.Adapter#getItemViewType(int) }
          */
-        @ViewDebug.ExportedProperty(mapping = {
+        @ViewDebug.ExportedProperty(category = "list", mapping = {
             @ViewDebug.IntToString(from = ITEM_VIEW_TYPE_IGNORE, to = "ITEM_VIEW_TYPE_IGNORE"),
             @ViewDebug.IntToString(from = ITEM_VIEW_TYPE_HEADER_OR_FOOTER, to = "ITEM_VIEW_TYPE_HEADER_OR_FOOTER")
         })
@@ -3842,7 +3842,7 @@
          * been added to the list view and whether they should be treated as
          * recycled views or not.
          */
-        @ViewDebug.ExportedProperty
+        @ViewDebug.ExportedProperty(category = "list")
         boolean recycledHeaderFooter;
 
         /**
@@ -3853,7 +3853,7 @@
          * view to be attached to the window rather than just attached to the
          * parent.
          */
-        @ViewDebug.ExportedProperty
+        @ViewDebug.ExportedProperty(category = "list")
         boolean forceAdd;
 
         public LayoutParams(Context c, AttributeSet attrs) {
diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java
index fe6d91a..10a8729 100644
--- a/core/java/android/widget/AdapterView.java
+++ b/core/java/android/widget/AdapterView.java
@@ -56,7 +56,7 @@
     /**
      * The position of the first child displayed
      */
-    @ViewDebug.ExportedProperty
+    @ViewDebug.ExportedProperty(category = "scrolling")
     int mFirstPosition = 0;
 
     /**
@@ -141,7 +141,7 @@
      * The position within the adapter's data set of the item to select
      * during the next layout.
      */
-    @ViewDebug.ExportedProperty    
+    @ViewDebug.ExportedProperty(category = "list")
     int mNextSelectedPosition = INVALID_POSITION;
 
     /**
@@ -152,7 +152,7 @@
     /**
      * The position within the adapter's data set of the currently selected item.
      */
-    @ViewDebug.ExportedProperty    
+    @ViewDebug.ExportedProperty(category = "list")
     int mSelectedPosition = INVALID_POSITION;
 
     /**
@@ -168,7 +168,7 @@
     /**
      * The number of items in the current adapter.
      */
-    @ViewDebug.ExportedProperty
+    @ViewDebug.ExportedProperty(category = "list")
     int mItemCount;
 
     /**
diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java
index e27bb4fe..e445180 100644
--- a/core/java/android/widget/FrameLayout.java
+++ b/core/java/android/widget/FrameLayout.java
@@ -46,27 +46,32 @@
  */
 @RemoteView
 public class FrameLayout extends ViewGroup {
-    @ViewDebug.ExportedProperty
+    @ViewDebug.ExportedProperty(category = "measurement")
     boolean mMeasureAllChildren = false;
 
-    @ViewDebug.ExportedProperty
+    @ViewDebug.ExportedProperty(category = "drawing")
     private Drawable mForeground;
-    @ViewDebug.ExportedProperty
+
+    @ViewDebug.ExportedProperty(category = "padding")
     private int mForegroundPaddingLeft = 0;
-    @ViewDebug.ExportedProperty
+
+    @ViewDebug.ExportedProperty(category = "padding")
     private int mForegroundPaddingTop = 0;
-    @ViewDebug.ExportedProperty
+
+    @ViewDebug.ExportedProperty(category = "padding")
     private int mForegroundPaddingRight = 0;
-    @ViewDebug.ExportedProperty
+
+    @ViewDebug.ExportedProperty(category = "padding")
     private int mForegroundPaddingBottom = 0;
 
     private final Rect mSelfBounds = new Rect();
     private final Rect mOverlayBounds = new Rect();
-    @ViewDebug.ExportedProperty
+
+    @ViewDebug.ExportedProperty(category = "drawing")
     private int mForegroundGravity = Gravity.FILL;
 
     /** {@hide} */
-    @ViewDebug.ExportedProperty
+    @ViewDebug.ExportedProperty(category = "drawing")
     protected boolean mForegroundInPadding = true;
 
     boolean mForegroundBoundsChanged = false;
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index bd07e1f..0525891 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -50,7 +50,7 @@
      * Whether the children of this layout are baseline aligned.  Only applicable
      * if {@link #mOrientation} is horizontal.
      */
-    @ViewDebug.ExportedProperty
+    @ViewDebug.ExportedProperty(category = "layout")
     private boolean mBaselineAligned = true;
 
     /**
@@ -60,7 +60,7 @@
      * Note: this is orthogonal to {@link #mBaselineAligned}, which is concerned
      * with whether the children of this layout are baseline aligned.
      */
-    @ViewDebug.ExportedProperty
+    @ViewDebug.ExportedProperty(category = "layout")
     private int mBaselineAlignedChildIndex = -1;
 
     /**
@@ -68,12 +68,13 @@
      * We'll calculate the baseline of this layout as we measure vertically; for
      * horizontal linear layouts, the offset of 0 is appropriate.
      */
-    @ViewDebug.ExportedProperty
+    @ViewDebug.ExportedProperty(category = "measurement")
     private int mBaselineChildTop = 0;
 
-    @ViewDebug.ExportedProperty
+    @ViewDebug.ExportedProperty(category = "measurement")
     private int mOrientation;
-    @ViewDebug.ExportedProperty(mapping = {
+
+    @ViewDebug.ExportedProperty(category = "measurement", mapping = {
             @ViewDebug.IntToString(from =  -1,                       to = "NONE"),
             @ViewDebug.IntToString(from = Gravity.NO_GRAVITY,        to = "NONE"),
             @ViewDebug.IntToString(from = Gravity.TOP,               to = "TOP"),
@@ -88,13 +89,14 @@
             @ViewDebug.IntToString(from = Gravity.FILL,              to = "FILL")
         })
     private int mGravity = Gravity.LEFT | Gravity.TOP;
-    @ViewDebug.ExportedProperty
+
+    @ViewDebug.ExportedProperty(category = "measurement")
     private int mTotalLength;
 
-    @ViewDebug.ExportedProperty
+    @ViewDebug.ExportedProperty(category = "layout")
     private float mWeightSum;
 
-    @ViewDebug.ExportedProperty
+    @ViewDebug.ExportedProperty(category = "layout")
     private boolean mUseLargestChild;
 
     private int[] mMaxAscent;
@@ -1364,7 +1366,7 @@
          * 0 if the view should not be stretched. Otherwise the extra pixels
          * will be pro-rated among all views whose weight is greater than 0.
          */
-        @ViewDebug.ExportedProperty
+        @ViewDebug.ExportedProperty(category = "layout")
         public float weight;
 
         /**
@@ -1372,7 +1374,7 @@
          *
          * @see android.view.Gravity
          */
-        @ViewDebug.ExportedProperty(mapping = {
+        @ViewDebug.ExportedProperty(category = "layout", mapping = {
             @ViewDebug.IntToString(from =  -1,                       to = "NONE"),
             @ViewDebug.IntToString(from = Gravity.NO_GRAVITY,        to = "NONE"),
             @ViewDebug.IntToString(from = Gravity.TOP,               to = "TOP"),
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index 892c44a..ec6dbb7 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -1140,7 +1140,7 @@
      *         UNSPECIFIED/AT_MOST modes, false otherwise.
      * @hide
      */
-    @ViewDebug.ExportedProperty
+    @ViewDebug.ExportedProperty(category = "list")
     protected boolean recycleOnMeasure() {
         return true;
     }
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index 8e9eb05..c0a546d 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -336,7 +336,7 @@
      *
      * @return true if the progress bar is in indeterminate mode
      */
-    @ViewDebug.ExportedProperty
+    @ViewDebug.ExportedProperty(category = "progress")
     public synchronized boolean isIndeterminate() {
         return mIndeterminate;
     }
@@ -609,7 +609,7 @@
      * @see #setMax(int)
      * @see #getMax()
      */
-    @ViewDebug.ExportedProperty
+    @ViewDebug.ExportedProperty(category = "progress")
     public synchronized int getProgress() {
         return mIndeterminate ? 0 : mProgress;
     }
@@ -626,7 +626,7 @@
      * @see #setMax(int)
      * @see #getMax()
      */
-    @ViewDebug.ExportedProperty
+    @ViewDebug.ExportedProperty(category = "progress")
     public synchronized int getSecondaryProgress() {
         return mIndeterminate ? 0 : mSecondaryProgress;
     }
@@ -640,7 +640,7 @@
      * @see #getProgress()
      * @see #getSecondaryProgress()
      */
-    @ViewDebug.ExportedProperty
+    @ViewDebug.ExportedProperty(category = "progress")
     public synchronized int getMax() {
         return mMax;
     }
diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java
index 1aa1df3..64cda49 100644
--- a/core/java/android/widget/RelativeLayout.java
+++ b/core/java/android/widget/RelativeLayout.java
@@ -1011,7 +1011,7 @@
      * @attr ref android.R.styleable#RelativeLayout_Layout_layout_centerVertical
      */
     public static class LayoutParams extends ViewGroup.MarginLayoutParams {
-        @ViewDebug.ExportedProperty(resolveId = true, indexMapping = {
+        @ViewDebug.ExportedProperty(category = "layout", resolveId = true, indexMapping = {
             @ViewDebug.IntToString(from = ABOVE,               to = "above"),
             @ViewDebug.IntToString(from = ALIGN_BASELINE,      to = "alignBaseline"),
             @ViewDebug.IntToString(from = ALIGN_BOTTOM,        to = "alignBottom"),
@@ -1040,7 +1040,7 @@
          * When true, uses the parent as the anchor if the anchor doesn't exist or if
          * the anchor's visibility is GONE.
          */
-        @ViewDebug.ExportedProperty
+        @ViewDebug.ExportedProperty(category = "layout")
         public boolean alignWithParent;
 
         public LayoutParams(Context c, AttributeSet attrs) {
diff --git a/core/java/android/widget/TableRow.java b/core/java/android/widget/TableRow.java
index 48d12df..b612004 100644
--- a/core/java/android/widget/TableRow.java
+++ b/core/java/android/widget/TableRow.java
@@ -387,13 +387,13 @@
         /**
          * <p>The column index of the cell represented by the widget.</p>
          */
-        @ViewDebug.ExportedProperty
+        @ViewDebug.ExportedProperty(category = "layout")
         public int column;
 
         /**
          * <p>The number of columns the widgets spans over.</p>
          */
-        @ViewDebug.ExportedProperty
+        @ViewDebug.ExportedProperty(category = "layout")
         public int span;
 
         private static final int LOCATION = 0;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 950012c..27e0e94 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -5733,7 +5733,7 @@
     /**
      * Convenience for {@link Selection#getSelectionStart}.
      */
-    @ViewDebug.ExportedProperty
+    @ViewDebug.ExportedProperty(category = "text")
     public int getSelectionStart() {
         return Selection.getSelectionStart(getText());
     }
@@ -5741,7 +5741,7 @@
     /**
      * Convenience for {@link Selection#getSelectionEnd}.
      */
-    @ViewDebug.ExportedProperty
+    @ViewDebug.ExportedProperty(category = "text")
     public int getSelectionEnd() {
         return Selection.getSelectionEnd(getText());
     }
@@ -7295,7 +7295,7 @@
         return false;
     }
 
-    @ViewDebug.ExportedProperty
+    @ViewDebug.ExportedProperty(category = "text")
     private CharSequence            mText;
     private CharSequence            mTransformed;
     private BufferType              mBufferType = BufferType.NORMAL;
diff --git a/core/java/com/android/internal/app/IMediaContainerService.aidl b/core/java/com/android/internal/app/IMediaContainerService.aidl
index 89649a9..5d1f632 100755
--- a/core/java/com/android/internal/app/IMediaContainerService.aidl
+++ b/core/java/com/android/internal/app/IMediaContainerService.aidl
@@ -19,6 +19,7 @@
 import android.net.Uri;
 import android.os.ParcelFileDescriptor;
 import android.content.pm.PackageInfoLite;
+import android.content.res.ObbInfo;
 
 interface IMediaContainerService {
     String copyResourceToContainer(in Uri packageURI,
@@ -28,4 +29,5 @@
                 in ParcelFileDescriptor outStream);
     PackageInfoLite getMinimalPackageInfo(in Uri fileUri, int flags);
     boolean checkFreeStorage(boolean external, in Uri fileUri);
+    ObbInfo getObbInfo(String filename);
 }
diff --git a/core/java/com/android/internal/app/PlatLogoActivity.java b/core/java/com/android/internal/app/PlatLogoActivity.java
new file mode 100644
index 0000000..ce5959d
--- /dev/null
+++ b/core/java/com/android/internal/app/PlatLogoActivity.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2010 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.app;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.ImageView;
+
+public class PlatLogoActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        
+        ImageView content = new ImageView(this);
+        content.setImageResource(com.android.internal.R.drawable.platlogo);
+        content.setScaleType(ImageView.ScaleType.FIT_CENTER);
+        
+        setContentView(content);
+    }
+}
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 5c37c7c..efdc399 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -135,7 +135,8 @@
 	android_backup_BackupDataOutput.cpp \
 	android_backup_FileBackupHelperBase.cpp \
 	android_backup_BackupHelperDispatcher.cpp \
-	android_content_res_ObbScanner.cpp
+	android_content_res_ObbScanner.cpp \
+    android_content_res_Configuration.cpp
 
 LOCAL_C_INCLUDES += \
 	$(JNI_H_INCLUDE) \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 7fe56a7..62ca2ef 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -167,6 +167,7 @@
 extern int register_android_view_KeyEvent(JNIEnv* env);
 extern int register_android_view_MotionEvent(JNIEnv* env);
 extern int register_android_content_res_ObbScanner(JNIEnv* env);
+extern int register_android_content_res_Configuration(JNIEnv* env);
 
 static AndroidRuntime* gCurRuntime = NULL;
 
@@ -1340,6 +1341,7 @@
     REG_JNI(register_android_view_MotionEvent),
 
     REG_JNI(register_android_content_res_ObbScanner),
+    REG_JNI(register_android_content_res_Configuration),
 };
 
 /*
diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp
index 1feb3b3..0932473a 100644
--- a/core/jni/android_app_NativeActivity.cpp
+++ b/core/jni/android_app_NativeActivity.cpp
@@ -600,7 +600,7 @@
 static jint
 loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jobject messageQueue,
         jstring internalDataDir, jstring externalDataDir, int sdkVersion,
-        jobject jAssetMgr)
+        jobject jAssetMgr, jbyteArray savedState)
 {
     LOG_TRACE("loadNativeCode_native");
 
@@ -666,7 +666,18 @@
         
         code->assetManager = assetManagerForJavaObject(env, jAssetMgr);
 
-        code->createActivityFunc(code, NULL, 0);
+        jbyte* rawSavedState = NULL;
+        jsize rawSavedSize = 0;
+        if (savedState != NULL) {
+            rawSavedState = env->GetByteArrayElements(savedState, NULL);
+            rawSavedSize = env->GetArrayLength(savedState);
+        }
+
+        code->createActivityFunc(code, rawSavedState, rawSavedSize);
+
+        if (rawSavedState != NULL) {
+            env->ReleaseByteArrayElements(savedState, rawSavedState, 0);
+        }
     }
     
     return (jint)code;
@@ -706,17 +717,31 @@
     }
 }
 
-static void
+static jbyteArray
 onSaveInstanceState_native(JNIEnv* env, jobject clazz, jint handle)
 {
     LOG_TRACE("onSaveInstanceState_native");
+
+    jbyteArray array = NULL;
+
     if (handle != 0) {
         NativeCode* code = (NativeCode*)handle;
         if (code->callbacks.onSaveInstanceState != NULL) {
             size_t len = 0;
-            code->callbacks.onSaveInstanceState(code, &len);
+            jbyte* state = (jbyte*)code->callbacks.onSaveInstanceState(code, &len);
+            if (len > 0) {
+                array = env->NewByteArray(len);
+                if (array != NULL) {
+                    env->SetByteArrayRegion(array, 0, len, state);
+                }
+            }
+            if (state != NULL) {
+                free(state);
+            }
         }
     }
+
+    return array;
 }
 
 static void
@@ -744,6 +769,18 @@
 }
 
 static void
+onConfigurationChanged_native(JNIEnv* env, jobject clazz, jint handle)
+{
+    LOG_TRACE("onConfigurationChanged_native");
+    if (handle != 0) {
+        NativeCode* code = (NativeCode*)handle;
+        if (code->callbacks.onConfigurationChanged != NULL) {
+            code->callbacks.onConfigurationChanged(code);
+        }
+    }
+}
+
+static void
 onLowMemory_native(JNIEnv* env, jobject clazz, jint handle)
 {
     LOG_TRACE("onLowMemory_native");
@@ -934,14 +971,15 @@
 }
 
 static const JNINativeMethod g_methods[] = {
-    { "loadNativeCode", "(Ljava/lang/String;Landroid/os/MessageQueue;Ljava/lang/String;Ljava/lang/String;ILandroid/content/res/AssetManager;)I",
+    { "loadNativeCode", "(Ljava/lang/String;Landroid/os/MessageQueue;Ljava/lang/String;Ljava/lang/String;ILandroid/content/res/AssetManager;[B)I",
             (void*)loadNativeCode_native },
     { "unloadNativeCode", "(I)V", (void*)unloadNativeCode_native },
     { "onStartNative", "(I)V", (void*)onStart_native },
     { "onResumeNative", "(I)V", (void*)onResume_native },
-    { "onSaveInstanceStateNative", "(I)V", (void*)onSaveInstanceState_native },
+    { "onSaveInstanceStateNative", "(I)[B", (void*)onSaveInstanceState_native },
     { "onPauseNative", "(I)V", (void*)onPause_native },
     { "onStopNative", "(I)V", (void*)onStop_native },
+    { "onConfigurationChangedNative", "(I)V", (void*)onConfigurationChanged_native },
     { "onLowMemoryNative", "(I)V", (void*)onLowMemory_native },
     { "onWindowFocusChangedNative", "(IZ)V", (void*)onWindowFocusChanged_native },
     { "onSurfaceCreatedNative", "(ILandroid/view/Surface;)V", (void*)onSurfaceCreated_native },
diff --git a/core/jni/android_content_res_Configuration.cpp b/core/jni/android_content_res_Configuration.cpp
new file mode 100644
index 0000000..28a43ab
--- /dev/null
+++ b/core/jni/android_content_res_Configuration.cpp
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2010, 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 "Configuration"
+
+#include <utils/Log.h>
+#include "utils/misc.h"
+
+#include "jni.h"
+#include <android_runtime/android_content_res_Configuration.h>
+#include "android_runtime/AndroidRuntime.h"
+
+namespace android {
+
+static struct {
+    jclass clazz;
+
+    jfieldID mcc;
+    jfieldID mnc;
+    jfieldID locale;
+    jfieldID screenLayout;
+    jfieldID touchscreen;
+    jfieldID keyboard;
+    jfieldID keyboardHidden;
+    jfieldID hardKeyboardHidden;
+    jfieldID navigation;
+    jfieldID navigationHidden;
+    jfieldID orientation;
+    jfieldID uiMode;
+} gConfigurationClassInfo;
+
+void android_Configuration_getFromJava(
+        JNIEnv* env, jobject clazz, struct AConfiguration* out) {
+    out->mcc = env->GetIntField(clazz, gConfigurationClassInfo.mcc);
+    out->mnc = env->GetIntField(clazz, gConfigurationClassInfo.mnc);
+    out->screenLayout = env->GetIntField(clazz, gConfigurationClassInfo.screenLayout);
+    out->touchscreen = env->GetIntField(clazz, gConfigurationClassInfo.touchscreen);
+    out->keyboard = env->GetIntField(clazz, gConfigurationClassInfo.keyboard);
+    out->navigation = env->GetIntField(clazz, gConfigurationClassInfo.navigation);
+
+    out->inputFlags = env->GetIntField(clazz, gConfigurationClassInfo.keyboardHidden);
+    int hardKeyboardHidden = env->GetIntField(clazz, gConfigurationClassInfo.hardKeyboardHidden);
+    if (out->inputFlags == ACONFIGURATION_KEYSHIDDEN_NO
+            && hardKeyboardHidden == 2) {
+        out->inputFlags = ACONFIGURATION_KEYSHIDDEN_SOFT;
+    }
+    out->inputFlags |= env->GetIntField(clazz, gConfigurationClassInfo.navigationHidden)
+            << ResTable_config::SHIFT_NAVHIDDEN;
+
+    out->orientation = env->GetIntField(clazz, gConfigurationClassInfo.orientation);
+    out->uiMode = env->GetIntField(clazz, gConfigurationClassInfo.uiMode);
+}
+
+/*
+ * JNI registration.
+ */
+static JNINativeMethod gMethods[] = {
+    /* name, signature, funcPtr */
+    //{ "getObbInfo_native", "(Ljava/lang/String;Landroid/content/res/ObbInfo;)Z",
+    //        (void*) android_content_res_ObbScanner_getObbInfo },
+};
+
+#define FIND_CLASS(var, className) \
+        var = env->FindClass(className); \
+        LOG_FATAL_IF(! var, "Unable to find class " className); \
+        var = jclass(env->NewGlobalRef(var));
+
+#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
+        var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
+        LOG_FATAL_IF(! var, "Unable to find field " fieldName);
+
+int register_android_content_res_Configuration(JNIEnv* env)
+{
+    FIND_CLASS(gConfigurationClassInfo.clazz, "android/content/res/Configuration");
+
+    GET_FIELD_ID(gConfigurationClassInfo.mcc, gConfigurationClassInfo.clazz,
+            "mcc", "I");
+    GET_FIELD_ID(gConfigurationClassInfo.mnc, gConfigurationClassInfo.clazz,
+            "mnc", "I");
+    GET_FIELD_ID(gConfigurationClassInfo.locale, gConfigurationClassInfo.clazz,
+            "locale", "Ljava/util/Locale;");
+    GET_FIELD_ID(gConfigurationClassInfo.screenLayout, gConfigurationClassInfo.clazz,
+            "screenLayout", "I");
+    GET_FIELD_ID(gConfigurationClassInfo.touchscreen, gConfigurationClassInfo.clazz,
+            "touchscreen", "I");
+    GET_FIELD_ID(gConfigurationClassInfo.keyboard, gConfigurationClassInfo.clazz,
+            "keyboard", "I");
+    GET_FIELD_ID(gConfigurationClassInfo.keyboardHidden, gConfigurationClassInfo.clazz,
+            "keyboardHidden", "I");
+    GET_FIELD_ID(gConfigurationClassInfo.hardKeyboardHidden, gConfigurationClassInfo.clazz,
+            "hardKeyboardHidden", "I");
+    GET_FIELD_ID(gConfigurationClassInfo.navigation, gConfigurationClassInfo.clazz,
+            "navigation", "I");
+    GET_FIELD_ID(gConfigurationClassInfo.navigationHidden, gConfigurationClassInfo.clazz,
+            "navigationHidden", "I");
+    GET_FIELD_ID(gConfigurationClassInfo.orientation, gConfigurationClassInfo.clazz,
+            "orientation", "I");
+    GET_FIELD_ID(gConfigurationClassInfo.uiMode, gConfigurationClassInfo.clazz,
+            "uiMode", "I");
+
+    return AndroidRuntime::registerNativeMethods(env, "android/content/res/Configuration", gMethods,
+            NELEM(gMethods));
+}
+
+}; // namespace android
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 82f822f..19b30cc 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1275,6 +1275,9 @@
                 android:finishOnCloseSystemDialogs="true"
                 android:excludeFromRecents="true">
         </activity>
+        <activity android:name="com.android.internal.app.PlatLogoActivity"
+                android:theme="@style/Theme.NoTitleBar.Fullscreen">
+        </activity>
         <activity android:name="com.android.internal.app.DisableCarModeActivity"
                 android:theme="@style/Theme.NoDisplay"
                 android:excludeFromRecents="true">
diff --git a/core/res/res/drawable-nodpi/platlogo.jpg b/core/res/res/drawable-nodpi/platlogo.jpg
new file mode 100644
index 0000000..0e7780c
--- /dev/null
+++ b/core/res/res/drawable-nodpi/platlogo.jpg
Binary files differ
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 8bf5c1b..c442fee 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -739,8 +739,8 @@
     <string name="force_close" msgid="3653416315450806396">"Ukončit aplikaci"</string>
     <string name="report" msgid="4060218260984795706">"Nahlásit"</string>
     <string name="wait" msgid="7147118217226317732">"Počkat"</string>
-    <string name="smv_application" msgid="295583804361236288">"Aplikace <xliff:g id="APPLICATION">%1$s</xliff:g> (proces<xliff:g id="PROCESS">%2$s</xliff:g>) porušila své samovynucované zásady StrictMode."</string>
-    <string name="smv_process" msgid="5120397012047462446">"Proces <xliff:g id="PROCESS">%1$s</xliff:g> porušil své samovynucované zásady StrictMode."</string>
+    <string name="smv_application" msgid="295583804361236288">"Aplikace <xliff:g id="APPLICATION">%1$s</xliff:g> (proces <xliff:g id="PROCESS">%2$s</xliff:g>) porušila své vlastní vynucené zásady StrictMode."</string>
+    <string name="smv_process" msgid="5120397012047462446">"Proces <xliff:g id="PROCESS">%1$s</xliff:g> porušil své vlastní vynucené zásady StrictMode."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"Běží aplikace <xliff:g id="APP">%1$s</xliff:g>"</string>
     <string name="heavy_weight_notification_detail" msgid="2423977499339403402">"Tuto možnost vyberte, chcete-li přepnout na aplikaci."</string>
     <string name="heavy_weight_switcher_title" msgid="1135403633766694316">"Přepnout mezi aplikacemi?"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index c236c97..bf3efd6 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -742,7 +742,7 @@
     <string name="smv_application" msgid="295583804361236288">"Die Anwendung <xliff:g id="APPLICATION">%1$s</xliff:g> (Prozess <xliff:g id="PROCESS">%2$s</xliff:g>) hat gegen ihre selbsterzwungene StrictMode-Richtlinie verstoßen."</string>
     <string name="smv_process" msgid="5120397012047462446">"Der Prozess <xliff:g id="PROCESS">%1$s</xliff:g> hat gegen seine selbsterzwungene StrictMode-Richtlinie verstoßen."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> läuft"</string>
-    <string name="heavy_weight_notification_detail" msgid="2423977499339403402">"Auswahl zum Wechseln in die Anwendung"</string>
+    <string name="heavy_weight_notification_detail" msgid="2423977499339403402">"Auswählen zum Wechseln in die Anwendung"</string>
     <string name="heavy_weight_switcher_title" msgid="1135403633766694316">"Anwendung wechseln?"</string>
     <string name="heavy_weight_switcher_text" msgid="4592075610079319667">"Eine andere Anwendung wird bereits ausgeführt und muss vor dem Start einer neuen Anwendung beendet werden."</string>
     <string name="old_app_action" msgid="493129172238566282">"Zu <xliff:g id="OLD_APP">%1$s</xliff:g> zurückkehren"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 7646b57..3ab7814 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -739,8 +739,8 @@
     <string name="force_close" msgid="3653416315450806396">"Forcer la fermeture"</string>
     <string name="report" msgid="4060218260984795706">"Rapport"</string>
     <string name="wait" msgid="7147118217226317732">"Attendre"</string>
-    <string name="smv_application" msgid="295583804361236288">"L\'application <xliff:g id="APPLICATION">%1$s</xliff:g> (processus <xliff:g id="PROCESS">%2$s</xliff:g>) a enfreint ses propres règles StrictMode."</string>
-    <string name="smv_process" msgid="5120397012047462446">"Le processus <xliff:g id="PROCESS">%1$s</xliff:g> a enfreint ses propres règles StrictMode."</string>
+    <string name="smv_application" msgid="295583804361236288">"L\'application <xliff:g id="APPLICATION">%1$s</xliff:g> (processus <xliff:g id="PROCESS">%2$s</xliff:g>) a enfreint ses propres règles du mode strict."</string>
+    <string name="smv_process" msgid="5120397012047462446">"Le processus <xliff:g id="PROCESS">%1$s</xliff:g> a enfreint ses propres règles du mode strict."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> en cours d\'exécution"</string>
     <string name="heavy_weight_notification_detail" msgid="2423977499339403402">"Sélectionner pour changer d\'application"</string>
     <string name="heavy_weight_switcher_title" msgid="1135403633766694316">"Passer d\'une application à l\'autre ?"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 974e848..4176886 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -173,7 +173,7 @@
     <string name="permlab_statusBar" msgid="7417192629601890791">"отключать или изменять строку состояния"</string>
     <string name="permdesc_statusBar" msgid="1365473595331989732">"Позволяет приложению отключать строку состояния или добавлять/удалять системные значки."</string>
     <string name="permlab_statusBarService" msgid="7247281911387931485">"строка состояния"</string>
-    <string name="permdesc_statusBarService" msgid="4097605867643520920">"Позволяет приложению быть строкой состояния."</string>
+    <string name="permdesc_statusBarService" msgid="4097605867643520920">"Позволяет приложению заменять строку состояния."</string>
     <string name="permlab_expandStatusBar" msgid="1148198785937489264">"разворачивать/сворачивать строку состояния"</string>
     <string name="permdesc_expandStatusBar" msgid="7088604400110768665">"Позволяет приложению разворачивать или сворачивать строку состояния."</string>
     <string name="permlab_processOutgoingCalls" msgid="1136262550878335980">"перехватывать исходящие вызовы"</string>
@@ -315,7 +315,7 @@
     <string name="permlab_recordAudio" msgid="3876049771427466323">"записывать аудио"</string>
     <string name="permdesc_recordAudio" msgid="6493228261176552356">"Позволяет приложению получать доступ к пути аудиозаписи."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"снимать фото и видео"</string>
-    <string name="permdesc_camera" msgid="6004878235852154239">"Позволяет приложению делать снимки и видео с помощью камеры. Это дает приложению возможность в любое время получать изображения с объектива камеры."</string>
+    <string name="permdesc_camera" msgid="6004878235852154239">"Позволяет приложению делать снимки и видео с помощью камеры в любое время."</string>
     <string name="permlab_brick" msgid="8337817093326370537">"отключать телефон"</string>
     <string name="permdesc_brick" msgid="5569526552607599221">"Позволяет данному приложению отключить телефон навсегда. Это очень опасно."</string>
     <string name="permlab_reboot" msgid="2898560872462638242">"принудительно перезагружать телефон"</string>
@@ -742,7 +742,7 @@
     <string name="smv_application" msgid="295583804361236288">"Приложение <xliff:g id="APPLICATION">%1$s</xliff:g> (процесс <xliff:g id="PROCESS">%2$s</xliff:g>) нарушило собственную политику StrictMode."</string>
     <string name="smv_process" msgid="5120397012047462446">"Процесс <xliff:g id="PROCESS">%1$s</xliff:g> нарушил собственную политику StrictMode."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"Приложение <xliff:g id="APP">%1$s</xliff:g> запущено"</string>
-    <string name="heavy_weight_notification_detail" msgid="2423977499339403402">"Нажмите, чтобы переключиться в приложение"</string>
+    <string name="heavy_weight_notification_detail" msgid="2423977499339403402">"Нажмите, чтобы перейти к приложению"</string>
     <string name="heavy_weight_switcher_title" msgid="1135403633766694316">"Переключить приложения?"</string>
     <string name="heavy_weight_switcher_text" msgid="4592075610079319667">"Выполняется другое приложение, которое должно быть остановлено прежде, чем запускать новое."</string>
     <string name="old_app_action" msgid="493129172238566282">"Вернуться к приложению <xliff:g id="OLD_APP">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 6d56991..f2fd734 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -739,8 +739,8 @@
     <string name="force_close" msgid="3653416315450806396">"Kapanmaya zorla"</string>
     <string name="report" msgid="4060218260984795706">"Rapor"</string>
     <string name="wait" msgid="7147118217226317732">"Bekle"</string>
-    <string name="smv_application" msgid="295583804361236288">"<xliff:g id="APPLICATION">%1$s</xliff:g> uygulaması (<xliff:g id="PROCESS">%2$s</xliff:g> işlemi) kendiliğinden zorunlu StrictMode politikasını ihlal etti."</string>
-    <string name="smv_process" msgid="5120397012047462446">"<xliff:g id="PROCESS">%1$s</xliff:g> işlemi kendiliğinden zorunlu StrictMode politikasını ihlal etti."</string>
+    <string name="smv_application" msgid="295583804361236288">"<xliff:g id="APPLICATION">%1$s</xliff:g> uygulaması (<xliff:g id="PROCESS">%2$s</xliff:g> işlemi) kendiliğinden uyguladığı StrictMode politikasını ihlal etti."</string>
+    <string name="smv_process" msgid="5120397012047462446">"<xliff:g id="PROCESS">%1$s</xliff:g> işlemi kendiliğinden uyguladığı StrictMode politikasını ihlal etti."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> çalışıyor"</string>
     <string name="heavy_weight_notification_detail" msgid="2423977499339403402">"Uygulama değiştirmeyi seçin"</string>
     <string name="heavy_weight_switcher_title" msgid="1135403633766694316">"Uygulamaların arasında geçiş yapılsın mı?"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 486e125..a0fb721 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -315,7 +315,7 @@
     <string name="permlab_recordAudio" msgid="3876049771427466323">"录音"</string>
     <string name="permdesc_recordAudio" msgid="6493228261176552356">"允许应用程序访问录音路径。"</string>
     <string name="permlab_camera" msgid="3616391919559751192">"拍摄照片和视频"</string>
-    <string name="permdesc_camera" msgid="6004878235852154239">"允许应用程序使用相机拍摄照片和视频,这样应用程序可随时收集进入相机镜头中看到的图片。"</string>
+    <string name="permdesc_camera" msgid="6004878235852154239">"允许应用程序使用相机拍摄照片和视频,这样应用程序可随时收集进入相机镜头中的图片。"</string>
     <string name="permlab_brick" msgid="8337817093326370537">"永久停用手机"</string>
     <string name="permdesc_brick" msgid="5569526552607599221">"允许应用程序永久停用整个手机,这非常危险。"</string>
     <string name="permlab_reboot" msgid="2898560872462638242">"强行重新启动手机"</string>
@@ -740,7 +740,7 @@
     <string name="report" msgid="4060218260984795706">"报告"</string>
     <string name="wait" msgid="7147118217226317732">"等待"</string>
     <string name="smv_application" msgid="295583804361236288">"应用程序<xliff:g id="APPLICATION">%1$s</xliff:g>(<xliff:g id="PROCESS">%2$s</xliff:g> 进程)违反了自我强制执行的严格模式 (StrictMode) 政策。"</string>
-    <string name="smv_process" msgid="5120397012047462446">"进程 <xliff:g id="PROCESS">%1$s</xliff:g> 违反了自我强制执行的严格模式 (StrictMode) 政策"</string>
+    <string name="smv_process" msgid="5120397012047462446">"进程 <xliff:g id="PROCESS">%1$s</xliff:g> 违反了自我强制执行的严格模式 (StrictMode) 政策。"</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g>正在运行"</string>
     <string name="heavy_weight_notification_detail" msgid="2423977499339403402">"选择以切换到该应用程序"</string>
     <string name="heavy_weight_switcher_title" msgid="1135403633766694316">"要切换应用程序吗?"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 560d407..d5dd857 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -34,7 +34,7 @@
     <string name="defaultVoiceMailAlphaTag" msgid="2660020990097733077">"語音留言"</string>
     <string name="defaultMsisdnAlphaTag" msgid="2850889754919584674">"MSISDN1"</string>
     <string name="mmiError" msgid="5154499457739052907">"連線發生問題或錯誤的 MMI 碼。"</string>
-    <string name="mmiFdnError" msgid="5224398216385316471">"僅允許在固定撥號時使用此操作。"</string>
+    <string name="mmiFdnError" msgid="5224398216385316471">"僅限對固定撥號號碼執行此作業。"</string>
     <string name="serviceEnabled" msgid="8147278346414714315">"服務已啟用。"</string>
     <string name="serviceEnabledFor" msgid="6856228140453471041">"已啟用服務:"</string>
     <string name="serviceDisabled" msgid="1937553226592516411">"服務已停用。"</string>
@@ -173,7 +173,7 @@
     <string name="permlab_statusBar" msgid="7417192629601890791">"停用或變更狀態列"</string>
     <string name="permdesc_statusBar" msgid="1365473595331989732">"允許應用程式停用狀態列或新增、移除系統圖示。"</string>
     <string name="permlab_statusBarService" msgid="7247281911387931485">"狀態列"</string>
-    <string name="permdesc_statusBarService" msgid="4097605867643520920">"允許應用程式成為狀態列。"</string>
+    <string name="permdesc_statusBarService" msgid="4097605867643520920">"允許應用程式以狀態列顯示。"</string>
     <string name="permlab_expandStatusBar" msgid="1148198785937489264">"展開/收攏狀態列"</string>
     <string name="permdesc_expandStatusBar" msgid="7088604400110768665">"允許應用程式展開或收攏狀態列。"</string>
     <string name="permlab_processOutgoingCalls" msgid="1136262550878335980">"攔截撥出電話"</string>
@@ -314,8 +314,8 @@
     <string name="permdesc_modifyAudioSettings" msgid="5793461287365991922">"允許應用程式編輯全域音訊設定,例如音量與路由。"</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"錄製音訊"</string>
     <string name="permdesc_recordAudio" msgid="6493228261176552356">"允許應用程式存取音訊錄製路徑。"</string>
-    <string name="permlab_camera" msgid="3616391919559751192">"拍照和錄影"</string>
-    <string name="permdesc_camera" msgid="6004878235852154239">"允許應用程式使用相機拍照和錄影。此功能可讓應用程式隨時透過相機收集圖片。"</string>
+    <string name="permlab_camera" msgid="3616391919559751192">"拍照和拍攝影片"</string>
+    <string name="permdesc_camera" msgid="6004878235852154239">"允許應用程式使用相機拍照和錄影,此功能可讓應用程式隨時透過相機收集圖片。"</string>
     <string name="permlab_brick" msgid="8337817093326370537">"永久停用電話"</string>
     <string name="permdesc_brick" msgid="5569526552607599221">"允許應用程式永久停用手機。此項操作非常危險。"</string>
     <string name="permlab_reboot" msgid="2898560872462638242">"強制重開機"</string>
@@ -739,12 +739,12 @@
     <string name="force_close" msgid="3653416315450806396">"強制關閉"</string>
     <string name="report" msgid="4060218260984795706">"回報"</string>
     <string name="wait" msgid="7147118217226317732">"等待"</string>
-    <string name="smv_application" msgid="295583804361236288">"應用程式 <xliff:g id="APPLICATION">%1$s</xliff:g> (處理程序 <xliff:g id="PROCESS">%2$s</xliff:g>) 已違反其自行實施的 StrictMode 政策。"</string>
-    <string name="smv_process" msgid="5120397012047462446">"處理程序 <xliff:g id="PROCESS">%1$s</xliff:g> 已違反其自行實施的 StrictMode 政策。"</string>
+    <string name="smv_application" msgid="295583804361236288">"應用程式 <xliff:g id="APPLICATION">%1$s</xliff:g> (處理程序 <xliff:g id="PROCESS">%2$s</xliff:g>) 已違反其自行強制實施的嚴格模式 (StrictMode) 政策。"</string>
+    <string name="smv_process" msgid="5120397012047462446">"處理程序 <xliff:g id="PROCESS">%1$s</xliff:g> 已違反其自行強制實施的嚴格模式 (StrictMode) 政策。"</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> 執行中"</string>
-    <string name="heavy_weight_notification_detail" msgid="2423977499339403402">"選取切換應用程式"</string>
+    <string name="heavy_weight_notification_detail" msgid="2423977499339403402">"選取以切換到該應用程式"</string>
     <string name="heavy_weight_switcher_title" msgid="1135403633766694316">"切換應用程式?"</string>
-    <string name="heavy_weight_switcher_text" msgid="4592075610079319667">"其他應用程式已在執行中,您必須停止執行,才能啟動新的應用程式。"</string>
+    <string name="heavy_weight_switcher_text" msgid="4592075610079319667">"其他應用程式已在執行中,您必須停止執行該應用程式,才能啟動新的應用程式。"</string>
     <string name="old_app_action" msgid="493129172238566282">"返回 <xliff:g id="OLD_APP">%1$s</xliff:g>"</string>
     <string name="old_app_description" msgid="942967900237208466">"請勿啟動新的應用程式。"</string>
     <string name="new_app_action" msgid="5472756926945440706">"啟動 <xliff:g id="OLD_APP">%1$s</xliff:g>"</string>
diff --git a/core/tests/coretests/Android.mk b/core/tests/coretests/Android.mk
index 01faaad..693ef18 100644
--- a/core/tests/coretests/Android.mk
+++ b/core/tests/coretests/Android.mk
@@ -11,11 +11,9 @@
 	$(call all-java-files-under, DisabledTestApp/src) \
 	$(call all-java-files-under, EnabledTestApp/src)
 
-LOCAL_STATIC_JAVA_LIBRARIES += android-common
-
 LOCAL_DX_FLAGS := --core-library
-LOCAL_STATIC_JAVA_LIBRARIES := core-tests-supportlib
-LOCAL_JAVA_LIBRARIES := android.test.runner android-common
+LOCAL_STATIC_JAVA_LIBRARIES := core-tests-supportlib android-common
+LOCAL_JAVA_LIBRARIES := android.test.runner
 LOCAL_PACKAGE_NAME := FrameworksCoreTests
 
 LOCAL_CERTIFICATE := platform
diff --git a/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java b/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java
index 0fe83e1..cbd8714 100644
--- a/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java
+++ b/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java
@@ -17,13 +17,11 @@
 package android.bluetooth;
 
 import android.app.Instrumentation;
-import android.bluetooth.BluetoothAdapter;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.test.InstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
 import android.util.Log;
 
 public class BluetoothStressTest extends InstrumentationTestCase {
@@ -161,7 +159,6 @@
         mContext.unregisterReceiver(mReceiver);
     }
 
-    @LargeTest
     public void testEnableDisable() {
         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
 
@@ -172,7 +169,6 @@
         }
     }
 
-    @LargeTest
     public void testDiscoverable() {
         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
         enable(adapter);
@@ -186,7 +182,6 @@
         disable(adapter);
     }
 
-    @LargeTest
     public void testScan() {
         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
         enable(adapter);
@@ -336,7 +331,7 @@
         mReceiver.resetFiredFlags();
 
         if (!adapter.isEnabled()) {
-            fail("undiscoverable(): bluetooth not enabled");
+            fail("undiscoverable() bluetooth not enabled");
         }
 
         int scanMode = adapter.getScanMode();
@@ -374,7 +369,7 @@
         mReceiver.resetFiredFlags();
 
         if (!adapter.isEnabled()) {
-            fail("startScan(): bluetooth not enabled");
+            fail("startScan() bluetooth not enabled");
         }
 
         if (adapter.isDiscovering()) {
@@ -404,7 +399,7 @@
         mReceiver.resetFiredFlags();
 
         if (!adapter.isEnabled()) {
-            fail("stopScan(): bluetooth not enabled");
+            fail("stopScan() bluetooth not enabled");
         }
 
         if (!adapter.isDiscovering()) {
diff --git a/docs/html/resources/community-groups.jd b/docs/html/resources/community-groups.jd
index 72bdf7a..651edbc 100644
--- a/docs/html/resources/community-groups.jd
+++ b/docs/html/resources/community-groups.jd
@@ -87,27 +87,27 @@
 <h3 id="ApplicationDeveloperLists">Application developer mailing lists</h3>
 <ul>
 <li><strong><a href="http://groups.google.com/group/android-developers">android-developers</a></strong>
-(<a href="mailto:android-developers-subscribe@googlegroups.com">subscribe via email</a>)<br>
+(<a href="http://groups.google.com/group/android-developers/subscribe">subscribe</a>)<br>
 You're now an experienced Android application developer. You've grasped the basics of Android app development, you're comfortable using the SDK, now you want to move to advanced topics. Get help here with troubleshooting applications, advice on implementation, and strategies for improving your application's performance and user experience. This is the not the right place to discuss user issues (use android-discuss for that) or beginner questions with the Android SDK (use android-beginners for that).
 </li>
 
 <li><strong><a href="http://groups.google.com/group/android-discuss">android-discuss</a></strong>
-(<a href="mailto:android-discuss-subscribe@googlegroups.com">subscribe via email</a>)<br>
+(<a href="http://groups.google.com/group/android-discuss/subscribe">subscribe</a>)<br>
 The "water cooler" of Android discussion. You can discuss just about anything Android-related here, ideas for the Android platform, announcements about your applications, discussions about Android devices, community resources... As long as your discussion is related to Android, it's on-topic here. However, if you have a discussion here that could belong on another list, you are probably not reaching all of your target audience here and may want to consider shifting to a more targeted list.
 </li>
 
 <li><strong><a href="http://groups.google.com/group/android-ndk">android-ndk</a></strong>
-(<a href="mailto:android-ndk-subscribe@googlegroups.com">subscribe via email</a>)<br>
+(<a href="http://groups.google.com/group/android-ndk/subscribe">subscribe</a>)<br>
 A place for discussing the Android NDK and topics related to using native code in Android applications.
 </li>
 
 <li><strong><a href="http://groups.google.com/group/android-security-discuss">android-security-discuss</a></strong>
-(<a href="mailto:android-security-discuss-subscribe@googlegroups.com">subscribe via email</a>)<br>
+(<a href="http://groups.google.com/group/android-security-discuss/subscribe">subscribe</a>)<br>
 A place for open discussion on secure development, emerging security concerns, and best practices for and by android developers. Please don't disclose vulnerabilities directly on this list, you'd be putting all Android users at risk.
 </li>
 
 <li><strong><a href="http://groups.google.com/group/android-security-announce">android-security-announce</a></strong>
-(<a href="mailto:android-security-announce-subscribe@googlegroups.com">subscribe via email</a>)<br>
+(<a href="http://groups.google.com/group/android-security-announce/subscribe">subscribe</a>)<br>
 A low-volume group for security-related announcements by the Android Security Team.
 </li>
 </ul>
diff --git a/include/android_runtime/android_content_res_Configuration.h b/include/android_runtime/android_content_res_Configuration.h
new file mode 100644
index 0000000..2f5a982
--- /dev/null
+++ b/include/android_runtime/android_content_res_Configuration.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#ifndef _ANDROID_CONTENT_RES_CONFIGURATION_H
+#define _ANDROID_CONTENT_RES_CONFIGURATION_H
+
+#include <utils/ResourceTypes.h>
+#include <android/configuration.h>
+
+#include "jni.h"
+
+struct AConfiguration : android::ResTable_config {
+};
+
+namespace android {
+
+extern void android_Configuration_getFromJava(
+        JNIEnv* env, jobject clazz, struct AConfiguration* out);
+
+} // namespace android
+
+
+#endif // _ANDROID_CONTENT_RES_CONFIGURATION_H
diff --git a/include/camera/CameraParameters.h b/include/camera/CameraParameters.h
index e12a694..6a5d254 100644
--- a/include/camera/CameraParameters.h
+++ b/include/camera/CameraParameters.h
@@ -171,10 +171,9 @@
     // Supported flash modes.
     // Example value: "auto,on,off". Read only.
     static const char KEY_SUPPORTED_FLASH_MODES[];
-    // Current focus mode. If the camera does not support auto-focus, the value
-    // should be FOCUS_MODE_FIXED. If the focus mode is not FOCUS_MODE_FIXED or
-    // or FOCUS_MODE_INFINITY, applications should call
-    // CameraHardwareInterface.autoFocus to start the focus.
+    // Current focus mode. This will not be empty. Applications should call
+    // CameraHardwareInterface.autoFocus to start the focus if focus mode is
+    // FOCUS_MODE_AUTO or FOCUS_MODE_MACRO.
     // Example value: "auto" or FOCUS_MODE_XXX constants. Read/write.
     static const char KEY_FOCUS_MODE[];
     // Supported focus modes.
@@ -231,11 +230,16 @@
     // be in focus. The object is sharpest at the optimal focus distance. The
     // depth of field is the far focus distance minus near focus distance.
     //
-    // Applications can read this parameter anytime to get the latest focus
-    // distances. If the focus mode is FOCUS_MODE_EDOF, the values may be all
-    // 0, which means focus distance is not applicable. If the focus mode is
-    // FOCUS_MODE_CONTINUOUS and autofocus has started, focus distances may
-    // change from time to time.
+    // Focus distances may change after starting auto focus, canceling auto
+    // focus, or starting the preview. Applications can read this anytime to get
+    // the latest focus distances. If the focus mode is FOCUS_MODE_CONTINUOUS,
+    // focus distances may change from time to time.
+    //
+    // This is intended to estimate the distance between the camera and the
+    // subject. After autofocus, the subject distance may be within near and far
+    // focus distance. However, the precision depends on the camera hardware,
+    // autofocus algorithm, the focus area, and the scene. The error can be
+    // large and it should be only used as a reference.
     //
     // Far focus distance > optimal focus distance > near focus distance. If
     // the far focus distance is infinity, the value should be "Infinity" (case
@@ -348,10 +352,10 @@
     // continuously. Applications should not call
     // CameraHardwareInterface.autoFocus in this mode.
     static const char FOCUS_MODE_EDOF[];
-    // Continuous focus mode. The camera continuously tries to focus. This is
-    // ideal for shooting video or shooting photo of moving object. Continuous
-    // focus starts when CameraHardwareInterface.autoFocus is called. Focus
-    // callback will be only called once as soon as the picture is in focus.
+    // Continuous auto focus mode. The camera continuously tries to focus. This
+    // is ideal for shooting video or shooting photo of moving object. Auto
+    // focus starts when the parameter is set. Applications should not call
+    // CameraHardwareInterface.autoFocus in this mode.
     static const char FOCUS_MODE_CONTINUOUS[];
 
     // The camera determines the exposure by giving more weight to the
diff --git a/include/media/stagefright/AudioSource.h b/include/media/stagefright/AudioSource.h
index f1d45d2..2597e9e 100644
--- a/include/media/stagefright/AudioSource.h
+++ b/include/media/stagefright/AudioSource.h
@@ -60,7 +60,8 @@
     int64_t mStartTimeUs;
     int16_t mMaxAmplitude;
     int64_t mPrevSampleTimeUs;
-    int64_t mNumLostFrames;
+    int64_t mTotalLostFrames;
+    int64_t mPrevLostBytes;
 
     MediaBufferGroup *mGroup;
 
diff --git a/include/ui/InputReader.h b/include/ui/InputReader.h
index f162231..71c6c51 100644
--- a/include/ui/InputReader.h
+++ b/include/ui/InputReader.h
@@ -454,6 +454,8 @@
     virtual void reset();
     virtual void process(const RawEvent* rawEvent);
 
+    virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode);
+
 private:
     // Amount that trackball needs to move in order to generate a key event.
     static const int32_t TRACKBALL_MOVEMENT_THRESHOLD = 6;
diff --git a/include/utils/AssetManager.h b/include/utils/AssetManager.h
index 97694ff..9e2bf37 100644
--- a/include/utils/AssetManager.h
+++ b/include/utils/AssetManager.h
@@ -129,6 +129,8 @@
      */
     void setConfiguration(const ResTable_config& config, const char* locale = NULL);
 
+    void getConfiguration(ResTable_config* outConfig) const;
+
     typedef Asset::AccessMode AccessMode;       // typing shortcut
 
     /*
diff --git a/include/utils/ObbFile.h b/include/utils/ObbFile.h
index 075927c..d2ca82e 100644
--- a/include/utils/ObbFile.h
+++ b/include/utils/ObbFile.h
@@ -35,6 +35,8 @@
     bool readFrom(int fd);
     bool writeTo(const char* filename);
     bool writeTo(int fd);
+    bool removeFrom(const char* filename);
+    bool removeFrom(int fd);
 
     const char* getFileName() const {
         return mFileName;
@@ -78,6 +80,8 @@
 
     size_t mFileSize;
 
+    size_t mFooterStart;
+
     unsigned char* mReadBuf;
 
     bool parseObbFile(int fd);
diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h
index c7d9ff1..da86da4 100644
--- a/include/utils/ResourceTypes.h
+++ b/include/utils/ResourceTypes.h
@@ -31,6 +31,8 @@
 #include <stdint.h>
 #include <sys/types.h>
 
+#include <android/configuration.h>
+
 namespace android {
 
 /** ********************************************************************
@@ -822,25 +824,25 @@
     };
     
     enum {
-        ORIENTATION_ANY  = 0x0000,
-        ORIENTATION_PORT = 0x0001,
-        ORIENTATION_LAND = 0x0002,
-        ORIENTATION_SQUARE = 0x0003,
+        ORIENTATION_ANY  = ACONFIGURATION_ORIENTATION_ANY,
+        ORIENTATION_PORT = ACONFIGURATION_ORIENTATION_PORT,
+        ORIENTATION_LAND = ACONFIGURATION_ORIENTATION_LAND,
+        ORIENTATION_SQUARE = ACONFIGURATION_ORIENTATION_SQUARE,
     };
     
     enum {
-        TOUCHSCREEN_ANY  = 0x0000,
-        TOUCHSCREEN_NOTOUCH  = 0x0001,
-        TOUCHSCREEN_STYLUS  = 0x0002,
-        TOUCHSCREEN_FINGER  = 0x0003,
+        TOUCHSCREEN_ANY  = ACONFIGURATION_TOUCHSCREEN_ANY,
+        TOUCHSCREEN_NOTOUCH  = ACONFIGURATION_TOUCHSCREEN_NOTOUCH,
+        TOUCHSCREEN_STYLUS  = ACONFIGURATION_TOUCHSCREEN_STYLUS,
+        TOUCHSCREEN_FINGER  = ACONFIGURATION_TOUCHSCREEN_FINGER,
     };
     
     enum {
-        DENSITY_DEFAULT = 0,
-        DENSITY_LOW = 120,
-        DENSITY_MEDIUM = 160,
-        DENSITY_HIGH = 240,
-        DENSITY_NONE = 0xffff
+        DENSITY_DEFAULT = ACONFIGURATION_DENSITY_DEFAULT,
+        DENSITY_LOW = ACONFIGURATION_DENSITY_LOW,
+        DENSITY_MEDIUM = ACONFIGURATION_DENSITY_MEDIUM,
+        DENSITY_HIGH = ACONFIGURATION_DENSITY_HIGH,
+        DENSITY_NONE = ACONFIGURATION_DENSITY_NONE
     };
     
     union {
@@ -853,33 +855,34 @@
     };
     
     enum {
-        KEYBOARD_ANY  = 0x0000,
-        KEYBOARD_NOKEYS  = 0x0001,
-        KEYBOARD_QWERTY  = 0x0002,
-        KEYBOARD_12KEY  = 0x0003,
+        KEYBOARD_ANY  = ACONFIGURATION_KEYBOARD_ANY,
+        KEYBOARD_NOKEYS  = ACONFIGURATION_KEYBOARD_NOKEYS,
+        KEYBOARD_QWERTY  = ACONFIGURATION_KEYBOARD_QWERTY,
+        KEYBOARD_12KEY  = ACONFIGURATION_KEYBOARD_12KEY,
     };
     
     enum {
-        NAVIGATION_ANY  = 0x0000,
-        NAVIGATION_NONAV  = 0x0001,
-        NAVIGATION_DPAD  = 0x0002,
-        NAVIGATION_TRACKBALL  = 0x0003,
-        NAVIGATION_WHEEL  = 0x0004,
+        NAVIGATION_ANY  = ACONFIGURATION_NAVIGATION_ANY,
+        NAVIGATION_NONAV  = ACONFIGURATION_NAVIGATION_NONAV,
+        NAVIGATION_DPAD  = ACONFIGURATION_NAVIGATION_DPAD,
+        NAVIGATION_TRACKBALL  = ACONFIGURATION_NAVIGATION_TRACKBALL,
+        NAVIGATION_WHEEL  = ACONFIGURATION_NAVIGATION_WHEEL,
     };
     
     enum {
         MASK_KEYSHIDDEN = 0x0003,
-        KEYSHIDDEN_ANY = 0x0000,
-        KEYSHIDDEN_NO = 0x0001,
-        KEYSHIDDEN_YES = 0x0002,
-        KEYSHIDDEN_SOFT = 0x0003,
+        KEYSHIDDEN_ANY = ACONFIGURATION_KEYSHIDDEN_ANY,
+        KEYSHIDDEN_NO = ACONFIGURATION_KEYSHIDDEN_NO,
+        KEYSHIDDEN_YES = ACONFIGURATION_KEYSHIDDEN_YES,
+        KEYSHIDDEN_SOFT = ACONFIGURATION_KEYSHIDDEN_SOFT,
     };
     
     enum {
         MASK_NAVHIDDEN = 0x000c,
-        NAVHIDDEN_ANY = 0x0000,
-        NAVHIDDEN_NO = 0x0004,
-        NAVHIDDEN_YES = 0x0008,
+        SHIFT_NAVHIDDEN = 2,
+        NAVHIDDEN_ANY = ACONFIGURATION_NAVHIDDEN_ANY << SHIFT_NAVHIDDEN,
+        NAVHIDDEN_NO = ACONFIGURATION_NAVHIDDEN_NO << SHIFT_NAVHIDDEN,
+        NAVHIDDEN_YES = ACONFIGURATION_NAVHIDDEN_YES << SHIFT_NAVHIDDEN,
     };
     
     union {
@@ -929,32 +932,34 @@
     enum {
         // screenLayout bits for screen size class.
         MASK_SCREENSIZE = 0x0f,
-        SCREENSIZE_ANY  = 0x00,
-        SCREENSIZE_SMALL = 0x01,
-        SCREENSIZE_NORMAL = 0x02,
-        SCREENSIZE_LARGE = 0x03,
-        SCREENSIZE_XLARGE = 0x04,
+        SCREENSIZE_ANY = ACONFIGURATION_SCREENSIZE_ANY,
+        SCREENSIZE_SMALL = ACONFIGURATION_SCREENSIZE_SMALL,
+        SCREENSIZE_NORMAL = ACONFIGURATION_SCREENSIZE_NORMAL,
+        SCREENSIZE_LARGE = ACONFIGURATION_SCREENSIZE_LARGE,
+        SCREENSIZE_XLARGE = ACONFIGURATION_SCREENSIZE_XLARGE,
         
         // screenLayout bits for wide/long screen variation.
         MASK_SCREENLONG = 0x30,
-        SCREENLONG_ANY = 0x00,
-        SCREENLONG_NO = 0x10,
-        SCREENLONG_YES = 0x20,
+        SHIFT_SCREENLONG = 4,
+        SCREENLONG_ANY = ACONFIGURATION_SCREENLONG_ANY << SHIFT_SCREENLONG,
+        SCREENLONG_NO = ACONFIGURATION_SCREENLONG_NO << SHIFT_SCREENLONG,
+        SCREENLONG_YES = ACONFIGURATION_SCREENLONG_YES << SHIFT_SCREENLONG,
     };
     
     enum {
         // uiMode bits for the mode type.
         MASK_UI_MODE_TYPE = 0x0f,
-        UI_MODE_TYPE_ANY = 0x00,
-        UI_MODE_TYPE_NORMAL = 0x01,
-        UI_MODE_TYPE_DESK = 0x02,
-        UI_MODE_TYPE_CAR = 0x03,
+        UI_MODE_TYPE_ANY = ACONFIGURATION_UI_MODE_TYPE_ANY,
+        UI_MODE_TYPE_NORMAL = ACONFIGURATION_UI_MODE_TYPE_NORMAL,
+        UI_MODE_TYPE_DESK = ACONFIGURATION_UI_MODE_TYPE_DESK,
+        UI_MODE_TYPE_CAR = ACONFIGURATION_UI_MODE_TYPE_CAR,
 
         // uiMode bits for the night switch.
         MASK_UI_MODE_NIGHT = 0x30,
-        UI_MODE_NIGHT_ANY = 0x00,
-        UI_MODE_NIGHT_NO = 0x10,
-        UI_MODE_NIGHT_YES = 0x20,
+        SHIFT_UI_MODE_NIGHT = 4,
+        UI_MODE_NIGHT_ANY = ACONFIGURATION_UI_MODE_NIGHT_ANY << SHIFT_UI_MODE_NIGHT,
+        UI_MODE_NIGHT_NO = ACONFIGURATION_UI_MODE_NIGHT_NO << SHIFT_UI_MODE_NIGHT,
+        UI_MODE_NIGHT_YES = ACONFIGURATION_UI_MODE_NIGHT_YES << SHIFT_UI_MODE_NIGHT,
     };
 
     union {
@@ -1023,19 +1028,19 @@
     // match the corresponding ones in android.content.pm.ActivityInfo and
     // attrs_manifest.xml.
     enum {
-        CONFIG_MCC = 0x0001,
-        CONFIG_MNC = 0x0002,
-        CONFIG_LOCALE = 0x0004,
-        CONFIG_TOUCHSCREEN = 0x0008,
-        CONFIG_KEYBOARD = 0x0010,
-        CONFIG_KEYBOARD_HIDDEN = 0x0020,
-        CONFIG_NAVIGATION = 0x0040,
-        CONFIG_ORIENTATION = 0x0080,
-        CONFIG_DENSITY = 0x0100,
-        CONFIG_SCREEN_SIZE = 0x0200,
-        CONFIG_VERSION = 0x0400,
-        CONFIG_SCREEN_LAYOUT = 0x0800,
-        CONFIG_UI_MODE = 0x1000
+        CONFIG_MCC = ACONFIGURATION_MCC,
+        CONFIG_MNC = ACONFIGURATION_MCC,
+        CONFIG_LOCALE = ACONFIGURATION_LOCALE,
+        CONFIG_TOUCHSCREEN = ACONFIGURATION_TOUCHSCREEN,
+        CONFIG_KEYBOARD = ACONFIGURATION_KEYBOARD,
+        CONFIG_KEYBOARD_HIDDEN = ACONFIGURATION_KEYBOARD_HIDDEN,
+        CONFIG_NAVIGATION = ACONFIGURATION_NAVIGATION,
+        CONFIG_ORIENTATION = ACONFIGURATION_ORIENTATION,
+        CONFIG_DENSITY = ACONFIGURATION_DENSITY,
+        CONFIG_SCREEN_SIZE = ACONFIGURATION_SCREEN_SIZE,
+        CONFIG_VERSION = ACONFIGURATION_VERSION,
+        CONFIG_SCREEN_LAYOUT = ACONFIGURATION_SCREEN_LAYOUT,
+        CONFIG_UI_MODE = ACONFIGURATION_UI_MODE
     };
     
     // Compare two configuration, returning CONFIG_* flags set for each value
diff --git a/include/utils/String8.h b/include/utils/String8.h
index 4e41410..ef0b51a 100644
--- a/include/utils/String8.h
+++ b/include/utils/String8.h
@@ -171,7 +171,8 @@
             status_t            append(const char* other);
             status_t            append(const char* other, size_t numChars);
 
-            status_t            appendFormat(const char* fmt, ...);
+            status_t            appendFormat(const char* fmt, ...)
+                    __attribute__((format (printf, 2, 3)));
 
             // Note that this function takes O(N) time to calculate the value.
             // No cache value is stored.
diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp
index 6618702..5f5a4ac 100644
--- a/libs/ui/InputReader.cpp
+++ b/libs/ui/InputReader.cpp
@@ -1080,6 +1080,14 @@
             1, & pointerId, pointerCoords, mXPrecision, mYPrecision, downTime);
 }
 
+int32_t TrackballInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
+    if (scanCode >= BTN_MOUSE && scanCode < BTN_JOYSTICK) {
+        return getEventHub()->getScanCodeState(getDeviceId(), scanCode);
+    } else {
+        return AKEY_STATE_UNKNOWN;
+    }
+}
+
 
 // --- TouchInputMapper ---
 
diff --git a/libs/utils/AssetManager.cpp b/libs/utils/AssetManager.cpp
index 60a0d82..e09e755 100644
--- a/libs/utils/AssetManager.cpp
+++ b/libs/utils/AssetManager.cpp
@@ -232,6 +232,12 @@
     }
 }
 
+void AssetManager::getConfiguration(ResTable_config* outConfig) const
+{
+    AutoMutex _l(mLock);
+    *outConfig = *mConfig;
+}
+
 /*
  * Open an asset.
  *
diff --git a/libs/utils/ObbFile.cpp b/libs/utils/ObbFile.cpp
index fe49300..adedf0c 100644
--- a/libs/utils/ObbFile.cpp
+++ b/libs/utils/ObbFile.cpp
@@ -156,9 +156,9 @@
             return false;
         }
 
-        if (footerSize < kFooterMinSize) {
-            LOGW("claimed footer size is too small (%08zx; minimum size is 0x%x)\n",
-                    footerSize, kFooterMinSize);
+        if (footerSize < (kFooterMinSize - kFooterTagSize)) {
+            LOGW("claimed footer size is too small (0x%zx; minimum size is 0x%x)\n",
+                    footerSize, kFooterMinSize - kFooterTagSize);
             return false;
         }
     }
@@ -169,6 +169,8 @@
         return false;
     }
 
+    mFooterStart = fileOffset;
+
     char* scanBuf = (char*)malloc(footerSize);
     if (scanBuf == NULL) {
         LOGW("couldn't allocate scanBuf: %s\n", strerror(errno));
@@ -293,4 +295,38 @@
     return true;
 }
 
+bool ObbFile::removeFrom(const char* filename)
+{
+    int fd;
+    bool success = false;
+
+    fd = ::open(filename, O_RDWR);
+    if (fd < 0) {
+        goto out;
+    }
+    success = removeFrom(fd);
+    close(fd);
+
+out:
+    if (!success) {
+        LOGW("failed to remove signature from %s: %s\n", filename, strerror(errno));
+    }
+    return success;
+}
+
+bool ObbFile::removeFrom(int fd)
+{
+    if (fd < 0) {
+        return false;
+    }
+
+    if (!readFrom(fd)) {
+        return false;
+    }
+
+    ftruncate(fd, mFooterStart);
+
+    return true;
+}
+
 }
diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp
index c113ffe..f69b8ad 100644
--- a/media/jni/android_media_MediaRecorder.cpp
+++ b/media/jni/android_media_MediaRecorder.cpp
@@ -318,6 +318,15 @@
     jobject surface = env->GetObjectField(thiz, fields.surface);
     if (surface != NULL) {
         const sp<Surface> native_surface = get_surface(env, surface);
+
+        // The application may misbehave and
+        // the preview surface becomes unavailable
+        if (native_surface.get() == 0) {
+            LOGE("Application lost the surface");
+            jniThrowException(env, "java/io/IOException", "invalid preview surface");
+            return;
+        }
+
         LOGI("prepare: surface=%p (identity=%d)", native_surface.get(), native_surface->getIdentity());
         if (process_media_recorder_call(env, mr->setPreviewSurface(native_surface), "java/lang/RuntimeException", "setPreviewSurface failed.")) {
             return;
diff --git a/media/libmedia/IMediaRecorder.cpp b/media/libmedia/IMediaRecorder.cpp
index 4eb63e8..947ff34 100644
--- a/media/libmedia/IMediaRecorder.cpp
+++ b/media/libmedia/IMediaRecorder.cpp
@@ -23,6 +23,7 @@
 #include <camera/ICamera.h>
 #include <media/IMediaRecorderClient.h>
 #include <media/IMediaRecorder.h>
+#include <unistd.h>
 
 namespace android {
 
@@ -373,6 +374,7 @@
             int64_t offset = data.readInt64();
             int64_t length = data.readInt64();
             reply->writeInt32(setOutputFile(fd, offset, length));
+            ::close(fd);
             return NO_ERROR;
         } break;
         case SET_VIDEO_SIZE: {
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index f6f89c7..8481d493 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -55,11 +55,6 @@
 StagefrightRecorder::~StagefrightRecorder() {
     LOGV("Destructor");
     stop();
-
-    if (mOutputFd >= 0) {
-        ::close(mOutputFd);
-        mOutputFd = -1;
-    }
 }
 
 status_t StagefrightRecorder::init() {
@@ -1084,6 +1079,11 @@
         mFlags = 0;
     }
 
+    if (mOutputFd >= 0) {
+        ::close(mOutputFd);
+        mOutputFd = -1;
+    }
+
     return OK;
 }
 
diff --git a/media/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp
index 99978e8..c8dfede 100644
--- a/media/libstagefright/AudioSource.cpp
+++ b/media/libstagefright/AudioSource.cpp
@@ -35,7 +35,8 @@
     : mStarted(false),
       mCollectStats(false),
       mPrevSampleTimeUs(0),
-      mNumLostFrames(0),
+      mTotalLostFrames(0),
+      mPrevLostBytes(0),
       mGroup(NULL) {
 
     LOGV("sampleRate: %d, channels: %d", sampleRate, channels);
@@ -108,7 +109,8 @@
     mStarted = false;
 
     if (mCollectStats) {
-        LOGI("Total lost audio frames: %lld", mNumLostFrames);
+        LOGI("Total lost audio frames: %lld",
+            mTotalLostFrames + (mPrevLostBytes >> 1));
     }
 
     return OK;
@@ -186,10 +188,11 @@
         // Insert null frames when lost frames are detected.
         int64_t timestampUs = mPrevSampleTimeUs;
         uint32_t numLostBytes = mRecord->getInputFramesLost() << 1;
+        numLostBytes += mPrevLostBytes;
 #if 0
         // Simulate lost frames
-        numLostBytes = ((rand() * 1.0 / RAND_MAX)) * kMaxBufferSize;
-        numLostBytes &= 0xFFFFFFFE; // Alignment request
+        numLostBytes = ((rand() * 1.0 / RAND_MAX)) * 2 * kMaxBufferSize;
+        numLostBytes &= 0xFFFFFFFE; // Alignment requirement
 
         // Reduce the chance to lose
         if (rand() * 1.0 / RAND_MAX >= 0.05) {
@@ -197,13 +200,18 @@
         }
 #endif
         if (numLostBytes > 0) {
-            // Not expect too many lost frames!
-            CHECK(numLostBytes <= kMaxBufferSize);
+            if (numLostBytes > kMaxBufferSize) {
+                mPrevLostBytes = numLostBytes - kMaxBufferSize;
+                numLostBytes = kMaxBufferSize;
+            }
 
-            timestampUs += (1000000LL * numLostBytes >> 1) / sampleRate;
+            CHECK_EQ(numLostBytes & 1, 0);
+            timestampUs += ((1000000LL * (numLostBytes >> 1)) +
+                    (sampleRate >> 1)) / sampleRate;
+
             CHECK(timestampUs > mPrevSampleTimeUs);
             if (mCollectStats) {
-                mNumLostFrames += (numLostBytes >> 1);
+                mTotalLostFrames += (numLostBytes >> 1);
             }
             if ((err = skipFrame(timestampUs, options)) == -1) {
                 buffer->release();
@@ -240,7 +248,7 @@
 
         buffer->meta_data()->setInt64(kKeyTime, mPrevSampleTimeUs);
         CHECK(timestampUs > mPrevSampleTimeUs);
-        if (mNumLostFrames == 0) {
+        if (mTotalLostFrames == 0) {
             CHECK_EQ(mPrevSampleTimeUs,
                 mStartTimeUs + (1000000LL * numFramesRecorded) / sampleRate);
         }
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index e426fca..f2653cf 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -854,18 +854,6 @@
 
 status_t AwesomePlayer::initVideoDecoder() {
     uint32_t flags = 0;
-#if 0
-    if (mRTPSession != NULL) {
-        // XXX hack.
-
-        const char *mime;
-        CHECK(mVideoTrack->getFormat()->findCString(kKeyMIMEType, &mime));
-        if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
-            flags |= OMXCodec::kPreferSoftwareCodecs;
-        }
-    }
-#endif
-
     mVideoSource = OMXCodec::Create(
             mClient.interface(), mVideoTrack->getFormat(),
             false, // createEncoder
@@ -1019,6 +1007,12 @@
 
     int64_t latenessUs = nowUs - timeUs;
 
+    if (mRTPSession != NULL) {
+        // We'll completely ignore timestamps for gtalk videochat
+        // and we'll play incoming video as fast as we get it.
+        latenessUs = 0;
+    }
+
     if (latenessUs > 40000) {
         // We're more than 40ms late.
         LOGV("we're late by %lld us (%.2f secs)", latenessUs, latenessUs / 1E6);
@@ -1222,7 +1216,7 @@
             MediaExtractor::Create(dataSource, MEDIA_MIMETYPE_CONTAINER_MPEG2TS);
 
         return setDataSource_l(extractor);
-    } else if (!strcmp("rtsp://gtalk", mUri.string())) {
+    } else if (!strncmp("rtsp://gtalk/", mUri.string(), 13)) {
         if (mLooper == NULL) {
             mLooper = new ALooper;
             mLooper->start(
@@ -1231,6 +1225,22 @@
                     PRIORITY_HIGHEST);
         }
 
+        const char *startOfCodecString = &mUri.string()[13];
+        const char *startOfSlash1 = strchr(startOfCodecString, '/');
+        if (startOfSlash1 == NULL) {
+            return BAD_VALUE;
+        }
+        const char *startOfWidthString = &startOfSlash1[1];
+        const char *startOfSlash2 = strchr(startOfWidthString, '/');
+        if (startOfSlash2 == NULL) {
+            return BAD_VALUE;
+        }
+        const char *startOfHeightString = &startOfSlash2[1];
+
+        String8 codecString(startOfCodecString, startOfSlash1 - startOfCodecString);
+        String8 widthString(startOfWidthString, startOfSlash2 - startOfWidthString);
+        String8 heightString(startOfHeightString);
+
 #if 0
         mRTPPusher = new UDPPusher("/data/misc/rtpout.bin", 5434);
         mLooper->registerHandler(mRTPPusher);
@@ -1257,8 +1267,8 @@
             "a=rtpmap:97 AMR/8000/1\r\n"
             "a=fmtp:97 octet-align\r\n";
 #elif 1
-        // My GTalk H.264 SDP
-        static const char *raw =
+        String8 sdp;
+        sdp.appendFormat(
             "v=0\r\n"
             "o=- 64 233572944 IN IP4 127.0.0.0\r\n"
             "s=QuickTime\r\n"
@@ -1268,24 +1278,16 @@
             "m=video 5434 RTP/AVP 97\r\n"
             "c=IN IP4 127.0.0.1\r\n"
             "b=AS:30\r\n"
-            "a=rtpmap:97 H264/90000\r\n"
-            "a=cliprect:0,0,200,320\r\n"
-            "a=framesize:97 320-200\r\n";
-#else
-        // GTalk H263 SDP
-        static const char *raw =
-            "v=0\r\n"
-            "o=- 64 233572944 IN IP4 127.0.0.0\r\n"
-            "s=QuickTime\r\n"
-            "t=0 0\r\n"
-            "a=range:npt=0-315\r\n"
-            "a=isma-compliance:2,2.0,2\r\n"
-            "m=video 5434 RTP/AVP 98\r\n"
-            "c=IN IP4 127.0.0.1\r\n"
-            "b=AS:30\r\n"
-            "a=rtpmap:98 H263-1998/90000\r\n"
-            "a=cliprect:0,0,200,320\r\n"
-            "a=framesize:98 320-200\r\n";
+            "a=rtpmap:97 %s/90000\r\n"
+            "a=cliprect:0,0,%s,%s\r\n"
+            "a=framesize:97 %s-%s\r\n",
+
+            codecString.string(),
+            heightString.string(), widthString.string(),
+            widthString.string(), heightString.string()
+            );
+        const char *raw = sdp.string();
+
 #endif
 
         sp<ASessionDescription> desc = new ASessionDescription;
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
index 9c48daf..3e31d61 100644
--- a/media/libstagefright/CameraSource.cpp
+++ b/media/libstagefright/CameraSource.cpp
@@ -244,6 +244,7 @@
 
 void CameraSource::signalBufferReturned(MediaBuffer *buffer) {
     LOGV("signalBufferReturned: %p", buffer->data());
+    Mutex::Autolock autoLock(mLock);
     for (List<sp<IMemory> >::iterator it = mFramesBeingEncoded.begin();
          it != mFramesBeingEncoded.end(); ++it) {
         if ((*it)->pointer() ==  buffer->data()) {
@@ -312,6 +313,7 @@
                 (*buffer)->setObserver(this);
                 (*buffer)->add_ref();
                 (*buffer)->meta_data()->setInt64(kKeyTime, frameTime);
+
                 return OK;
             }
         }
diff --git a/media/libstagefright/HTTPStream.cpp b/media/libstagefright/HTTPStream.cpp
index 9c99866..ccc6a34 100644
--- a/media/libstagefright/HTTPStream.cpp
+++ b/media/libstagefright/HTTPStream.cpp
@@ -68,7 +68,7 @@
         return UNKNOWN_ERROR;
     }
 
-    setReceiveTimeout(5);  // Time out reads after 5 secs by default
+    setReceiveTimeout(30);  // Time out reads after 30 secs by default
 
     mState = CONNECTING;
 
@@ -158,7 +158,7 @@
 // The workaround accepts both behaviours but could potentially break
 // legitimate responses that use a single newline to "fold" headers, which is
 // why it's not yet on by default.
-#define WORKAROUND_FOR_MISSING_CR       0
+#define WORKAROUND_FOR_MISSING_CR       1
 
 status_t HTTPStream::receive_line(char *line, size_t size) {
     if (mState != CONNECTED) {
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 20fbc05..1460f37 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -1377,91 +1377,6 @@
 
             mGotAllCodecSpecificData = true;
             continue;
-        } else if (!mGotAllCodecSpecificData &&
-                count == 1 && mIsMPEG4 && mCodecSpecificData == NULL) {
-            // The TI mpeg4 encoder does not properly set the
-            // codec-specific-data flag.
-
-            const uint8_t *data =
-                (const uint8_t *)buffer->data() + buffer->range_offset();
-
-            const size_t size = buffer->range_length();
-
-            size_t offset = 0;
-            while (offset + 3 < size) {
-                if (data[offset] == 0x00 && data[offset + 1] == 0x00
-                    && data[offset + 2] == 0x01 && data[offset + 3] == 0xb6) {
-                    break;
-                }
-
-                ++offset;
-            }
-
-            // CHECK(offset + 3 < size);
-            if (offset + 3 >= size) {
-                // XXX assume the entire first chunk of data is the codec specific
-                // data.
-                offset = size;
-            }
-
-            mCodecSpecificDataSize = offset;
-            mCodecSpecificData = malloc(offset);
-            memcpy(mCodecSpecificData, data, offset);
-
-            buffer->set_range(buffer->range_offset() + offset, size - offset);
-
-            if (size == offset) {
-                buffer->release();
-                buffer = NULL;
-
-                continue;
-            }
-
-            mGotAllCodecSpecificData = true;
-        } else if (!mGotAllCodecSpecificData && mIsAvc && count < 3) {
-            // The TI video encoder does not flag codec specific data
-            // as such and also splits up SPS and PPS across two buffers.
-
-            const uint8_t *data =
-                (const uint8_t *)buffer->data() + buffer->range_offset();
-
-            size_t size = buffer->range_length();
-
-            CHECK(count == 2 || mCodecSpecificData == NULL);
-
-            size_t offset = mCodecSpecificDataSize;
-            mCodecSpecificDataSize += size + 4;
-            mCodecSpecificData =
-                realloc(mCodecSpecificData, mCodecSpecificDataSize);
-
-            memcpy((uint8_t *)mCodecSpecificData + offset,
-                   "\x00\x00\x00\x01", 4);
-
-            memcpy((uint8_t *)mCodecSpecificData + offset + 4, data, size);
-
-            buffer->release();
-            buffer = NULL;
-
-            if (count == 2) {
-                void *tmp = mCodecSpecificData;
-                size = mCodecSpecificDataSize;
-                mCodecSpecificData = NULL;
-                mCodecSpecificDataSize = 0;
-
-                status_t err = makeAVCCodecSpecificData(
-                        (const uint8_t *)tmp, size);
-                free(tmp);
-                tmp = NULL;
-                CHECK_EQ(OK, err);
-
-                mGotAllCodecSpecificData = true;
-            }
-
-            continue;
-        }
-
-        if (!mGotAllCodecSpecificData) {
-            mGotAllCodecSpecificData = true;
         }
 
         // Make a deep copy of the MediaBuffer and Metadata and release
@@ -1962,6 +1877,8 @@
                   mOwner->writeInt32(samplerate << 16);
                   if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime)) {
                     mOwner->beginBox("esds");
+                        CHECK(mCodecSpecificData);
+                        CHECK(mCodecSpecificDataSize > 0);
 
                         mOwner->writeInt32(0);     // version=0, flags=0
                         mOwner->writeInt8(0x03);   // ES_DescrTag
@@ -2042,6 +1959,8 @@
                   CHECK(23 + mCodecSpecificDataSize < 128);
 
                   if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) {
+                      CHECK(mCodecSpecificData);
+                      CHECK(mCodecSpecificDataSize > 0);
                       mOwner->beginBox("esds");
 
                         mOwner->writeInt32(0);           // version=0, flags=0
@@ -2086,6 +2005,8 @@
 
                       mOwner->endBox();  // d263
                   } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
+                      CHECK(mCodecSpecificData);
+                      CHECK(mCodecSpecificDataSize > 0);
                       mOwner->beginBox("avcC");
                         mOwner->write(mCodecSpecificData, mCodecSpecificDataSize);
                       mOwner->endBox();  // avcC
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 11396ef..4741b1d 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -1685,6 +1685,14 @@
 
                 MediaBuffer *buffer = info->mMediaBuffer;
 
+                if (msg.u.extended_buffer_data.range_offset
+                        + msg.u.extended_buffer_data.range_length
+                            > buffer->size()) {
+                    CODEC_LOGE(
+                            "Codec lied about its buffer size requirements, "
+                            "sending a buffer larger than the originally "
+                            "advertised size in FILL_BUFFER_DONE!");
+                }
                 buffer->set_range(
                         msg.u.extended_buffer_data.range_offset,
                         msg.u.extended_buffer_data.range_length);
diff --git a/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp b/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp
index d5eb156..389180c 100644
--- a/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp
+++ b/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp
@@ -98,6 +98,7 @@
     : mSource(source),
       mMeta(meta),
       mNumInputFrames(-1),
+      mPrevTimestampUs(-1),
       mStarted(false),
       mInputBuffer(NULL),
       mInputFrameData(NULL),
@@ -337,14 +338,18 @@
 
     MediaBuffer *outputBuffer;
     CHECK_EQ(OK, mGroup->acquire_buffer(&outputBuffer));
-    uint8_t *outPtr = (uint8_t *) outputBuffer->data();
-    uint32_t dataLength = outputBuffer->size();
+
+    // Add 4 bytes for the start code 0x00000001
+    uint8_t *outPtr = (uint8_t *) outputBuffer->data() + 4;
+    uint32_t dataLength = outputBuffer->size() - 4;
 
     int32_t type;
     AVCEnc_Status encoderStatus = AVCENC_SUCCESS;
 
-    // Return SPS and PPS for the first two buffers
-    if (!mSpsPpsHeaderReceived) {
+    // Combine SPS and PPS and place them in the very first output buffer
+    // SPS and PPS are separated by start code 0x00000001
+    // Assume that we have exactly one SPS and exactly one PPS.
+    while (!mSpsPpsHeaderReceived && mNumInputFrames <= 0) {
         encoderStatus = PVAVCEncodeNAL(mHandle, outPtr, &dataLength, &type);
         if (encoderStatus == AVCENC_WRONG_STATE) {
             mSpsPpsHeaderReceived = true;
@@ -352,11 +357,22 @@
         } else {
             switch (type) {
                 case AVC_NALTYPE_SPS:
-                case AVC_NALTYPE_PPS:
-                    LOGV("%s received",
-                            (type == AVC_NALTYPE_SPS)? "SPS": "PPS");
                     ++mNumInputFrames;
-                    outputBuffer->set_range(0, dataLength);
+                    memcpy(outputBuffer->data(), "\x00\x00\x00\x01", 4);
+                    outputBuffer->set_range(0, dataLength + 4);
+                    outPtr += (dataLength + 4);  // 4 bytes for next start code
+                    dataLength = outputBuffer->size() -
+                            (outputBuffer->range_length() + 4);
+                    break;
+                case AVC_NALTYPE_PPS:
+                    ++mNumInputFrames;
+                    memcpy(((uint8_t *) outputBuffer->data()) +
+                            outputBuffer->range_length(),
+                            "\x00\x00\x00\x01", 4);
+                    outputBuffer->set_range(0,
+                            dataLength + outputBuffer->range_length() + 4);
+                    outputBuffer->meta_data()->setInt32(kKeyIsCodecConfig, 1);
+                    outputBuffer->meta_data()->setInt64(kKeyTime, 0);
                     *out = outputBuffer;
                     return OK;
                 default:
@@ -378,10 +394,34 @@
             outputBuffer->release();
             return err;
         }
+
+        if (mInputBuffer->size() - ((mVideoWidth * mVideoHeight * 3) >> 1) != 0) {
+            outputBuffer->release();
+            mInputBuffer->release();
+            mInputBuffer = NULL;
+            return UNKNOWN_ERROR;
+        }
+
         int64_t timeUs;
         CHECK(mInputBuffer->meta_data()->findInt64(kKeyTime, &timeUs));
         outputBuffer->meta_data()->setInt64(kKeyTime, timeUs);
 
+        // When the timestamp of the current sample is the same as
+        // that of the previous sample, the encoding of the sample
+        // is bypassed, and the output length is set to 0.
+        if (mNumInputFrames >= 1 && mPrevTimestampUs == timeUs) {
+            // Frame arrives too late
+            mInputBuffer->release();
+            mInputBuffer = NULL;
+            outputBuffer->set_range(0, 0);
+            *out = outputBuffer;
+            return OK;
+        }
+
+        // Don't accept out-of-order samples
+        CHECK(mPrevTimestampUs < timeUs);
+        mPrevTimestampUs = timeUs;
+
         AVCFrameIO videoInput;
         memset(&videoInput, 0, sizeof(videoInput));
         videoInput.height = ((mVideoHeight  + 15) >> 4) << 4;
diff --git a/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp b/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp
index 5002442e..a011137 100644
--- a/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp
+++ b/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp
@@ -69,6 +69,7 @@
       mMeta(meta),
       mNumInputFrames(-1),
       mNextModTimeUs(0),
+      mPrevTimestampUs(-1),
       mStarted(false),
       mInputBuffer(NULL),
       mInputFrameData(NULL),
@@ -294,10 +295,23 @@
         outputBuffer->release();
         return UNKNOWN_ERROR;
     }
+
+    if (mInputBuffer->size() - ((mVideoWidth * mVideoHeight * 3) >> 1) != 0) {
+        outputBuffer->release();
+        mInputBuffer->release();
+        mInputBuffer = NULL;
+        return UNKNOWN_ERROR;
+    }
+
     int64_t timeUs;
     CHECK(mInputBuffer->meta_data()->findInt64(kKeyTime, &timeUs));
-    if (mNextModTimeUs > timeUs) {
-        LOGV("mNextModTimeUs %lld > timeUs %lld", mNextModTimeUs, timeUs);
+
+    // When the timestamp of the current sample is the same as that
+    // of the previous sample, encoding of the current sample is
+    // bypassed, and the output length of the sample is set to 0
+    if (mNumInputFrames >= 1 &&
+        (mNextModTimeUs > timeUs || mPrevTimestampUs == timeUs)) {
+        // Frame arrives too late
         outputBuffer->set_range(0, 0);
         *out = outputBuffer;
         mInputBuffer->release();
@@ -305,6 +319,10 @@
         return OK;
     }
 
+    // Don't accept out-of-order samples
+    CHECK(mPrevTimestampUs < timeUs);
+    mPrevTimestampUs = timeUs;
+
     // Color convert to OMX_COLOR_FormatYUV420Planar if necessary
     outputBuffer->meta_data()->setInt64(kKeyTime, timeUs);
     uint8_t *inPtr = (uint8_t *) mInputBuffer->data();
diff --git a/media/libstagefright/include/AVCEncoder.h b/media/libstagefright/include/AVCEncoder.h
index 4fe2e30..83e1f97 100644
--- a/media/libstagefright/include/AVCEncoder.h
+++ b/media/libstagefright/include/AVCEncoder.h
@@ -64,6 +64,7 @@
     int32_t  mVideoBitRate;
     int32_t  mVideoColorFormat;
     int64_t  mNumInputFrames;
+    int64_t  mPrevTimestampUs;
     status_t mInitCheck;
     bool     mStarted;
     bool     mSpsPpsHeaderReceived;
diff --git a/media/libstagefright/include/M4vH263Encoder.h b/media/libstagefright/include/M4vH263Encoder.h
index dd146f4..dbe9fd0 100644
--- a/media/libstagefright/include/M4vH263Encoder.h
+++ b/media/libstagefright/include/M4vH263Encoder.h
@@ -59,6 +59,7 @@
     int32_t  mVideoColorFormat;
     int64_t  mNumInputFrames;
     int64_t  mNextModTimeUs;
+    int64_t  mPrevTimestampUs;
     status_t mInitCheck;
     bool     mStarted;
 
diff --git a/media/libstagefright/rtsp/AH263Assembler.cpp b/media/libstagefright/rtsp/AH263Assembler.cpp
index 8b59998..2818041 100644
--- a/media/libstagefright/rtsp/AH263Assembler.cpp
+++ b/media/libstagefright/rtsp/AH263Assembler.cpp
@@ -110,7 +110,7 @@
         buffer->data()[0] = 0x00;
         buffer->data()[1] = 0x00;
     } else {
-        buffer->setRange(2, buffer->size() - 2);
+        buffer->setRange(buffer->offset() + 2, buffer->size() - 2);
     }
 
     mPackets.push_back(buffer);
diff --git a/media/libstagefright/rtsp/APacketSource.cpp b/media/libstagefright/rtsp/APacketSource.cpp
index a577704..224b4bf 100644
--- a/media/libstagefright/rtsp/APacketSource.cpp
+++ b/media/libstagefright/rtsp/APacketSource.cpp
@@ -356,24 +356,10 @@
     if (!mBuffers.empty()) {
         const sp<ABuffer> buffer = *mBuffers.begin();
 
-        uint64_t ntpTime;
-        CHECK(buffer->meta()->findInt64(
-                    "ntp-time", (int64_t *)&ntpTime));
-
         MediaBuffer *mediaBuffer = new MediaBuffer(buffer->size());
-        mediaBuffer->meta_data()->setInt64(kKeyNTPTime, ntpTime);
 
-        if (mFirstAccessUnit) {
-            mFirstAccessUnit = false;
-            mFirstAccessUnitNTP = ntpTime;
-        }
-        if (ntpTime > mFirstAccessUnitNTP) {
-            ntpTime -= mFirstAccessUnitNTP;
-        } else {
-            ntpTime = 0;
-        }
-
-        int64_t timeUs = (int64_t)(ntpTime * 1E6 / (1ll << 32));
+        int64_t timeUs;
+        CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
 
         mediaBuffer->meta_data()->setInt64(kKeyTime, timeUs);
 
@@ -390,10 +376,29 @@
 void APacketSource::queueAccessUnit(const sp<ABuffer> &buffer) {
     int32_t damaged;
     if (buffer->meta()->findInt32("damaged", &damaged) && damaged) {
-        // LOG(VERBOSE) << "discarding damaged AU";
+        LOG(VERBOSE) << "discarding damaged AU";
         return;
     }
 
+    uint64_t ntpTime;
+    CHECK(buffer->meta()->findInt64(
+                "ntp-time", (int64_t *)&ntpTime));
+
+    if (mFirstAccessUnit) {
+        mFirstAccessUnit = false;
+        mFirstAccessUnitNTP = ntpTime;
+    }
+
+    if (ntpTime > mFirstAccessUnitNTP) {
+        ntpTime -= mFirstAccessUnitNTP;
+    } else {
+        ntpTime = 0;
+    }
+
+    int64_t timeUs = (int64_t)(ntpTime * 1E6 / (1ll << 32));
+
+    buffer->meta()->setInt64("timeUs", timeUs);
+
     Mutex::Autolock autoLock(mLock);
     mBuffers.push_back(buffer);
     mCondition.signal();
diff --git a/media/libstagefright/rtsp/ARTPConnection.cpp b/media/libstagefright/rtsp/ARTPConnection.cpp
index 5bd306b..469af3e 100644
--- a/media/libstagefright/rtsp/ARTPConnection.cpp
+++ b/media/libstagefright/rtsp/ARTPConnection.cpp
@@ -28,8 +28,6 @@
 #include <arpa/inet.h>
 #include <sys/socket.h>
 
-#define IGNORE_RTCP_TIME        0
-
 namespace android {
 
 static const size_t kMaxUDPSize = 1500;
@@ -61,8 +59,9 @@
     struct sockaddr_in mRemoteRTCPAddr;
 };
 
-ARTPConnection::ARTPConnection()
-    : mPollEventPending(false),
+ARTPConnection::ARTPConnection(uint32_t flags)
+    : mFlags(flags),
+      mPollEventPending(false),
       mLastReceiverReportTimeUs(-1) {
 }
 
@@ -280,7 +279,10 @@
                 sp<ARTPSource> source = s->mSources.valueAt(i);
 
                 source->addReceiverReport(buffer);
-                source->addFIR(buffer);
+
+                if (mFlags & kRegularlyRequestFIR) {
+                    source->addFIR(buffer);
+                }
             }
 
             if (buffer->size() > 0) {
@@ -405,13 +407,11 @@
     buffer->setInt32Data(u16at(&data[2]));
     buffer->setRange(payloadOffset, size - payloadOffset);
 
-#if IGNORE_RTCP_TIME
-    if (!source->timeEstablished()) {
+    if ((mFlags & kFakeTimestamps) && !source->timeEstablished()) {
         source->timeUpdate(rtpTime, 0);
-        source->timeUpdate(rtpTime + 20, 0x100000000ll);
+        source->timeUpdate(rtpTime + 90000, 0x100000000ll);
         CHECK(source->timeEstablished());
     }
-#endif
 
     source->processRTPPacket(buffer);
 
@@ -533,9 +533,9 @@
 
     sp<ARTPSource> source = findSource(s, id);
 
-#if !IGNORE_RTCP_TIME
-    source->timeUpdate(rtpTime, ntpTime);
-#endif
+    if ((mFlags & kFakeTimestamps) == 0) {
+        source->timeUpdate(rtpTime, ntpTime);
+    }
 
     return 0;
 }
diff --git a/media/libstagefright/rtsp/ARTPConnection.h b/media/libstagefright/rtsp/ARTPConnection.h
index 49839ad..c535199 100644
--- a/media/libstagefright/rtsp/ARTPConnection.h
+++ b/media/libstagefright/rtsp/ARTPConnection.h
@@ -28,7 +28,12 @@
 struct ASessionDescription;
 
 struct ARTPConnection : public AHandler {
-    ARTPConnection();
+    enum Flags {
+        kFakeTimestamps      = 1,
+        kRegularlyRequestFIR = 2,
+    };
+
+    ARTPConnection(uint32_t flags = 0);
 
     void addStream(
             int rtpSocket, int rtcpSocket,
@@ -56,6 +61,8 @@
 
     static const int64_t kSelectTimeoutUs;
 
+    uint32_t mFlags;
+
     struct StreamInfo;
     List<StreamInfo> mStreams;
 
diff --git a/media/libstagefright/rtsp/ARTPSession.cpp b/media/libstagefright/rtsp/ARTPSession.cpp
index 0e0f45a..e082078 100644
--- a/media/libstagefright/rtsp/ARTPSession.cpp
+++ b/media/libstagefright/rtsp/ARTPSession.cpp
@@ -40,7 +40,10 @@
 
     mDesc = desc;
 
-    mRTPConn = new ARTPConnection;
+    mRTPConn = new ARTPConnection(
+            ARTPConnection::kFakeTimestamps
+                | ARTPConnection::kRegularlyRequestFIR);
+
     looper()->registerHandler(mRTPConn);
 
     for (size_t i = 1; i < mDesc->countTracks(); ++i) {
diff --git a/media/libstagefright/rtsp/ARTPSource.cpp b/media/libstagefright/rtsp/ARTPSource.cpp
index e08183e..225f6e8 100644
--- a/media/libstagefright/rtsp/ARTPSource.cpp
+++ b/media/libstagefright/rtsp/ARTPSource.cpp
@@ -98,7 +98,7 @@
     mNTPTime[mNumTimes] = ntpTime;
     mRTPTime[mNumTimes++] = rtpTime;
 
-    if (mNumTimes == 2) {
+    if (timeEstablished()) {
         for (List<sp<ABuffer> >::iterator it = mQueue.begin();
              it != mQueue.end(); ++it) {
             sp<AMessage> meta = (*it)->meta();
@@ -112,13 +112,6 @@
 }
 
 bool ARTPSource::queuePacket(const sp<ABuffer> &buffer) {
-#if 1
-    if (mNumTimes != 2) {
-        // Drop incoming packets until we've established a time base.
-        return false;
-    }
-#endif
-
     uint32_t seqNum = (uint32_t)buffer->int32Data();
 
     if (mNumTimes == 2) {
diff --git a/media/libstagefright/rtsp/ARTPWriter.cpp b/media/libstagefright/rtsp/ARTPWriter.cpp
index cc23856..d6dd597 100644
--- a/media/libstagefright/rtsp/ARTPWriter.cpp
+++ b/media/libstagefright/rtsp/ARTPWriter.cpp
@@ -43,7 +43,7 @@
 #if 1
     mRTPAddr.sin_addr.s_addr = INADDR_ANY;
 #else
-    mRTPAddr.sin_addr.s_addr = inet_addr("172.19.19.74");
+    mRTPAddr.sin_addr.s_addr = inet_addr("172.19.18.246");
 #endif
 
     mRTPAddr.sin_port = htons(5634);
diff --git a/media/tests/MediaFrameworkTest/AndroidManifest.xml b/media/tests/MediaFrameworkTest/AndroidManifest.xml
index 246f9fc..f70a145 100644
--- a/media/tests/MediaFrameworkTest/AndroidManifest.xml
+++ b/media/tests/MediaFrameworkTest/AndroidManifest.xml
@@ -51,4 +51,9 @@
          android:label="MediaRecorder stress tests InstrumentationRunner">
      </instrumentation>
 
+      <instrumentation android:name=".MediaFrameworkPowerTestRunner"
+         android:targetPackage="com.android.mediaframeworktest"
+         android:label="Media Power tests InstrumentationRunner">
+     </instrumentation>
+
 </manifest>
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkPowerTestRunner.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkPowerTestRunner.java
new file mode 100755
index 0000000..34db4db
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkPowerTestRunner.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2010 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.mediaframeworktest;
+
+import com.android.mediaframeworktest.power.MediaPlayerPowerTest;
+
+import junit.framework.TestSuite;
+
+import android.test.InstrumentationTestRunner;
+import android.test.InstrumentationTestSuite;
+
+
+/**
+ * Instrumentation Test Runner for all MediaPlayer tests.
+ *
+ * Running all tests:
+ *
+ * adb shell am instrument \
+ *   -w com.android.mediaframeworktest/.MediaFrameworkPowerTestRunner
+ */
+
+public class MediaFrameworkPowerTestRunner extends InstrumentationTestRunner {
+
+  @Override
+  public TestSuite getAllTests() {
+      TestSuite suite = new InstrumentationTestSuite(this);
+      suite.addTestSuite(MediaPlayerPowerTest.class);
+      return suite;
+  }
+
+  @Override
+  public ClassLoader getLoader() {
+      return MediaFrameworkPowerTestRunner.class.getClassLoader();
+  }
+}
+
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/power/MediaPlayerPowerTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/power/MediaPlayerPowerTest.java
new file mode 100644
index 0000000..9e91740
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/power/MediaPlayerPowerTest.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2010 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.mediaframeworktest.power;
+
+import com.android.mediaframeworktest.MediaFrameworkTest;
+import com.android.mediaframeworktest.MediaNames;
+import android.media.MediaPlayer;
+import android.os.Environment;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+
+import java.io.File;
+
+/**
+ * Junit / Instrumentation test case for the power measurment the media player
+ */
+public class MediaPlayerPowerTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> {
+    private String TAG = "MediaPlayerPowerTest";
+    private String MP3_POWERTEST =
+            Environment.getExternalStorageDirectory().toString() + "/power_sample_mp3.mp3";
+    private String MP3_STREAM = "http://75.17.48.204:10088/power_media/power_sample_mp3.mp3";
+    private String OGG_STREAM = "http://75.17.48.204:10088/power_media/power_sample_ogg.mp3";
+    private String AAC_STREAM = "http://75.17.48.204:10088/power_media/power_sample_aac.mp3";
+
+    public MediaPlayerPowerTest() {
+        super("com.android.mediaframeworktest", MediaFrameworkTest.class);
+    }
+
+    protected void setUp() throws Exception {
+        getActivity();
+        super.setUp();
+
+    }
+
+    public void audioPlayback(String filePath) {
+        try {
+            MediaPlayer mp = new MediaPlayer();
+            mp.setDataSource(filePath);
+            mp.prepare();
+            mp.start();
+            Thread.sleep(200000);
+            mp.stop();
+            mp.release();
+        } catch (Exception e) {
+            Log.v(TAG, e.toString());
+            assertTrue("MP3 Playback", false);
+        }
+    }
+
+    // A very simple test case which start the audio player.
+    // Power measurment will be done in other application.
+    public void testPowerLocalMP3Playback() throws Exception {
+        audioPlayback(MP3_POWERTEST);
+    }
+
+    public void testPowerStreamMP3Playback() throws Exception {
+        audioPlayback(MP3_STREAM);
+    }
+
+    public void testPowerStreamOGGPlayback() throws Exception {
+        audioPlayback(OGG_STREAM);
+    }
+
+    public void testPowerStreamAACPlayback() throws Exception {
+        audioPlayback(AAC_STREAM);
+    }
+}
diff --git a/native/android/Android.mk b/native/android/Android.mk
index 950a1e9..bd2b27a 100644
--- a/native/android/Android.mk
+++ b/native/android/Android.mk
@@ -7,6 +7,7 @@
 #
 LOCAL_SRC_FILES:= \
     asset_manager.cpp \
+    configuration.cpp \
     input.cpp \
     looper.cpp \
     native_activity.cpp \
diff --git a/native/android/asset_manager.cpp b/native/android/asset_manager.cpp
index 36c381e..3f7c1b6 100644
--- a/native/android/asset_manager.cpp
+++ b/native/android/asset_manager.cpp
@@ -17,7 +17,7 @@
 #define LOG_TAG "NAsset"
 #include <utils/Log.h>
 
-#include <android/asset_manager.h>
+#include <android/asset_manager_jni.h>
 #include <utils/AssetManager.h>
 #include <utils/AssetDir.h>
 #include <utils/Asset.h>
diff --git a/native/android/configuration.cpp b/native/android/configuration.cpp
new file mode 100644
index 0000000..d76164f
--- /dev/null
+++ b/native/android/configuration.cpp
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2010 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 "Configuration"
+#include <utils/Log.h>
+
+#include <utils/AssetManager.h>
+
+#include <android_runtime/android_content_res_Configuration.h>
+
+using namespace android;
+
+AConfiguration* AConfiguration_new() {
+    AConfiguration* config = new AConfiguration;
+    memset(config, 0, sizeof(AConfiguration));
+    return config;
+}
+
+void AConfiguration_delete(AConfiguration* config) {
+    delete config;
+}
+
+void AConfiguration_fromAssetManager(AConfiguration* out, AAssetManager* am) {
+    ((AssetManager*)am)->getConfiguration(out);
+}
+
+void AConfiguration_copy(AConfiguration* dest, AConfiguration* src) {
+    *dest = *src;
+}
+
+int32_t AConfiguration_getMcc(AConfiguration* config) {
+    return config->mcc;
+}
+
+int32_t AConfiguration_getMnc(AConfiguration* config) {
+    return config->mnc;
+}
+
+void AConfiguration_getLanguage(AConfiguration* config, char* outLanguage) {
+    outLanguage[0] = config->language[0];
+    outLanguage[1] = config->language[1];
+}
+
+void AConfiguration_getCountry(AConfiguration* config, char* outCountry) {
+    outCountry[0] = config->country[0];
+    outCountry[1] = config->country[1];
+}
+
+int32_t AConfiguration_getOrientation(AConfiguration* config) {
+    return config->orientation;
+}
+
+int32_t AConfiguration_getTouchscreen(AConfiguration* config) {
+    return config->touchscreen;
+}
+
+int32_t AConfiguration_getDensity(AConfiguration* config) {
+    return config->density;
+}
+
+int32_t AConfiguration_getKeyboard(AConfiguration* config) {
+    return config->keyboard;
+}
+
+int32_t AConfiguration_getNavigation(AConfiguration* config) {
+    return config->navigation;
+}
+
+int32_t AConfiguration_getKeysHidden(AConfiguration* config) {
+    return config->inputFlags&ResTable_config::MASK_KEYSHIDDEN;
+}
+
+int32_t AConfiguration_getNavHidden(AConfiguration* config) {
+    return (config->inputFlags&ResTable_config::MASK_NAVHIDDEN)
+            >> ResTable_config::SHIFT_NAVHIDDEN;
+}
+
+int32_t AConfiguration_getSdkVersion(AConfiguration* config) {
+    return config->sdkVersion;
+}
+
+int32_t AConfiguration_getScreenSize(AConfiguration* config) {
+    return config->screenLayout&ResTable_config::MASK_SCREENSIZE;
+}
+
+int32_t AConfiguration_getScreenLong(AConfiguration* config) {
+    return (config->screenLayout&ResTable_config::MASK_SCREENLONG)
+            >> ResTable_config::SHIFT_SCREENLONG;
+}
+
+int32_t AConfiguration_getUiModeType(AConfiguration* config) {
+    return config->uiMode&ResTable_config::MASK_UI_MODE_TYPE;
+}
+
+int32_t AConfiguration_getUiModeNight(AConfiguration* config) {
+    return (config->uiMode&ResTable_config::MASK_UI_MODE_NIGHT)
+            >> ResTable_config::SHIFT_UI_MODE_NIGHT;
+
+}
+
+// ----------------------------------------------------------------------
+
+void AConfiguration_setMcc(AConfiguration* config, int32_t mcc) {
+    config->mcc = mcc;
+}
+
+void AConfiguration_setMnc(AConfiguration* config, int32_t mnc) {
+    config->mnc = mnc;
+}
+
+void AConfiguration_setLanguage(AConfiguration* config, const char* language) {
+    config->language[0] = language[0];
+    config->language[1] = language[1];
+}
+
+void AConfiguration_setCountry(AConfiguration* config, const char* country) {
+    config->country[0] = country[0];
+    config->country[1] = country[1];
+}
+
+void AConfiguration_setOrientation(AConfiguration* config, int32_t orientation) {
+    config->orientation = orientation;
+}
+
+void AConfiguration_setTouchscreen(AConfiguration* config, int32_t touchscreen) {
+    config->touchscreen = touchscreen;
+}
+
+void AConfiguration_setDensity(AConfiguration* config, int32_t density) {
+    config->density = density;
+}
+
+void AConfiguration_setKeyboard(AConfiguration* config, int32_t keyboard) {
+    config->keyboard = keyboard;
+}
+
+void AConfiguration_setNavigation(AConfiguration* config, int32_t navigation) {
+    config->navigation = navigation;
+}
+
+void AConfiguration_setKeysHidden(AConfiguration* config, int32_t keysHidden) {
+    config->inputFlags = (config->inputFlags&~ResTable_config::MASK_KEYSHIDDEN)
+            | (keysHidden&ResTable_config::MASK_KEYSHIDDEN);
+}
+
+void AConfiguration_setNavHidden(AConfiguration* config, int32_t navHidden) {
+    config->inputFlags = (config->inputFlags&~ResTable_config::MASK_NAVHIDDEN)
+            | ((navHidden<<ResTable_config::SHIFT_NAVHIDDEN)&ResTable_config::MASK_NAVHIDDEN);
+}
+
+void AConfiguration_setSdkVersion(AConfiguration* config, int32_t sdkVersion) {
+    config->sdkVersion = sdkVersion;
+}
+
+void AConfiguration_setScreenSize(AConfiguration* config, int32_t screenSize) {
+    config->screenLayout = (config->screenLayout&~ResTable_config::MASK_SCREENSIZE)
+            | (screenSize&ResTable_config::MASK_SCREENSIZE);
+}
+
+void AConfiguration_setScreenLong(AConfiguration* config, int32_t screenLong) {
+    config->screenLayout = (config->screenLayout&~ResTable_config::MASK_SCREENLONG)
+            | ((screenLong<<ResTable_config::SHIFT_SCREENLONG)&ResTable_config::MASK_SCREENLONG);
+}
+
+void AConfiguration_setUiModeType(AConfiguration* config, int32_t uiModeType) {
+    config->uiMode = (config->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
+            | (uiModeType&ResTable_config::MASK_UI_MODE_TYPE);
+}
+
+void AConfiguration_setUiModeNight(AConfiguration* config, int32_t uiModeNight) {
+    config->uiMode = (config->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
+            | ((uiModeNight<<ResTable_config::SHIFT_UI_MODE_NIGHT)&ResTable_config::MASK_UI_MODE_NIGHT);
+
+}
+
+// ----------------------------------------------------------------------
+
+int32_t AConfiguration_diff(AConfiguration* config1, AConfiguration* config2) {
+    return (config1->diff(*config2));
+}
+
+int32_t AConfiguration_match(AConfiguration* base, AConfiguration* requested) {
+    return base->match(*requested);
+}
+
+int32_t AConfiguration_isBetterThan(AConfiguration* base, AConfiguration* test,
+        AConfiguration* requested) {
+    return base->isBetterThan(*test, requested);
+}
diff --git a/native/copy-to-ndk.sh b/native/copy-to-ndk.sh
new file mode 100644
index 0000000..4f5a16a
--- /dev/null
+++ b/native/copy-to-ndk.sh
@@ -0,0 +1,55 @@
+# Take care of copying current header files over to the correct
+# location in the NDK.
+
+copyndkheaders() {
+    local CURR_PLATFORM=android-9
+    local ALL_PLATFORMS="$CURR_PLATFORM android-8 android-5 android-4 android-3"
+
+    local SRC_HEADERS=$ANDROID_BUILD_TOP/frameworks/base/native/include/android
+    local NDK_PLATFORMS=$ANDROID_BUILD_TOP/development/ndk/platforms
+    local DST_HEADERS=$NDK_PLATFORMS/$CURR_PLATFORM
+
+    local SRC_LIB_ANDROID=$ANDROID_PRODUCT_OUT/system/lib/libandroid.so
+    local DST_LIB_ANDROID=$NDK_PLATFORMS/$CURR_PLATFORM/arch-arm/usr/lib/libandroid.so
+
+    local didsomething=""
+
+    #echo "SRC_HEADERS: $SRC_HEADERS"
+
+    for i in $(cd $SRC_HEADERS; ls *.h); do
+        local src=$SRC_HEADERS/$i
+        local changed=""
+        for j in $ALL_PLATFORMS; do
+            local dst=$NDK_PLATFORMS/$j/arch-arm/usr/include/android/$i
+            if [ "$changed" == "" -a -e $dst ]; then
+                #echo "Exists: $dst"
+                if diff $src $dst >/dev/null; then
+                    echo "$i: has not changed from $j" >/dev/null
+                    changed="false"
+                else
+                    changed="true"
+                    echo "$i: has changed from $j" >/dev/null
+                fi
+            fi
+        done
+        if [ "$changed" == "true" -o "$changed" == "" ]; then
+            echo "Updating: $i"
+            cp $src $NDK_PLATFORMS/$CURR_PLATFORM/arch-arm/usr/include/android/$i
+            didsomething="true"
+        fi
+    done
+
+    if diff $SRC_LIB_ANDROID $DST_LIB_ANDROID >/dev/null; then
+        echo "libandroid.so: has not changed" >/dev/null
+    else
+        echo "Updating: $DST_LIB_ANDROID"
+        cp $SRC_LIB_ANDROID $DST_LIB_ANDROID
+        didsomething="true"
+    fi
+    if [ "$didsomething" != "" ]; then
+        echo "Headers changed...  rebuilding platforms."
+        sh $ANDROID_BUILD_TOP/ndk/build/tools/build-platforms.sh
+    fi
+}
+
+copyndkheaders
diff --git a/native/include/android/asset_manager.h b/native/include/android/asset_manager.h
index 89989f8..4fa0ef3 100644
--- a/native/include/android/asset_manager.h
+++ b/native/include/android/asset_manager.h
@@ -18,8 +18,6 @@
 #ifndef ANDROID_ASSET_MANAGER_H
 #define ANDROID_ASSET_MANAGER_H
 
-#include <jni.h>
-
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -43,14 +41,6 @@
 
 
 /**
- * Given a Dalvik AssetManager object, obtain the corresponding native AAssetManager
- * object.  Note that the caller is responsible for obtaining and holding a VM reference
- * to the jobject to prevent its being garbage collected while the native object is
- * in use.
- */
-AAssetManager* AAssetManager_fromJava(JNIEnv* env, jobject assetManager);
-
-/**
  * Open the named directory within the asset hierarchy.  The directory can then
  * be inspected with the AAssetDir functions.  To open the top-level directory,
  * pass in "" as the dirName.
diff --git a/native/include/android/asset_manager_jni.h b/native/include/android/asset_manager_jni.h
new file mode 100644
index 0000000..aec2d3c
--- /dev/null
+++ b/native/include/android/asset_manager_jni.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+
+#ifndef ANDROID_ASSET_MANAGER_JNI_H
+#define ANDROID_ASSET_MANAGER_JNI_H
+
+#include <android/asset_manager.h>
+#include <jni.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Given a Dalvik AssetManager object, obtain the corresponding native AAssetManager
+ * object.  Note that the caller is responsible for obtaining and holding a VM reference
+ * to the jobject to prevent its being garbage collected while the native object is
+ * in use.
+ */
+AAssetManager* AAssetManager_fromJava(JNIEnv* env, jobject assetManager);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif      // ANDROID_ASSET_MANAGER_JNI_H
diff --git a/native/include/android/configuration.h b/native/include/android/configuration.h
new file mode 100644
index 0000000..79b9b1e
--- /dev/null
+++ b/native/include/android/configuration.h
@@ -0,0 +1,319 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#ifndef ANDROID_CONFIGURATION_H
+#define ANDROID_CONFIGURATION_H
+
+#include <android/asset_manager.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct AConfiguration;
+typedef struct AConfiguration AConfiguration;
+
+enum {
+    ACONFIGURATION_ORIENTATION_ANY  = 0x0000,
+    ACONFIGURATION_ORIENTATION_PORT = 0x0001,
+    ACONFIGURATION_ORIENTATION_LAND = 0x0002,
+    ACONFIGURATION_ORIENTATION_SQUARE = 0x0003,
+
+    ACONFIGURATION_TOUCHSCREEN_ANY  = 0x0000,
+    ACONFIGURATION_TOUCHSCREEN_NOTOUCH  = 0x0001,
+    ACONFIGURATION_TOUCHSCREEN_STYLUS  = 0x0002,
+    ACONFIGURATION_TOUCHSCREEN_FINGER  = 0x0003,
+
+    ACONFIGURATION_DENSITY_DEFAULT = 0,
+    ACONFIGURATION_DENSITY_LOW = 120,
+    ACONFIGURATION_DENSITY_MEDIUM = 160,
+    ACONFIGURATION_DENSITY_HIGH = 240,
+    ACONFIGURATION_DENSITY_NONE = 0xffff,
+
+    ACONFIGURATION_KEYBOARD_ANY  = 0x0000,
+    ACONFIGURATION_KEYBOARD_NOKEYS  = 0x0001,
+    ACONFIGURATION_KEYBOARD_QWERTY  = 0x0002,
+    ACONFIGURATION_KEYBOARD_12KEY  = 0x0003,
+
+    ACONFIGURATION_NAVIGATION_ANY  = 0x0000,
+    ACONFIGURATION_NAVIGATION_NONAV  = 0x0001,
+    ACONFIGURATION_NAVIGATION_DPAD  = 0x0002,
+    ACONFIGURATION_NAVIGATION_TRACKBALL  = 0x0003,
+    ACONFIGURATION_NAVIGATION_WHEEL  = 0x0004,
+
+    ACONFIGURATION_KEYSHIDDEN_ANY = 0x0000,
+    ACONFIGURATION_KEYSHIDDEN_NO = 0x0001,
+    ACONFIGURATION_KEYSHIDDEN_YES = 0x0002,
+    ACONFIGURATION_KEYSHIDDEN_SOFT = 0x0003,
+
+    ACONFIGURATION_NAVHIDDEN_ANY = 0x0000,
+    ACONFIGURATION_NAVHIDDEN_NO = 0x0001,
+    ACONFIGURATION_NAVHIDDEN_YES = 0x0002,
+
+    ACONFIGURATION_SCREENSIZE_ANY  = 0x00,
+    ACONFIGURATION_SCREENSIZE_SMALL = 0x01,
+    ACONFIGURATION_SCREENSIZE_NORMAL = 0x02,
+    ACONFIGURATION_SCREENSIZE_LARGE = 0x03,
+    ACONFIGURATION_SCREENSIZE_XLARGE = 0x04,
+
+    ACONFIGURATION_SCREENLONG_ANY = 0x00,
+    ACONFIGURATION_SCREENLONG_NO = 0x1,
+    ACONFIGURATION_SCREENLONG_YES = 0x2,
+
+    ACONFIGURATION_UI_MODE_TYPE_ANY = 0x00,
+    ACONFIGURATION_UI_MODE_TYPE_NORMAL = 0x01,
+    ACONFIGURATION_UI_MODE_TYPE_DESK = 0x02,
+    ACONFIGURATION_UI_MODE_TYPE_CAR = 0x03,
+
+    ACONFIGURATION_UI_MODE_NIGHT_ANY = 0x00,
+    ACONFIGURATION_UI_MODE_NIGHT_NO = 0x10,
+    ACONFIGURATION_UI_MODE_NIGHT_YES = 0x20,
+
+    ACONFIGURATION_MCC = 0x0001,
+    ACONFIGURATION_MNC = 0x0002,
+    ACONFIGURATION_LOCALE = 0x0004,
+    ACONFIGURATION_TOUCHSCREEN = 0x0008,
+    ACONFIGURATION_KEYBOARD = 0x0010,
+    ACONFIGURATION_KEYBOARD_HIDDEN = 0x0020,
+    ACONFIGURATION_NAVIGATION = 0x0040,
+    ACONFIGURATION_ORIENTATION = 0x0080,
+    ACONFIGURATION_DENSITY = 0x0100,
+    ACONFIGURATION_SCREEN_SIZE = 0x0200,
+    ACONFIGURATION_VERSION = 0x0400,
+    ACONFIGURATION_SCREEN_LAYOUT = 0x0800,
+    ACONFIGURATION_UI_MODE = 0x1000,
+};
+
+/**
+ * Create a new AConfiguration, initialized with no values set.
+ */
+AConfiguration* AConfiguration_new();
+
+/**
+ * Free an AConfiguration that was previously created with
+ * AConfiguration_new().
+ */
+void AConfiguration_delete(AConfiguration* config);
+
+/**
+ * Create and return a new AConfiguration based on the current configuration in
+ * use in the given AssetManager.
+ */
+void AConfiguration_fromAssetManager(AConfiguration* out, AAssetManager* am);
+
+/**
+ * Copy the contents of 'src' to 'dest'.
+ */
+void AConfiguration_copy(AConfiguration* dest, AConfiguration* src);
+
+/**
+ * Return the current MCC set in the configuration.  0 if not set.
+ */
+int32_t AConfiguration_getMcc(AConfiguration* config);
+
+/**
+ * Set the current MCC in the configuration.  0 to clear.
+ */
+void AConfiguration_setMcc(AConfiguration* config, int32_t mcc);
+
+/**
+ * Return the current MNC set in the configuration.  0 if not set.
+ */
+int32_t AConfiguration_getMnc(AConfiguration* config);
+
+/**
+ * Set the current MNC in the configuration.  0 to clear.
+ */
+void AConfiguration_setMnc(AConfiguration* config, int32_t mnc);
+
+/**
+ * Return the current language code set in the configuration.  The output will
+ * be filled with an array of two characters.  They are not 0-terminated.  If
+ * a language is not set, they will be 0.
+ */
+void AConfiguration_getLanguage(AConfiguration* config, char* outLanguage);
+
+/**
+ * Set the current language code in the configuration, from the first two
+ * characters in the string.
+ */
+void AConfiguration_setLanguage(AConfiguration* config, const char* language);
+
+/**
+ * Return the current country code set in the configuration.  The output will
+ * be filled with an array of two characters.  They are not 0-terminated.  If
+ * a country is not set, they will be 0.
+ */
+void AConfiguration_getCountry(AConfiguration* config, char* outCountry);
+
+/**
+ * Set the current country code in the configuration, from the first two
+ * characters in the string.
+ */
+void AConfiguration_setCountry(AConfiguration* config, const char* country);
+
+/**
+ * Return the current ACONFIGURATION_ORIENTATION_* set in the configuration.
+ */
+int32_t AConfiguration_getOrientation(AConfiguration* config);
+
+/**
+ * Set the current orientation in the configuration.
+ */
+void AConfiguration_setOrientation(AConfiguration* config, int32_t orientation);
+
+/**
+ * Return the current ACONFIGURATION_TOUCHSCREEN_* set in the configuration.
+ */
+int32_t AConfiguration_getTouchscreen(AConfiguration* config);
+
+/**
+ * Set the current touchscreen in the configuration.
+ */
+void AConfiguration_setTouchscreen(AConfiguration* config, int32_t touchscreen);
+
+/**
+ * Return the current ACONFIGURATION_DENSITY_* set in the configuration.
+ */
+int32_t AConfiguration_getDensity(AConfiguration* config);
+
+/**
+ * Set the current density in the configuration.
+ */
+void AConfiguration_setDensity(AConfiguration* config, int32_t density);
+
+/**
+ * Return the current ACONFIGURATION_KEYBOARD_* set in the configuration.
+ */
+int32_t AConfiguration_getKeyboard(AConfiguration* config);
+
+/**
+ * Set the current keyboard in the configuration.
+ */
+void AConfiguration_setKeyboard(AConfiguration* config, int32_t keyboard);
+
+/**
+ * Return the current ACONFIGURATION_NAVIGATION_* set in the configuration.
+ */
+int32_t AConfiguration_getNavigation(AConfiguration* config);
+
+/**
+ * Set the current navigation in the configuration.
+ */
+void AConfiguration_setNavigation(AConfiguration* config, int32_t navigation);
+
+/**
+ * Return the current ACONFIGURATION_KEYSHIDDEN_* set in the configuration.
+ */
+int32_t AConfiguration_getKeysHidden(AConfiguration* config);
+
+/**
+ * Set the current keys hidden in the configuration.
+ */
+void AConfiguration_setKeysHidden(AConfiguration* config, int32_t keysHidden);
+
+/**
+ * Return the current ACONFIGURATION_NAVHIDDEN_* set in the configuration.
+ */
+int32_t AConfiguration_getNavHidden(AConfiguration* config);
+
+/**
+ * Set the current nav hidden in the configuration.
+ */
+void AConfiguration_setNavHidden(AConfiguration* config, int32_t navHidden);
+
+/**
+ * Return the current SDK (API) version set in the configuration.
+ */
+int32_t AConfiguration_getSdkVersion(AConfiguration* config);
+
+/**
+ * Set the current SDK version in the configuration.
+ */
+void AConfiguration_setSdkVersion(AConfiguration* config, int32_t sdkVersion);
+
+/**
+ * Return the current ACONFIGURATION_SCREENSIZE_* set in the configuration.
+ */
+int32_t AConfiguration_getScreenSize(AConfiguration* config);
+
+/**
+ * Set the current screen size in the configuration.
+ */
+void AConfiguration_setScreenSize(AConfiguration* config, int32_t screenSize);
+
+/**
+ * Return the current ACONFIGURATION_SCREENLONG_* set in the configuration.
+ */
+int32_t AConfiguration_getScreenLong(AConfiguration* config);
+
+/**
+ * Set the current screen long in the configuration.
+ */
+void AConfiguration_setScreenLong(AConfiguration* config, int32_t screenLong);
+
+/**
+ * Return the current ACONFIGURATION_UI_MODE_TYPE_* set in the configuration.
+ */
+int32_t AConfiguration_getUiModeType(AConfiguration* config);
+
+/**
+ * Set the current UI mode type in the configuration.
+ */
+void AConfiguration_setUiModeType(AConfiguration* config, int32_t uiModeType);
+
+/**
+ * Return the current ACONFIGURATION_UI_MODE_NIGHT_* set in the configuration.
+ */
+int32_t AConfiguration_getUiModeNight(AConfiguration* config);
+
+/**
+ * Set the current UI mode night in the configuration.
+ */
+void AConfiguration_setUiModeNight(AConfiguration* config, int32_t uiModeNight);
+
+/**
+ * Perform a diff between two configurations.  Returns a bit mask of
+ * ACONFIGURATION_* constants, each bit set meaning that configuration element
+ * is different between them.
+ */
+int32_t AConfiguration_diff(AConfiguration* config1, AConfiguration* config2);
+
+/**
+ * Determine whether 'base' is a valid configuration for use within the
+ * environment 'requested'.  Returns 0 if there are any values in 'base'
+ * that conflict with 'requested'.  Returns 1 if it does not conflict.
+ */
+int32_t AConfiguration_match(AConfiguration* base, AConfiguration* requested);
+
+/**
+ * Determine whether the configuration in 'test' is better than the existing
+ * configuration in 'base'.  If 'requested' is non-NULL, this decision is based
+ * on the overall configuration given there.  If it is NULL, this decision is
+ * simply based on which configuration is more specific.  Returns non-0 if
+ * 'test' is better than 'base'.
+ *
+ * This assumes you have already filtered the configurations with
+ * AConfiguration_match().
+ */
+int32_t AConfiguration_isBetterThan(AConfiguration* base, AConfiguration* test,
+        AConfiguration* requested);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif // ANDROID_CONFIGURATION_H
diff --git a/native/include/android/native_activity.h b/native/include/android/native_activity.h
index ee4204d..d74e1ce 100644
--- a/native/include/android/native_activity.h
+++ b/native/include/android/native_activity.h
@@ -197,6 +197,12 @@
     void (*onContentRectChanged)(ANativeActivity* activity, const ARect* rect);
 
     /**
+     * The current device AConfiguration has changed.  The new configuration can
+     * be retrieved from assetManager.
+     */
+    void (*onConfigurationChanged)(ANativeActivity* activity);
+
+    /**
      * The system is running low on memory.  Use this callback to release
      * resources you do not need, to help the system avoid killing more
      * important processes.
@@ -208,7 +214,9 @@
  * This is the function that must be in the native code to instantiate the
  * application's native activity.  It is called with the activity instance (see
  * above); if the code is being instantiated from a previously saved instance,
- * the savedState will be non-NULL and point to the saved data.
+ * the savedState will be non-NULL and point to the saved data.  You must make
+ * any copy of this data you need -- it will be released after you return from
+ * this function.
  */
 typedef void ANativeActivity_createFunc(ANativeActivity* activity,
         void* savedState, size_t savedStateSize);
diff --git a/native/include/android/native_window.h b/native/include/android/native_window.h
index 7599d7e..ad03d0e 100644
--- a/native/include/android/native_window.h
+++ b/native/include/android/native_window.h
@@ -36,12 +36,23 @@
 typedef struct ANativeWindow ANativeWindow;
 
 typedef struct ANativeWindow_Buffer {
+    // The number of pixels that are show horizontally.
     int32_t width;
+
+    // The number of pixels that are shown vertically.
     int32_t height;
+
+    // The number of *pixels* that a line in the buffer takes in
+    // memory.  This may be >= width.
     int32_t stride;
+
+    // The format of the buffer.  One of WINDOW_FORMAT_*
     int32_t format;
+
+    // The actual bits.
     void* bits;
     
+    // Do not touch.
     uint32_t reserved[6];
 } ANativeWindow_Buffer;
 
diff --git a/opengl/libs/EGL/getProcAddress.cpp b/opengl/libs/EGL/getProcAddress.cpp
index 23837ef..dcf8735c 100644
--- a/opengl/libs/EGL/getProcAddress.cpp
+++ b/opengl/libs/EGL/getProcAddress.cpp
@@ -27,9 +27,12 @@
 // ----------------------------------------------------------------------------
 
 #undef API_ENTRY
-#undef CALL_GL_API
+#undef CALL_GL_EXTENSION_API
 #undef GL_EXTENSION
 #undef GL_EXTENSION_NAME
+#undef GL_EXTENSION_ARRAY
+#undef GL_EXTENSION_LIST
+#undef GET_TLS
 
 #if defined(__arm__)
 
@@ -60,7 +63,7 @@
             :                                                   \
             );
 
-    #define GL_EXTENSION_NAME(_n)  __glExtFwd##_n
+    #define GL_EXTENSION_NAME(_n)   __glExtFwd##_n
 
     #define GL_EXTENSION(_n)                         \
         void API_ENTRY(GL_EXTENSION_NAME(_n))() {    \
@@ -78,97 +81,40 @@
 
 #endif
 
-GL_EXTENSION(0)
-GL_EXTENSION(1)
-GL_EXTENSION(2)
-GL_EXTENSION(3)
-GL_EXTENSION(4)
-GL_EXTENSION(5)
-GL_EXTENSION(6)
-GL_EXTENSION(7)
-GL_EXTENSION(8)
-GL_EXTENSION(9)
-GL_EXTENSION(10)
-GL_EXTENSION(11)
-GL_EXTENSION(12)
-GL_EXTENSION(13)
-GL_EXTENSION(14)
-GL_EXTENSION(15)
+#define GL_EXTENSION_LIST(name) \
+        name(0)   name(1)   name(2)   name(3)   \
+        name(4)   name(5)   name(6)   name(7)   \
+        name(8)   name(9)   name(10)  name(11)  \
+        name(12)  name(13)  name(14)  name(15)  \
+        name(16)  name(17)  name(18)  name(19)  \
+        name(20)  name(21)  name(22)  name(23)  \
+        name(24)  name(25)  name(26)  name(27)  \
+        name(28)  name(29)  name(30)  name(31)  \
+        name(32)  name(33)  name(34)  name(35)  \
+        name(36)  name(37)  name(38)  name(39)  \
+        name(40)  name(41)  name(42)  name(43)  \
+        name(44)  name(45)  name(46)  name(47)  \
+        name(48)  name(49)  name(50)  name(51)  \
+        name(52)  name(53)  name(54)  name(55)  \
+        name(56)  name(57)  name(58)  name(59)  \
+        name(60)  name(61)  name(62)  name(63)
 
-GL_EXTENSION(16)
-GL_EXTENSION(17)
-GL_EXTENSION(18)
-GL_EXTENSION(19)
-GL_EXTENSION(20)
-GL_EXTENSION(21)
-GL_EXTENSION(22)
-GL_EXTENSION(23)
-GL_EXTENSION(24)
-GL_EXTENSION(25)
-GL_EXTENSION(26)
-GL_EXTENSION(27)
-GL_EXTENSION(28)
-GL_EXTENSION(29)
-GL_EXTENSION(30)
-GL_EXTENSION(31)
 
-GL_EXTENSION(32)
-GL_EXTENSION(33)
-GL_EXTENSION(34)
-GL_EXTENSION(35)
-GL_EXTENSION(36)
-GL_EXTENSION(37)
-GL_EXTENSION(38)
-GL_EXTENSION(39)
-GL_EXTENSION(40)
-GL_EXTENSION(41)
-GL_EXTENSION(42)
-GL_EXTENSION(43)
-GL_EXTENSION(44)
-GL_EXTENSION(45)
-GL_EXTENSION(46)
-GL_EXTENSION(47)
+GL_EXTENSION_LIST( GL_EXTENSION )
 
-GL_EXTENSION(48)
-GL_EXTENSION(49)
-GL_EXTENSION(50)
-GL_EXTENSION(51)
-GL_EXTENSION(52)
-GL_EXTENSION(53)
-GL_EXTENSION(54)
-GL_EXTENSION(55)
-GL_EXTENSION(56)
-GL_EXTENSION(57)
-GL_EXTENSION(58)
-GL_EXTENSION(59)
-GL_EXTENSION(60)
-GL_EXTENSION(61)
-GL_EXTENSION(62)
-GL_EXTENSION(63)
+#define GL_EXTENSION_ARRAY(_n)  GL_EXTENSION_NAME(_n),
 
 extern const __eglMustCastToProperFunctionPointerType gExtensionForwarders[MAX_NUMBER_OF_GL_EXTENSIONS] = {
-     GL_EXTENSION_NAME(0),  GL_EXTENSION_NAME(1),  GL_EXTENSION_NAME(2),  GL_EXTENSION_NAME(3),
-     GL_EXTENSION_NAME(4),  GL_EXTENSION_NAME(5),  GL_EXTENSION_NAME(6),  GL_EXTENSION_NAME(7),
-     GL_EXTENSION_NAME(8),  GL_EXTENSION_NAME(9),  GL_EXTENSION_NAME(10), GL_EXTENSION_NAME(11),
-     GL_EXTENSION_NAME(12), GL_EXTENSION_NAME(13), GL_EXTENSION_NAME(14), GL_EXTENSION_NAME(15),
-     GL_EXTENSION_NAME(16), GL_EXTENSION_NAME(17), GL_EXTENSION_NAME(18), GL_EXTENSION_NAME(19),
-     GL_EXTENSION_NAME(20), GL_EXTENSION_NAME(21), GL_EXTENSION_NAME(22), GL_EXTENSION_NAME(23),
-     GL_EXTENSION_NAME(24), GL_EXTENSION_NAME(25), GL_EXTENSION_NAME(26), GL_EXTENSION_NAME(27),
-     GL_EXTENSION_NAME(28), GL_EXTENSION_NAME(29), GL_EXTENSION_NAME(30), GL_EXTENSION_NAME(31),
-     GL_EXTENSION_NAME(32), GL_EXTENSION_NAME(33), GL_EXTENSION_NAME(34), GL_EXTENSION_NAME(35),
-     GL_EXTENSION_NAME(36), GL_EXTENSION_NAME(37), GL_EXTENSION_NAME(38), GL_EXTENSION_NAME(39),
-     GL_EXTENSION_NAME(40), GL_EXTENSION_NAME(41), GL_EXTENSION_NAME(42), GL_EXTENSION_NAME(43),
-     GL_EXTENSION_NAME(44), GL_EXTENSION_NAME(45), GL_EXTENSION_NAME(46), GL_EXTENSION_NAME(47),
-     GL_EXTENSION_NAME(48), GL_EXTENSION_NAME(49), GL_EXTENSION_NAME(50), GL_EXTENSION_NAME(51),
-     GL_EXTENSION_NAME(52), GL_EXTENSION_NAME(53), GL_EXTENSION_NAME(54), GL_EXTENSION_NAME(55),
-     GL_EXTENSION_NAME(56), GL_EXTENSION_NAME(57), GL_EXTENSION_NAME(58), GL_EXTENSION_NAME(59),
-     GL_EXTENSION_NAME(60), GL_EXTENSION_NAME(61), GL_EXTENSION_NAME(62), GL_EXTENSION_NAME(63)
+        GL_EXTENSION_LIST( GL_EXTENSION_ARRAY )
  };
 
+#undef GET_TLS
+#undef GL_EXTENSION_LIST
+#undef GL_EXTENSION_ARRAY
 #undef GL_EXTENSION_NAME
 #undef GL_EXTENSION
 #undef API_ENTRY
-#undef CALL_GL_API
+#undef CALL_GL_EXTENSION_API
 
 // ----------------------------------------------------------------------------
 }; // namespace android
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index f1c6532..c6e0a24 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -24,6 +24,8 @@
 import android.content.pm.PackageInfoLite;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageParser;
+import android.content.res.ObbInfo;
+import android.content.res.ObbScanner;
 import android.net.Uri;
 import android.os.Environment;
 import android.os.IBinder;
@@ -142,6 +144,10 @@
         public boolean checkFreeStorage(boolean external, Uri fileUri) {
             return checkFreeStorageInner(external, fileUri);
         }
+
+        public ObbInfo getObbInfo(String filename) {
+            return ObbScanner.getObbInfo(filename);
+        }
     };
 
     public DefaultContainerService() {
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 83d9c47..e2e6f1a 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -37,6 +37,7 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.LocalPowerManager;
+import android.os.Looper;
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -48,15 +49,20 @@
 import com.android.internal.policy.PolicyManager;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.telephony.ITelephony;
+import com.android.internal.view.BaseInputHandler;
 import com.android.internal.widget.PointerLocationView;
 
 import android.util.Config;
 import android.util.EventLog;
 import android.util.Log;
+import android.util.Slog;
 import android.view.Display;
 import android.view.Gravity;
 import android.view.HapticFeedbackConstants;
 import android.view.IWindowManager;
+import android.view.InputChannel;
+import android.view.InputQueue;
+import android.view.InputHandler;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.WindowOrientationListener;
@@ -220,6 +226,17 @@
     
     int mPointerLocationMode = 0;
     PointerLocationView mPointerLocationView = null;
+    InputChannel mPointerLocationInputChannel;
+    
+    private final InputHandler mPointerLocationInputHandler = new BaseInputHandler() {
+        @Override
+        public void handleMotion(MotionEvent event, Runnable finishedCallback) {
+            finishedCallback.run();
+            synchronized (mLock) {
+                mPointerLocationView.addTouchEvent(event);
+            }
+        }
+    };
     
     // The current size of the screen.
     int mW, mH;
@@ -613,8 +630,26 @@
             WindowManagerImpl wm = (WindowManagerImpl)
                     mContext.getSystemService(Context.WINDOW_SERVICE);
             wm.addView(addView, lp);
+            
+            if (mPointerLocationInputChannel == null) {
+                try {
+                    mPointerLocationInputChannel =
+                        mWindowManager.monitorInput("PointerLocationView");
+                    InputQueue.registerInputChannel(mPointerLocationInputChannel,
+                            mPointerLocationInputHandler, mHandler.getLooper().getQueue());
+                } catch (RemoteException ex) {
+                    Slog.e(TAG, "Could not set up input monitoring channel for PointerLocation.",
+                            ex);
+                }
+            }
         }
         if (removeView != null) {
+            if (mPointerLocationInputChannel != null) {
+                InputQueue.unregisterInputChannel(mPointerLocationInputChannel);
+                mPointerLocationInputChannel.dispose();
+                mPointerLocationInputChannel = null;
+            }
+            
             WindowManagerImpl wm = (WindowManagerImpl)
                     mContext.getSystemService(Context.WINDOW_SERVICE);
             wm.removeView(removeView);
@@ -728,20 +763,6 @@
                 : Configuration.KEYBOARDHIDDEN_YES;
     }
     
-    public void dispatchedPointerEventLw(MotionEvent ev, int targetX, int targetY) {
-        if (mPointerLocationView == null) {
-            return;
-        }
-        synchronized (mLock) {
-            if (mPointerLocationView == null) {
-                return;
-            }
-            ev.offsetLocation(targetX, targetY);
-            mPointerLocationView.addTouchEvent(ev);
-            ev.offsetLocation(-targetX, -targetY);
-        }
-    }
-    
     /** {@inheritDoc} */
     public int windowTypeToLayerLw(int type) {
         if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
diff --git a/services/java/com/android/server/InputManager.java b/services/java/com/android/server/InputManager.java
index 91951233..c2c799b 100644
--- a/services/java/com/android/server/InputManager.java
+++ b/services/java/com/android/server/InputManager.java
@@ -75,7 +75,8 @@
             int sw);
     private static native boolean nativeHasKeys(int deviceId, int sourceMask,
             int[] keyCodes, boolean[] keyExists);
-    private static native void nativeRegisterInputChannel(InputChannel inputChannel);
+    private static native void nativeRegisterInputChannel(InputChannel inputChannel,
+            boolean monitor);
     private static native void nativeUnregisterInputChannel(InputChannel inputChannel);
     private static native int nativeInjectInputEvent(InputEvent event,
             int injectorPid, int injectorUid, int syncMode, int timeoutMillis);
@@ -225,14 +226,38 @@
         return nativeHasKeys(deviceId, sourceMask, keyCodes, keyExists);
     }
     
+    /**
+     * Creates an input channel that will receive all input from the input dispatcher.
+     * @param inputChannelName The input channel name.
+     * @return The input channel.
+     */
+    public InputChannel monitorInput(String inputChannelName) {
+        if (inputChannelName == null) {
+            throw new IllegalArgumentException("inputChannelName must not be null.");
+        }
+        
+        InputChannel[] inputChannels = InputChannel.openInputChannelPair(inputChannelName);
+        nativeRegisterInputChannel(inputChannels[0], true);
+        inputChannels[0].dispose(); // don't need to retain the Java object reference
+        return inputChannels[1];
+    }
+
+    /**
+     * Registers an input channel so that it can be used as an input event target.
+     * @param inputChannel The input channel to register.
+     */
     public void registerInputChannel(InputChannel inputChannel) {
         if (inputChannel == null) {
             throw new IllegalArgumentException("inputChannel must not be null.");
         }
         
-        nativeRegisterInputChannel(inputChannel);
+        nativeRegisterInputChannel(inputChannel, false);
     }
     
+    /**
+     * Unregisters an input channel.
+     * @param inputChannel The input channel to unregister.
+     */
     public void unregisterInputChannel(InputChannel inputChannel) {
         if (inputChannel == null) {
             throw new IllegalArgumentException("inputChannel must not be null.");
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index d7b92ec..344bfc1 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -16,34 +16,42 @@
 
 package com.android.server;
 
+import com.android.internal.app.IMediaContainerService;
 import com.android.server.am.ActivityManagerService;
 
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.ServiceConnection;
 import android.content.pm.PackageManager;
 import android.content.res.ObbInfo;
-import android.content.res.ObbScanner;
 import android.net.Uri;
-import android.os.storage.IMountService;
-import android.os.storage.IMountServiceListener;
-import android.os.storage.IMountShutdownObserver;
-import android.os.storage.StorageResultCode;
 import android.os.Binder;
+import android.os.Environment;
 import android.os.Handler;
 import android.os.HandlerThread;
+import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
-import android.os.IBinder;
-import android.os.Environment;
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.SystemProperties;
+import android.os.storage.IMountService;
+import android.os.storage.IMountServiceListener;
+import android.os.storage.IMountShutdownObserver;
+import android.os.storage.IObbActionListener;
+import android.os.storage.StorageResultCode;
 import android.util.Slog;
+
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
 
 /**
  * MountService implements back-end services for platform storage
@@ -135,9 +143,77 @@
     final private HashSet<String> mAsecMountSet = new HashSet<String>();
 
     /**
-     * Private hash of currently mounted filesystem images.
+     * Mounted OBB tracking information. Used to track the current state of all
+     * OBBs.
      */
-    final private HashSet<String> mObbMountSet = new HashSet<String>();
+    final private Map<IObbActionListener, ObbState> mObbMounts = new HashMap<IObbActionListener, ObbState>();
+    final private Map<String, ObbState> mObbPathToStateMap = new HashMap<String, ObbState>();
+
+    class ObbState implements IBinder.DeathRecipient {
+        public ObbState(String filename, IObbActionListener token, int callerUid) {
+            this.filename = filename;
+            this.token = token;
+            this.callerUid = callerUid;
+            mounted = false;
+        }
+
+        // OBB source filename
+        String filename;
+
+        // Token of remote Binder caller
+        IObbActionListener token;
+
+        // Binder.callingUid()
+        public int callerUid;
+
+        // Whether this is mounted currently.
+        boolean mounted;
+
+        @Override
+        public void binderDied() {
+            ObbAction action = new UnmountObbAction(this, true);
+            mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
+
+            removeObbState(this);
+
+            token.asBinder().unlinkToDeath(this, 0);
+        }
+    }
+
+    // OBB Action Handler
+    final private ObbActionHandler mObbActionHandler;
+
+    // OBB action handler messages
+    private static final int OBB_RUN_ACTION = 1;
+    private static final int OBB_MCS_BOUND = 2;
+    private static final int OBB_MCS_UNBIND = 3;
+    private static final int OBB_MCS_RECONNECT = 4;
+    private static final int OBB_MCS_GIVE_UP = 5;
+
+    /*
+     * Default Container Service information
+     */
+    static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName(
+            "com.android.defcontainer", "com.android.defcontainer.DefaultContainerService");
+
+    final private DefaultContainerConnection mDefContainerConn = new DefaultContainerConnection();
+
+    class DefaultContainerConnection implements ServiceConnection {
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            if (DEBUG_OBB)
+                Slog.i(TAG, "onServiceConnected");
+            IMediaContainerService imcs = IMediaContainerService.Stub.asInterface(service);
+            mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_MCS_BOUND, imcs));
+        }
+
+        public void onServiceDisconnected(ComponentName name) {
+            if (DEBUG_OBB)
+                Slog.i(TAG, "onServiceDisconnected");
+        }
+    };
+
+    // Used in the ObbActionHandler
+    private IMediaContainerService mContainerService = null;
 
     // Handler messages
     private static final int H_UNMOUNT_PM_UPDATE = 1;
@@ -359,7 +435,7 @@
 
         public void binderDied() {
             if (LOCAL_LOGD) Slog.d(TAG, "An IMountServiceListener has died!");
-            synchronized(mListeners) {
+            synchronized (mListeners) {
                 mListeners.remove(this);
                 mListener.asBinder().unlinkToDeath(this, 0);
             }
@@ -904,6 +980,9 @@
         mHandlerThread.start();
         mHandler = new MountServiceHandler(mHandlerThread.getLooper());
 
+        // Add OBB Action Handler to MountService thread.
+        mObbActionHandler = new ObbActionHandler(mHandlerThread.getLooper());
+
         /*
          * Vold does not run in the simulator, so pretend the connector thread
          * ran and did its thing.
@@ -1338,12 +1417,16 @@
         mHandler.sendEmptyMessage(H_UNMOUNT_PM_DONE);
     }
 
-    private boolean isCallerOwnerOfPackage(String packageName) {
+    private boolean isCallerOwnerOfPackageOrSystem(String packageName) {
         final int callerUid = Binder.getCallingUid();
-        return isUidOwnerOfPackage(packageName, callerUid);
+        return isUidOwnerOfPackageOrSystem(packageName, callerUid);
     }
 
-    private boolean isUidOwnerOfPackage(String packageName, int callerUid) {
+    private boolean isUidOwnerOfPackageOrSystem(String packageName, int callerUid) {
+        if (callerUid == android.os.Process.SYSTEM_UID) {
+            return true;
+        }
+
         if (packageName == null) {
             return false;
         }
@@ -1362,12 +1445,6 @@
         waitForReady();
         warnOnNotMounted();
 
-        // XXX replace with call to IMediaContainerService
-        ObbInfo obbInfo = ObbScanner.getObbInfo(filename);
-        if (!isCallerOwnerOfPackage(obbInfo.packageName)) {
-            throw new IllegalArgumentException("Caller package does not match OBB file");
-        }
-
         try {
             ArrayList<String> rsp = mConnector.doCommand(String.format("obb path %s", filename));
             String []tok = rsp.get(0).split(" ");
@@ -1379,7 +1456,7 @@
         } catch (NativeDaemonConnectorException e) {
             int code = e.getCode();
             if (code == VoldResponseCode.OpFailedStorageNotFound) {
-                throw new IllegalArgumentException(String.format("OBB '%s' not found", filename));
+                return null;
             } else {
                 throw new IllegalStateException(String.format("Unexpected response code %d", code));
             }
@@ -1387,95 +1464,390 @@
     }
 
     public boolean isObbMounted(String filename) {
-        waitForReady();
-        warnOnNotMounted();
-
-        // XXX replace with call to IMediaContainerService
-        ObbInfo obbInfo = ObbScanner.getObbInfo(filename);
-        if (!isCallerOwnerOfPackage(obbInfo.packageName)) {
-            throw new IllegalArgumentException("Caller package does not match OBB file");
-        }
-
-        synchronized (mObbMountSet) {
-            return mObbMountSet.contains(filename);
+        synchronized (mObbMounts) {
+            return mObbPathToStateMap.containsKey(filename);
         }
     }
 
-    public int mountObb(String filename, String key) {
+    public void mountObb(String filename, String key, IObbActionListener token) {
         waitForReady();
         warnOnNotMounted();
 
-        synchronized (mObbMountSet) {
-            if (mObbMountSet.contains(filename)) {
-                return StorageResultCode.OperationFailedStorageMounted;
+        final ObbState obbState;
+
+        synchronized (mObbMounts) {
+            if (isObbMounted(filename)) {
+                throw new IllegalArgumentException("OBB file is already mounted");
             }
+
+            if (mObbMounts.containsKey(token)) {
+                throw new IllegalArgumentException("You may only have one OBB mounted at a time");
+            }
+
+            final int callerUid = Binder.getCallingUid();
+            obbState = new ObbState(filename, token, callerUid);
+            addObbState(obbState);
         }
 
-        final int callerUid = Binder.getCallingUid();
-
-        // XXX replace with call to IMediaContainerService
-        ObbInfo obbInfo = ObbScanner.getObbInfo(filename);
-        if (!isUidOwnerOfPackage(obbInfo.packageName, callerUid)) {
-            throw new IllegalArgumentException("Caller package does not match OBB file");
-        }
-
-        if (key == null) {
-            key = "none";
-        }
-
-        int rc = StorageResultCode.OperationSucceeded;
-        String cmd = String.format("obb mount %s %s %d", filename, key, callerUid);
         try {
-            mConnector.doCommand(cmd);
-        } catch (NativeDaemonConnectorException e) {
-            int code = e.getCode();
-            if (code != VoldResponseCode.OpFailedStorageBusy) {
-                rc = StorageResultCode.OperationFailedInternalError;
-            }
+            token.asBinder().linkToDeath(obbState, 0);
+        } catch (RemoteException rex) {
+            Slog.e(TAG, "Failed to link to listener death");
         }
 
-        if (rc == StorageResultCode.OperationSucceeded) {
-            synchronized (mObbMountSet) {
-                mObbMountSet.add(filename);
-            }
-        }
-        return rc;
+        MountObbAction action = new MountObbAction(obbState, key);
+        mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
+
+        if (DEBUG_OBB)
+            Slog.i(TAG, "Send to OBB handler: " + action.toString());
     }
 
-    public int unmountObb(String filename, boolean force) {
-        waitForReady();
-        warnOnNotMounted();
+    public void unmountObb(String filename, boolean force, IObbActionListener token) {
+        final ObbState obbState;
 
-        ObbInfo obbInfo = ObbScanner.getObbInfo(filename);
-        if (!isCallerOwnerOfPackage(obbInfo.packageName)) {
-            throw new IllegalArgumentException("Caller package does not match OBB file");
+        synchronized (mObbMounts) {
+            if (!isObbMounted(filename)) {
+                throw new IllegalArgumentException("OBB is not mounted");
+            }
+            obbState = mObbPathToStateMap.get(filename);
         }
 
-        synchronized (mObbMountSet) {
-            if (!mObbMountSet.contains(filename)) {
-                return StorageResultCode.OperationFailedStorageNotMounted;
-            }
-         }
+        UnmountObbAction action = new UnmountObbAction(obbState, force);
+        mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
 
-        int rc = StorageResultCode.OperationSucceeded;
-        String cmd = String.format("obb unmount %s%s", filename, (force ? " force" : ""));
-        try {
-            mConnector.doCommand(cmd);
-        } catch (NativeDaemonConnectorException e) {
-            int code = e.getCode();
-            if (code == VoldResponseCode.OpFailedStorageBusy) {
-                rc = StorageResultCode.OperationFailedStorageBusy;
+        if (DEBUG_OBB)
+            Slog.i(TAG, "Send to OBB handler: " + action.toString());
+    }
+
+    private void addObbState(ObbState obbState) {
+        synchronized (mObbMounts) {
+            mObbMounts.put(obbState.token, obbState);
+            mObbPathToStateMap.put(obbState.filename, obbState);
+        }
+    }
+
+    private void removeObbState(ObbState obbState) {
+        synchronized (mObbMounts) {
+            mObbMounts.remove(obbState.token);
+            mObbPathToStateMap.remove(obbState.filename);
+        }
+    }
+
+    private class ObbActionHandler extends Handler {
+        private boolean mBound = false;
+        private List<ObbAction> mActions = new LinkedList<ObbAction>();
+
+        ObbActionHandler(Looper l) {
+            super(l);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case OBB_RUN_ACTION: {
+                    ObbAction action = (ObbAction) msg.obj;
+
+                    if (DEBUG_OBB)
+                        Slog.i(TAG, "OBB_RUN_ACTION: " + action.toString());
+
+                    // If a bind was already initiated we don't really
+                    // need to do anything. The pending install
+                    // will be processed later on.
+                    if (!mBound) {
+                        // If this is the only one pending we might
+                        // have to bind to the service again.
+                        if (!connectToService()) {
+                            Slog.e(TAG, "Failed to bind to media container service");
+                            action.handleError();
+                            return;
+                        } else {
+                            // Once we bind to the service, the first
+                            // pending request will be processed.
+                            mActions.add(action);
+                        }
+                    } else {
+                        // Already bound to the service. Just make
+                        // sure we trigger off processing the first request.
+                        if (mActions.size() == 0) {
+                            mObbActionHandler.sendEmptyMessage(OBB_MCS_BOUND);
+                        }
+
+                        mActions.add(action);
+                    }
+                    break;
+                }
+                case OBB_MCS_BOUND: {
+                    if (DEBUG_OBB)
+                        Slog.i(TAG, "OBB_MCS_BOUND");
+                    if (msg.obj != null) {
+                        mContainerService = (IMediaContainerService) msg.obj;
+                    }
+                    if (mContainerService == null) {
+                        // Something seriously wrong. Bail out
+                        Slog.e(TAG, "Cannot bind to media container service");
+                        for (ObbAction action : mActions) {
+                            // Indicate service bind error
+                            action.handleError();
+                        }
+                        mActions.clear();
+                    } else if (mActions.size() > 0) {
+                        ObbAction action = mActions.get(0);
+                        if (action != null) {
+                            action.execute(this);
+                        }
+                    } else {
+                        // Should never happen ideally.
+                        Slog.w(TAG, "Empty queue");
+                    }
+                    break;
+                }
+                case OBB_MCS_RECONNECT: {
+                    if (DEBUG_OBB)
+                        Slog.i(TAG, "OBB_MCS_RECONNECT");
+                    if (mActions.size() > 0) {
+                        if (mBound) {
+                            disconnectService();
+                        }
+                        if (!connectToService()) {
+                            Slog.e(TAG, "Failed to bind to media container service");
+                            for (ObbAction action : mActions) {
+                                // Indicate service bind error
+                                action.handleError();
+                            }
+                            mActions.clear();
+                        }
+                    }
+                    break;
+                }
+                case OBB_MCS_UNBIND: {
+                    if (DEBUG_OBB)
+                        Slog.i(TAG, "OBB_MCS_UNBIND");
+
+                    // Delete pending install
+                    if (mActions.size() > 0) {
+                        mActions.remove(0);
+                    }
+                    if (mActions.size() == 0) {
+                        if (mBound) {
+                            disconnectService();
+                        }
+                    } else {
+                        // There are more pending requests in queue.
+                        // Just post MCS_BOUND message to trigger processing
+                        // of next pending install.
+                        mObbActionHandler.sendEmptyMessage(OBB_MCS_BOUND);
+                    }
+                    break;
+                }
+                case OBB_MCS_GIVE_UP: {
+                    if (DEBUG_OBB)
+                        Slog.i(TAG, "OBB_MCS_GIVE_UP");
+                    mActions.remove(0);
+                    break;
+                }
+            }
+        }
+
+        private boolean connectToService() {
+            if (DEBUG_OBB)
+                Slog.i(TAG, "Trying to bind to DefaultContainerService");
+
+            Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
+            if (mContext.bindService(service, mDefContainerConn, Context.BIND_AUTO_CREATE)) {
+                mBound = true;
+                return true;
+            }
+            return false;
+        }
+
+        private void disconnectService() {
+            mContainerService = null;
+            mBound = false;
+            mContext.unbindService(mDefContainerConn);
+        }
+    }
+
+    abstract class ObbAction {
+        private static final int MAX_RETRIES = 3;
+        private int mRetries;
+
+        ObbState mObbState;
+
+        ObbAction(ObbState obbState) {
+            mObbState = obbState;
+        }
+
+        public void execute(ObbActionHandler handler) {
+            try {
+                if (DEBUG_OBB)
+                    Slog.i(TAG, "Starting to execute action: " + this.toString());
+                mRetries++;
+                if (mRetries > MAX_RETRIES) {
+                    Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
+                    mObbActionHandler.sendEmptyMessage(OBB_MCS_GIVE_UP);
+                    handleError();
+                    return;
+                } else {
+                    handleExecute();
+                    if (DEBUG_OBB)
+                        Slog.i(TAG, "Posting install MCS_UNBIND");
+                    mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
+                }
+            } catch (RemoteException e) {
+                if (DEBUG_OBB)
+                    Slog.i(TAG, "Posting install MCS_RECONNECT");
+                mObbActionHandler.sendEmptyMessage(OBB_MCS_RECONNECT);
+            } catch (Exception e) {
+                if (DEBUG_OBB)
+                    Slog.d(TAG, "Error handling OBB action", e);
+                handleError();
+            }
+        }
+
+        abstract void handleExecute() throws RemoteException;
+        abstract void handleError();
+    }
+
+    class MountObbAction extends ObbAction {
+        private String mKey;
+
+        MountObbAction(ObbState obbState, String key) {
+            super(obbState);
+            mKey = key;
+        }
+
+        public void handleExecute() throws RemoteException {
+            ObbInfo obbInfo = mContainerService.getObbInfo(mObbState.filename);
+            if (!isUidOwnerOfPackageOrSystem(obbInfo.packageName, mObbState.callerUid)) {
+                throw new IllegalArgumentException("Caller package does not match OBB file");
+            }
+
+            if (mKey == null) {
+                mKey = "none";
+            }
+
+            int rc = StorageResultCode.OperationSucceeded;
+            String cmd = String.format("obb mount %s %s %d", mObbState.filename, mKey,
+                    mObbState.callerUid);
+            try {
+                mConnector.doCommand(cmd);
+            } catch (NativeDaemonConnectorException e) {
+                int code = e.getCode();
+                if (code != VoldResponseCode.OpFailedStorageBusy) {
+                    rc = StorageResultCode.OperationFailedInternalError;
+                }
+            }
+
+            if (rc == StorageResultCode.OperationSucceeded) {
+                try {
+                    mObbState.token.onObbResult(mObbState.filename, "mounted");
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
+                }
             } else {
-                rc = StorageResultCode.OperationFailedInternalError;
+                Slog.e(TAG, "Couldn't mount OBB file");
+
+                // We didn't succeed, so remove this from the mount-set.
+                removeObbState(mObbState);
             }
         }
 
-        if (rc == StorageResultCode.OperationSucceeded) {
-            synchronized (mObbMountSet) {
-                mObbMountSet.remove(filename);
+        public void handleError() {
+            removeObbState(mObbState);
+
+            try {
+                mObbState.token.onObbResult(mObbState.filename, "error");
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Couldn't send back OBB mount error for " + mObbState.filename);
             }
         }
-        return rc;
+
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+            sb.append("MountObbAction{");
+            sb.append("filename=");
+            sb.append(mObbState.filename);
+            sb.append(",callerUid=");
+            sb.append(mObbState.callerUid);
+            sb.append(",token=");
+            sb.append(mObbState.token != null ? mObbState.token.toString() : "NULL");
+            sb.append('}');
+            return sb.toString();
+        }
+    }
+
+    class UnmountObbAction extends ObbAction {
+        private boolean mForceUnmount;
+
+        UnmountObbAction(ObbState obbState, boolean force) {
+            super(obbState);
+            mForceUnmount = force;
+        }
+
+        public void handleExecute() throws RemoteException {
+            ObbInfo obbInfo = mContainerService.getObbInfo(mObbState.filename);
+
+            if (!isCallerOwnerOfPackageOrSystem(obbInfo.packageName)) {
+                throw new IllegalArgumentException("Caller package does not match OBB file");
+            }
+
+            int rc = StorageResultCode.OperationSucceeded;
+            String cmd = String.format("obb unmount %s%s", mObbState.filename,
+                    (mForceUnmount ? " force" : ""));
+            try {
+                mConnector.doCommand(cmd);
+            } catch (NativeDaemonConnectorException e) {
+                int code = e.getCode();
+                if (code == VoldResponseCode.OpFailedStorageBusy) {
+                    rc = StorageResultCode.OperationFailedStorageBusy;
+                } else {
+                    rc = StorageResultCode.OperationFailedInternalError;
+                }
+            }
+
+            if (rc == StorageResultCode.OperationSucceeded) {
+                removeObbState(mObbState);
+
+                try {
+                    mObbState.token.onObbResult(mObbState.filename, "unmounted");
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
+                }
+            } else {
+                try {
+                    mObbState.token.onObbResult(mObbState.filename, "error");
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
+                }
+            }
+        }
+
+        public void handleError() {
+            removeObbState(mObbState);
+
+            try {
+                mObbState.token.onObbResult(mObbState.filename, "error");
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Couldn't send back OBB unmount error for " + mObbState.filename);
+            }
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+            sb.append("UnmountObbAction{");
+            sb.append("filename=");
+            sb.append(mObbState.filename != null ? mObbState.filename : "null");
+            sb.append(",force=");
+            sb.append(mForceUnmount);
+            sb.append(",callerUid=");
+            sb.append(mObbState.callerUid);
+            sb.append(",token=");
+            sb.append(mObbState.token != null ? mObbState.token.toString() : "null");
+            sb.append('}');
+            return sb.toString();
+        }
     }
 }
 
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 0def5f2..59deef3 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -62,10 +62,14 @@
 import android.content.pm.PackageManager;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
+import android.graphics.Canvas;
 import android.graphics.Matrix;
+import android.graphics.Paint;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.graphics.Region;
+import android.graphics.Typeface;
+import android.graphics.Paint.FontMetricsInt;
 import android.os.BatteryStats;
 import android.os.Binder;
 import android.os.Bundle;
@@ -92,6 +96,7 @@
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseIntArray;
+import android.util.TypedValue;
 import android.view.Display;
 import android.view.Gravity;
 import android.view.HapticFeedbackConstants;
@@ -115,6 +120,7 @@
 import android.view.WindowManager;
 import android.view.WindowManagerImpl;
 import android.view.WindowManagerPolicy;
+import android.view.Surface.OutOfResourcesException;
 import android.view.WindowManager.LayoutParams;
 import android.view.animation.AccelerateInterpolator;
 import android.view.animation.Animation;
@@ -370,6 +376,7 @@
     private DimAnimator mDimAnimator = null;
     Surface mBlurSurface;
     boolean mBlurShown;
+    Watermark mWatermark;
 
     int mTransactionSequence = 0;
 
@@ -4382,11 +4389,19 @@
         }
         return mInputManager.getKeyCodeState(-1, InputDevice.SOURCE_DPAD, sw);
     }
-
+    
     public boolean hasKeys(int[] keycodes, boolean[] keyExists) {
         return mInputManager.hasKeys(-1, InputDevice.SOURCE_ANY, keycodes, keyExists);
     }
 
+    public InputChannel monitorInput(String inputChannelName) {
+        if (!checkCallingPermission(android.Manifest.permission.READ_INPUT_STATE,
+                "monitorInput()")) {
+            throw new SecurityException("Requires READ_INPUT_STATE permission");
+        }
+        return mInputManager.monitorInput(inputChannelName);
+    }
+
     public void enableScreenAfterBoot() {
         synchronized(mWindowMap) {
             if (mSystemBooted) {
@@ -8507,12 +8522,6 @@
             updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES);
         }
         
-        if (mFxSession == null) {
-            mFxSession = new SurfaceSession();
-        }
-
-        if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION");
-
         // Initialize state of exiting tokens.
         for (i=mExitingTokens.size()-1; i>=0; i--) {
             mExitingTokens.get(i).hasVisible = false;
@@ -8529,8 +8538,24 @@
         float buttonBrightness = -1;
         boolean focusDisplayed = false;
         boolean animating = false;
+        boolean createWatermark = false;
+
+        if (mFxSession == null) {
+            mFxSession = new SurfaceSession();
+            createWatermark = true;
+        }
+
+        if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION");
 
         Surface.openTransaction();
+
+        if (createWatermark) {
+            createWatermark();
+        }
+        if (mWatermark != null) {
+            mWatermark.positionSurface(dw, dh);
+        }
+
         try {
             boolean wallpaperForceHidingChanged = false;
             int repeats = 0;
@@ -10047,6 +10072,93 @@
         mScreenFrozenLock.release();
     }
 
+    static int getPropertyInt(String name, int defUnits, int defDps, DisplayMetrics dm) {
+        String str = SystemProperties.get(name);
+        try {
+            int val = Integer.parseInt(str);
+            return val;
+        } catch (Exception e) {
+        }
+        if (defUnits == TypedValue.COMPLEX_UNIT_PX) {
+            return defDps;
+        }
+        int val = (int)TypedValue.applyDimension(defUnits, defDps, dm);
+        return val;
+    }
+
+    class Watermark {
+        Surface mSurface;
+        int mWidth;
+        int mHeight;
+        int mXPercent;
+        int mYPercent;
+
+        Watermark(SurfaceSession session, String text) {
+            final DisplayMetrics dm = new DisplayMetrics();
+            mDisplay.getMetrics(dm);
+
+            int fontSize = getPropertyInt("ro.watermark.height",
+                    TypedValue.COMPLEX_UNIT_DIP, 48, dm);
+            mXPercent = getPropertyInt("ro.watermark.x",
+                    TypedValue.COMPLEX_UNIT_PX, 50, dm);
+            mYPercent = getPropertyInt("ro.watermark.y",
+                    TypedValue.COMPLEX_UNIT_PX, 99, dm);
+            int color = getPropertyInt("ro.watermark.color",
+                    TypedValue.COMPLEX_UNIT_PX, 0x80ffffff, dm);
+            int shadowRadius = getPropertyInt("ro.watermark.shadow.radius",
+                    TypedValue.COMPLEX_UNIT_PX, 5, dm);
+            int shadowDx = getPropertyInt("ro.watermark.shadow.dx",
+                    TypedValue.COMPLEX_UNIT_PX, 0, dm);
+            int shadowDy = getPropertyInt("ro.watermark.shadow.dy",
+                    TypedValue.COMPLEX_UNIT_PX, 0, dm);
+            int shadowColor = getPropertyInt("ro.watermark.shadow.color",
+                    TypedValue.COMPLEX_UNIT_PX, 0xff000000, dm);
+
+            Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
+            paint.setTextSize(fontSize);
+            paint.setColor(color);
+            paint.setShadowLayer(shadowRadius, shadowDx, shadowDy, shadowColor);
+            paint.setTypeface(Typeface.create(Typeface.SANS_SERIF, Typeface.BOLD));
+
+            FontMetricsInt fm = paint.getFontMetricsInt();
+            mHeight = fm.descent - fm.ascent + 20;
+            mWidth = (int)paint.measureText(text) + 20;
+
+            try {
+                mSurface = new Surface(session, 0,
+                        "WatermarkSurface",
+                        -1, mWidth, mHeight, PixelFormat.TRANSLUCENT, 0);
+                mSurface.setLayer(TYPE_LAYER_MULTIPLIER*100);
+                Rect dirty = new Rect(0, 0, mWidth, mHeight);
+                Canvas c = mSurface.lockCanvas(dirty);
+                c.drawText(text, 10, -fm.ascent+10, paint);
+                mSurface.unlockCanvasAndPost(c);
+                mSurface.show();
+            } catch (OutOfResourcesException e) {
+            }
+        }
+
+        void positionSurface(int dw, int dh) {
+            int availW = dw - mWidth;
+            int availH = dh - mHeight;
+            mSurface.setPosition((availW*mXPercent)/100,
+                    (availH*mYPercent)/100);
+        }
+    }
+
+    void createWatermark() {
+        if (mWatermark != null) {
+            return;
+        }
+
+        String text = SystemProperties.get("ro.watermark.text");
+        if (text == null || text.length() <= 0) {
+            return;
+        }
+
+        mWatermark = new Watermark(mFxSession, text);
+    }
+
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (mContext.checkCallingOrSelfPermission("android.permission.DUMP")
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 162fffe..dff6a8a 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -3571,6 +3571,7 @@
             
             if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
                 // Tell anyone interested that we are done booting!
+                SystemProperties.set("sys.boot_completed", "1");
                 broadcastIntentLocked(null, null,
                         new Intent(Intent.ACTION_BOOT_COMPLETED, null),
                         null, null, 0, null, null,
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index 0982b32..ebe71ab 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -213,7 +213,7 @@
     void setDisplayOrientation(int32_t displayId, int32_t orientation);
 
     status_t registerInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel,
-            jweak inputChannelObjWeak);
+            jweak inputChannelObjWeak, bool monitor);
     status_t unregisterInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel);
 
     void setInputWindows(JNIEnv* env, jobjectArray windowObjArray);
@@ -334,6 +334,7 @@
     bool mWindowsReady;
     Vector<InputWindow> mWindows;
     Vector<InputWindow*> mWallpaperWindows;
+    Vector<sp<InputChannel> > mMonitoringChannels;
 
     // Focus tracking for keys, trackball, etc.
     InputWindow* mFocusedWindow;
@@ -382,6 +383,10 @@
     static void addTarget(const InputWindow* window, int32_t targetFlags,
             nsecs_t timeSpentWaitingForApplication, Vector<InputTarget>& outTargets);
 
+    void registerMonitoringChannel(const sp<InputChannel>& inputChannel);
+    void unregisterMonitoringChannel(const sp<InputChannel>& inputChannel);
+    void addMonitoringTargetsLd(Vector<InputTarget>& outTargets);
+
     static inline JNIEnv* jniEnv() {
         return AndroidRuntime::getJNIEnv();
     }
@@ -492,7 +497,7 @@
 }
 
 status_t NativeInputManager::registerInputChannel(JNIEnv* env,
-        const sp<InputChannel>& inputChannel, jobject inputChannelObj) {
+        const sp<InputChannel>& inputChannel, jobject inputChannelObj, bool monitor) {
     jweak inputChannelObjWeak = env->NewWeakGlobalRef(inputChannelObj);
     if (! inputChannelObjWeak) {
         LOGE("Could not create weak reference for input channel.");
@@ -519,9 +524,14 @@
 
     status = mInputManager->registerInputChannel(inputChannel);
     if (! status) {
+        // Success.
+        if (monitor) {
+            registerMonitoringChannel(inputChannel);
+        }
         return OK;
     }
 
+    // Failed!
     {
         AutoMutex _l(mInputChannelRegistryLock);
         mInputChannelObjWeakByReceiveFd.removeItem(inputChannel->getReceivePipeFd());
@@ -552,6 +562,8 @@
 
     env->DeleteWeakGlobalRef(inputChannelObjWeak);
 
+    unregisterMonitoringChannel(inputChannel);
+
     return mInputManager->unregisterInputChannel(inputChannel);
 }
 
@@ -829,6 +841,8 @@
 
         env->DeleteLocalRef(inputChannelObjLocal);
     }
+
+    unregisterMonitoringChannel(inputChannel);
 }
 
 bool NativeInputManager::notifyInputChannelANR(const sp<InputChannel>& inputChannel,
@@ -1429,7 +1443,9 @@
 
             // If there is no currently touched window then fail.
             if (! mTouchedWindow) {
-                LOGW("Dropping event because there is no touched window to receive it.");
+#if DEBUG_INPUT_DISPATCHER_POLICY
+                LOGD("Dropping event because there is no touched window to receive it.");
+#endif
                 injectionResult = INPUT_EVENT_INJECTION_FAILED;
                 injectionPermission = INJECTION_PERMISSION_GRANTED;
                 break; // failed, exit wait loop
@@ -1587,6 +1603,8 @@
             outTargets.clear();
             return INPUT_EVENT_INJECTION_SUCCEEDED;
         }
+
+        addMonitoringTargetsLd(outTargets);
     }
 
     pokeUserActivityIfNeeded(windowType, POWER_MANAGER_BUTTON_EVENT);
@@ -1631,6 +1649,8 @@
         }
 
         windowType = focusedWindow->layoutParamsType;
+
+        addMonitoringTargetsLd(outTargets);
     } // release lock
 
     pokeUserActivityIfNeeded(windowType, POWER_MANAGER_BUTTON_EVENT);
@@ -1657,6 +1677,8 @@
         }
 
         windowType = touchedWindow->layoutParamsType;
+
+        addMonitoringTargetsLd(outTargets);
     } // release lock
 
     int32_t eventType;
@@ -1714,6 +1736,39 @@
     android_server_PowerManagerService_userActivity(eventTime, eventType);
 }
 
+void NativeInputManager::registerMonitoringChannel(const sp<InputChannel>& inputChannel) {
+    { // acquire lock
+         AutoMutex _l(mDispatchLock);
+         mMonitoringChannels.push(inputChannel);
+    } // release lock
+}
+
+void NativeInputManager::unregisterMonitoringChannel(const sp<InputChannel>& inputChannel) {
+    { // acquire lock
+         AutoMutex _l(mDispatchLock);
+
+         for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
+             if (mMonitoringChannels[i] == inputChannel) {
+                 mMonitoringChannels.removeAt(i);
+                 break;
+             }
+         }
+    } // release lock
+}
+
+void NativeInputManager::addMonitoringTargetsLd(Vector<InputTarget>& outTargets) {
+    for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
+        outTargets.push();
+
+        InputTarget& target = outTargets.editTop();
+        target.inputChannel = mMonitoringChannels[i];
+        target.flags = 0;
+        target.timeout = -1;
+        target.xOffset = 0;
+        target.yOffset = 0;
+    }
+}
+
 static void dumpMotionRange(String8& dump,
         const char* name, const InputDeviceInfo::MotionRange* range) {
     if (range) {
@@ -1805,6 +1860,11 @@
                 mWindows[i].ownerPid, mWindows[i].ownerUid,
                 mWindows[i].dispatchingTimeout / 1000000.0);
     }
+
+    for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
+        dump.appendFormat("  monitoringChannel[%d]: '%s'\n",
+                i, mMonitoringChannels[i]->getName().string());
+    }
 }
 
 // ----------------------------------------------------------------------------
@@ -2012,7 +2072,7 @@
 }
 
 static void android_server_InputManager_nativeRegisterInputChannel(JNIEnv* env, jclass clazz,
-        jobject inputChannelObj) {
+        jobject inputChannelObj, jboolean monitor) {
     if (checkInputManagerUnitialized(env)) {
         return;
     }
@@ -2026,15 +2086,17 @@
 
 
     status_t status = gNativeInputManager->registerInputChannel(
-            env, inputChannel, inputChannelObj);
+            env, inputChannel, inputChannelObj, monitor);
     if (status) {
         jniThrowRuntimeException(env, "Failed to register input channel.  "
                 "Check logs for details.");
         return;
     }
 
-    android_view_InputChannel_setDisposeCallback(env, inputChannelObj,
-            android_server_InputManager_handleInputChannelDisposed, NULL);
+    if (! monitor) {
+        android_view_InputChannel_setDisposeCallback(env, inputChannelObj,
+                android_server_InputManager_handleInputChannelDisposed, NULL);
+    }
 }
 
 static void android_server_InputManager_nativeUnregisterInputChannel(JNIEnv* env, jclass clazz,
@@ -2149,7 +2211,7 @@
             (void*) android_server_InputManager_nativeGetSwitchState },
     { "nativeHasKeys", "(II[I[Z)Z",
             (void*) android_server_InputManager_nativeHasKeys },
-    { "nativeRegisterInputChannel", "(Landroid/view/InputChannel;)V",
+    { "nativeRegisterInputChannel", "(Landroid/view/InputChannel;Z)V",
             (void*) android_server_InputManager_nativeRegisterInputChannel },
     { "nativeUnregisterInputChannel", "(Landroid/view/InputChannel;)V",
             (void*) android_server_InputManager_nativeUnregisterInputChannel },
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index a14bfb5..79772ed 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -5,6 +5,7 @@
     clz.cpp.arm \
     DisplayHardware/DisplayHardware.cpp \
     DisplayHardware/DisplayHardwareBase.cpp \
+    DisplayHardware/HWComposer.cpp \
     BlurFilter.cpp.arm \
     GLExtensions.cpp \
     Layer.cpp \
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
index 2eac0a8..166c528 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
@@ -36,11 +36,11 @@
 
 #include "DisplayHardware/DisplayHardware.h"
 
-#include <hardware/copybit.h>
 #include <hardware/overlay.h>
 #include <hardware/gralloc.h>
 
 #include "GLExtensions.h"
+#include "HWComposer.h"
 
 using namespace android;
 
@@ -76,7 +76,7 @@
         const sp<SurfaceFlinger>& flinger,
         uint32_t dpy)
     : DisplayHardwareBase(flinger, dpy),
-      mFlags(0)
+      mFlags(0), mHwc(0)
 {
     init(dpy);
 }
@@ -262,6 +262,17 @@
 
     // Unbind the context from this thread
     eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+
+
+    // initialize the H/W composer
+    mHwc = new HWComposer();
+    if (mHwc->initCheck() == NO_ERROR) {
+        mHwc->setFrameBuffer(mDisplay, mSurface);
+    }
+}
+
+HWComposer& DisplayHardware::getHwComposer() const {
+    return *mHwc;
 }
 
 /*
@@ -317,7 +328,12 @@
     }
     
     mPageFlipCount++;
-    eglSwapBuffers(dpy, surface);
+
+    if (mHwc->initCheck() == NO_ERROR) {
+        mHwc->commit();
+    } else {
+        eglSwapBuffers(dpy, surface);
+    }
     checkEGLErrors("eglSwapBuffers");
 
     // for debugging
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardware.h b/services/surfaceflinger/DisplayHardware/DisplayHardware.h
index 66bf521..f2cfd2d 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayHardware.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.h
@@ -34,12 +34,11 @@
 #include "DisplayHardware/DisplayHardwareBase.h"
 
 struct overlay_control_device_t;
-struct framebuffer_device_t;
-struct copybit_image_t;
 
 namespace android {
 
 class FramebufferNativeWindow;
+class HWComposer;
 
 class DisplayHardware : public DisplayHardwareBase
 {
@@ -80,6 +79,9 @@
     uint32_t getPageFlipCount() const;
     EGLDisplay getEGLDisplay() const { return mDisplay; }
     overlay_control_device_t* getOverlayEngine() const { return mOverlayEngine; }
+
+    // Hardware Composer
+    HWComposer& getHwComposer() const;
     
     status_t compositionComplete() const;
     
@@ -107,6 +109,8 @@
     GLint           mMaxViewportDims;
     GLint           mMaxTextureSize;
     
+    HWComposer*     mHwc;
+
     sp<FramebufferNativeWindow> mNativeWindow;
     overlay_control_device_t* mOverlayEngine;
 };
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
new file mode 100644
index 0000000..0291d78
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+
+#include <hardware/hardware.h>
+
+#include <cutils/log.h>
+
+#include <EGL/egl.h>
+
+#include "HWComposer.h"
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+HWComposer::HWComposer()
+    : mModule(0), mHwc(0), mList(0), mCapacity(0),
+      mDpy(EGL_NO_DISPLAY), mSur(EGL_NO_SURFACE)
+{
+    int err = hw_get_module(HWC_HARDWARE_MODULE_ID, &mModule);
+    LOGW_IF(err, "%s module not found", HWC_HARDWARE_MODULE_ID);
+    if (err == 0) {
+        err = hwc_open(mModule, &mHwc);
+        LOGE_IF(err, "%s device failed to initialize (%s)",
+                HWC_HARDWARE_COMPOSER, strerror(-err));
+    }
+}
+
+HWComposer::~HWComposer() {
+    free(mList);
+    if (mHwc) {
+        hwc_close(mHwc);
+    }
+}
+
+status_t HWComposer::initCheck() const {
+    return mHwc ? NO_ERROR : NO_INIT;
+}
+
+void HWComposer::setFrameBuffer(EGLDisplay dpy, EGLSurface sur) {
+    mDpy = (hwc_display_t)dpy;
+    mSur = (hwc_surface_t)sur;
+}
+
+status_t HWComposer::createWorkList(size_t numLayers) {
+    if (mHwc) {
+        if (!mList || mCapacity < numLayers) {
+            free(mList);
+            size_t size = sizeof(hwc_layer_list) + numLayers*sizeof(hwc_layer_t);
+            mList = (hwc_layer_list_t*)malloc(size);
+            mCapacity = numLayers;
+        }
+        mList->flags = HWC_GEOMETRY_CHANGED;
+        mList->numHwLayers = numLayers;
+    }
+    return NO_ERROR;
+}
+
+status_t HWComposer::prepare() const {
+    int err = mHwc->prepare(mHwc, mList);
+    return (status_t)err;
+}
+
+status_t HWComposer::commit() const {
+    int err = mHwc->set(mHwc, mDpy, mSur, mList);
+    mList->flags &= ~HWC_GEOMETRY_CHANGED;
+    return (status_t)err;
+}
+
+size_t HWComposer::getNumLayers() const {
+    return mList ? mList->numHwLayers : 0;
+}
+
+hwc_layer_t* HWComposer::getLayers() const {
+    return mList ? mList->hwLayers : 0;
+}
+
+// ---------------------------------------------------------------------------
+}; // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
new file mode 100644
index 0000000..c5d5c2b
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#ifndef ANDROID_SF_HWCOMPOSER_H
+#define ANDROID_SF_HWCOMPOSER_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <EGL/egl.h>
+
+#include <hardware/hwcomposer.h>
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+class HWComposer
+{
+public:
+
+    HWComposer();
+    ~HWComposer();
+
+    status_t initCheck() const;
+
+    // tells the HAL what the framebuffer is
+    void setFrameBuffer(EGLDisplay dpy, EGLSurface sur);
+
+    // create a work list for numLayers layer
+    status_t createWorkList(size_t numLayers);
+
+    // Asks the HAL what it can do
+    status_t prepare() const;
+
+    // commits the list
+    status_t commit() const;
+
+
+    size_t getNumLayers() const;
+    hwc_layer_t* getLayers() const;
+
+private:
+    hw_module_t const*      mModule;
+    hwc_composer_device_t*  mHwc;
+    hwc_layer_list_t*       mList;
+    size_t                  mCapacity;
+    hwc_display_t           mDpy;
+    hwc_surface_t           mSur;
+};
+
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_SF_HWCOMPOSER_H
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 629d993..3720e1669 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -35,6 +35,7 @@
 #include "Layer.h"
 #include "SurfaceFlinger.h"
 #include "DisplayHardware/DisplayHardware.h"
+#include "DisplayHardware/HWComposer.h"
 
 
 #define DEBUG_RESIZE    0
@@ -177,6 +178,62 @@
     return NO_ERROR;
 }
 
+void Layer::setGeometry(hwc_layer_t* hwcl)
+{
+    hwcl->compositionType = HWC_FRAMEBUFFER;
+    hwcl->hints = 0;
+    hwcl->flags = 0;
+    hwcl->transform = 0;
+    hwcl->blending = HWC_BLENDING_NONE;
+
+    // we can't do alpha-fade with the hwc HAL
+    const State& s(drawingState());
+    if (s.alpha < 0xFF) {
+        hwcl->flags = HWC_SKIP_LAYER;
+        return;
+    }
+
+    // we can only handle simple transformation
+    if (mOrientation & Transform::ROT_INVALID) {
+        hwcl->flags = HWC_SKIP_LAYER;
+        return;
+    }
+
+    hwcl->transform = mOrientation;
+
+    if (needsBlending()) {
+        hwcl->blending = mPremultipliedAlpha ?
+                HWC_BLENDING_PREMULT : HWC_BLENDING_COVERAGE;
+    }
+
+    hwcl->displayFrame.left   = mTransformedBounds.left;
+    hwcl->displayFrame.top    = mTransformedBounds.top;
+    hwcl->displayFrame.right  = mTransformedBounds.right;
+    hwcl->displayFrame.bottom = mTransformedBounds.bottom;
+
+    hwcl->visibleRegionScreen.rects =
+            reinterpret_cast<hwc_rect_t const *>(
+                    visibleRegionScreen.getArray(
+                            &hwcl->visibleRegionScreen.numRects));
+}
+
+void Layer::setPerFrameData(hwc_layer_t* hwcl) {
+    sp<GraphicBuffer> buffer(mBufferManager.getActiveBuffer());
+    if (buffer == NULL) {
+        // this situation can happen if we ran out of memory for instance.
+        // not much we can do. continue to use whatever texture was bound
+        // to this context.
+        hwcl->handle = NULL;
+        return;
+    }
+    hwcl->handle = const_cast<native_handle_t*>(buffer->handle);
+    // TODO: set the crop value properly
+    hwcl->sourceCrop.left   = 0;
+    hwcl->sourceCrop.top    = 0;
+    hwcl->sourceCrop.right  = buffer->width;
+    hwcl->sourceCrop.bottom = buffer->height;
+}
+
 void Layer::reloadTexture(const Region& dirty)
 {
     sp<GraphicBuffer> buffer(mBufferManager.getActiveBuffer());
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index e1d283b..188da6a 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -68,6 +68,8 @@
     bool isFixedSize() const;
 
     // LayerBase interface
+    virtual void setGeometry(hwc_layer_t* hwcl);
+    virtual void setPerFrameData(hwc_layer_t* hwcl);
     virtual void onDraw(const Region& clip) const;
     virtual uint32_t doTransaction(uint32_t transactionFlags);
     virtual void lockPageFlip(bool& recomputeVisibleRegions);
diff --git a/services/surfaceflinger/LayerBase.cpp b/services/surfaceflinger/LayerBase.cpp
index d5aa53f..043d54d 100644
--- a/services/surfaceflinger/LayerBase.cpp
+++ b/services/surfaceflinger/LayerBase.cpp
@@ -39,8 +39,11 @@
 
 // ---------------------------------------------------------------------------
 
+int32_t LayerBase::sSequence = 1;
+
 LayerBase::LayerBase(SurfaceFlinger* flinger, DisplayID display)
     : dpy(display), contentDirty(false),
+      sequence(uint32_t(android_atomic_inc(&sSequence))),
       mFlinger(flinger),
       mNeedsFiltering(false),
       mOrientation(0),
@@ -304,22 +307,17 @@
     }
 }
 
-void LayerBase::draw(const Region& inClip) const
+void LayerBase::setGeometry(hwc_layer_t* hwcl) {
+    hwcl->flags |= HWC_SKIP_LAYER;
+}
+
+void LayerBase::setPerFrameData(hwc_layer_t* hwcl) {
+    hwcl->compositionType = HWC_FRAMEBUFFER;
+    hwcl->handle = NULL;
+}
+
+void LayerBase::draw(const Region& clip) const
 {
-    // invalidate the region we'll update
-    Region clip(inClip);  // copy-on-write, so no-op most of the time
-
-    // Remove the transparent area from the clipping region
-    const State& s = drawingState();
-    if (LIKELY(!s.transparentRegion.isEmpty())) {
-        clip.subtract(transparentRegionScreen);
-        if (clip.isEmpty()) {
-            // usually this won't happen because this should be taken care of
-            // by SurfaceFlinger::computeVisibleRegions()
-            return;
-        }        
-    }
-
     // reset GL state
     glEnable(GL_SCISSOR_TEST);
 
diff --git a/services/surfaceflinger/LayerBase.h b/services/surfaceflinger/LayerBase.h
index 4288cf7..dd1cd05 100644
--- a/services/surfaceflinger/LayerBase.h
+++ b/services/surfaceflinger/LayerBase.h
@@ -35,6 +35,8 @@
 
 #include <pixelflinger/pixelflinger.h>
 
+#include <hardware/hwcomposer.h>
+
 #include "Transform.h"
 
 namespace android {
@@ -53,6 +55,8 @@
 
 class LayerBase : public RefBase
 {
+    static int32_t sSequence;
+
 public:
             LayerBase(SurfaceFlinger* flinger, DisplayID display);
 
@@ -61,6 +65,7 @@
             Region      visibleRegionScreen;
             Region      transparentRegionScreen;
             Region      coveredRegionScreen;
+            int32_t     sequence;
             
             struct State {
                 uint32_t        w;
@@ -105,6 +110,10 @@
 
     virtual const char* getTypeId() const { return "LayerBase"; }
 
+    virtual void setGeometry(hwc_layer_t* hwcl);
+
+    virtual void setPerFrameData(hwc_layer_t* hwcl);
+
     /**
      * draw - performs some global clipping optimizations
      * and calls onDraw().
@@ -210,12 +219,6 @@
     inline  const State&    currentState() const    { return mCurrentState; }
     inline  State&          currentState()          { return mCurrentState; }
 
-    static int compareCurrentStateZ(
-            sp<LayerBase> const * layerA,
-            sp<LayerBase> const * layerB) {
-        return layerA[0]->currentState().z - layerB[0]->currentState().z;
-    }
-
     int32_t  getOrientation() const { return mOrientation; }
     int  tx() const             { return mLeft; }
     int  ty() const             { return mTop; }
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 3167c4c..006bb10 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -52,6 +52,7 @@
 #include "SurfaceFlinger.h"
 
 #include "DisplayHardware/DisplayHardware.h"
+#include "DisplayHardware/HWComposer.h"
 
 /* ideally AID_GRAPHICS would be in a semi-public header
  * or there would be a way to map a user/group name to its id
@@ -65,95 +66,6 @@
 namespace android {
 // ---------------------------------------------------------------------------
 
-SurfaceFlinger::LayerVector::LayerVector(const SurfaceFlinger::LayerVector& rhs)
-    : lookup(rhs.lookup), layers(rhs.layers)
-{
-}
-
-ssize_t SurfaceFlinger::LayerVector::indexOf(
-        const sp<LayerBase>& key, size_t guess) const
-{
-    if (guess<size() && lookup.keyAt(guess) == key)
-        return guess;
-    const ssize_t i = lookup.indexOfKey(key);
-    if (i>=0) {
-        const size_t idx = lookup.valueAt(i);
-        LOGE_IF(layers[idx]!=key,
-            "LayerVector[%p]: layers[%d]=%p, key=%p",
-            this, int(idx), layers[idx].get(), key.get());
-        return idx;
-    }
-    return i;
-}
-
-ssize_t SurfaceFlinger::LayerVector::add(
-        const sp<LayerBase>& layer,
-        Vector< sp<LayerBase> >::compar_t cmp)
-{
-    size_t count = layers.size();
-    ssize_t l = 0;
-    ssize_t h = count-1;
-    ssize_t mid;
-    sp<LayerBase> const* a = layers.array();
-    while (l <= h) {
-        mid = l + (h - l)/2;
-        const int c = cmp(a+mid, &layer);
-        if (c == 0)     { l = mid; break; }
-        else if (c<0)   { l = mid+1; }
-        else            { h = mid-1; }
-    }
-    size_t order = l;
-    while (order<count && !cmp(&layer, a+order)) {
-        order++;
-    }
-    count = lookup.size();
-    for (size_t i=0 ; i<count ; i++) {
-        if (lookup.valueAt(i) >= order) {
-            lookup.editValueAt(i)++;
-        }
-    }
-    layers.insertAt(layer, order);
-    lookup.add(layer, order);
-    return order;
-}
-
-ssize_t SurfaceFlinger::LayerVector::remove(const sp<LayerBase>& layer)
-{
-    const ssize_t keyIndex = lookup.indexOfKey(layer);
-    if (keyIndex >= 0) {
-        const size_t index = lookup.valueAt(keyIndex);
-        LOGE_IF(layers[index]!=layer,
-                "LayerVector[%p]: layers[%u]=%p, layer=%p",
-                this, int(index), layers[index].get(), layer.get());
-        layers.removeItemsAt(index);
-        lookup.removeItemsAt(keyIndex);
-        const size_t count = lookup.size();
-        for (size_t i=0 ; i<count ; i++) {
-            if (lookup.valueAt(i) >= size_t(index)) {
-                lookup.editValueAt(i)--;
-            }
-        }
-        return index;
-    }
-    return NAME_NOT_FOUND;
-}
-
-ssize_t SurfaceFlinger::LayerVector::reorder(
-        const sp<LayerBase>& layer,
-        Vector< sp<LayerBase> >::compar_t cmp)
-{
-    // XXX: it's a little lame. but oh well...
-    ssize_t err = remove(layer);
-    if (err >=0)
-        err = add(layer, cmp);
-    return err;
-}
-
-// ---------------------------------------------------------------------------
-#if 0
-#pragma mark -
-#endif
-
 SurfaceFlinger::SurfaceFlinger()
     :   BnSurfaceComposer(), Thread(false),
         mTransactionFlags(0),
@@ -165,6 +77,7 @@
         mAccessSurfaceFlinger("android.permission.ACCESS_SURFACE_FLINGER"),
         mDump("android.permission.DUMP"),
         mVisibleRegionsDirty(false),
+        mHwWorkListDirty(false),
         mDeferReleaseConsole(false),
         mFreezeDisplay(false),
         mFreezeCount(0),
@@ -457,6 +370,11 @@
     // post surfaces (if needed)
     handlePageFlip();
 
+    if (UNLIKELY(mHwWorkListDirty)) {
+        // build the h/w work list
+        handleWorkList();
+    }
+
     const DisplayHardware& hw(graphicPlane(0).displayHardware());
     if (LIKELY(hw.canDraw() && !isFrozen())) {
         // repaint the framebuffer (if needed)
@@ -521,6 +439,10 @@
 {
     Vector< sp<LayerBase> > ditchedLayers;
 
+    /*
+     * Perform and commit the transaction
+     */
+
     { // scope for the lock
         Mutex::Autolock _l(mStateLock);
         const nsecs_t now = systemTime();
@@ -528,9 +450,15 @@
         handleTransactionLocked(transactionFlags, ditchedLayers);
         mLastTransactionTime = systemTime() - now;
         mDebugInTransaction = 0;
+        mHwWorkListDirty = true;
+        // here the transaction has been committed
     }
 
-    // do this without lock held
+    /*
+     * Clean-up all layers that went away
+     * (do this without the lock held)
+     */
+
     const size_t count = ditchedLayers.size();
     for (size_t i=0 ; i<count ; i++) {
         if (ditchedLayers[i] != 0) {
@@ -764,8 +692,8 @@
 void SurfaceFlinger::handlePageFlip()
 {
     bool visibleRegions = mVisibleRegionsDirty;
-    LayerVector& currentLayers = const_cast<LayerVector&>(
-            mDrawingState.layersSortedByZ);
+    LayerVector& currentLayers(
+            const_cast<LayerVector&>(mDrawingState.layersSortedByZ));
     visibleRegions |= lockPageFlip(currentLayers);
 
         const DisplayHardware& hw = graphicPlane(0).displayHardware();
@@ -773,8 +701,22 @@
         if (visibleRegions) {
             Region opaqueRegion;
             computeVisibleRegions(currentLayers, mDirtyRegion, opaqueRegion);
+
+            /*
+             *  rebuild the visible layer list
+             */
+            mVisibleLayersSortedByZ.clear();
+            const LayerVector& currentLayers(mDrawingState.layersSortedByZ);
+            size_t count = currentLayers.size();
+            mVisibleLayersSortedByZ.setCapacity(count);
+            for (size_t i=0 ; i<count ; i++) {
+                if (!currentLayers[i]->visibleRegionScreen.isEmpty())
+                    mVisibleLayersSortedByZ.add(currentLayers[i]);
+            }
+
             mWormholeRegion = screenRegion.subtract(opaqueRegion);
             mVisibleRegionsDirty = false;
+            mHwWorkListDirty = true;
         }
 
     unlockPageFlip(currentLayers);
@@ -805,6 +747,20 @@
     }
 }
 
+void SurfaceFlinger::handleWorkList()
+{
+    mHwWorkListDirty = false;
+    HWComposer& hwc(graphicPlane(0).displayHardware().getHwComposer());
+    if (hwc.initCheck() == NO_ERROR) {
+        const Vector< sp<LayerBase> >& currentLayers(mVisibleLayersSortedByZ);
+        const size_t count = currentLayers.size();
+        hwc.createWorkList(count);
+        hwc_layer_t* const cur(hwc.getLayers());
+        for (size_t i=0 ; cur && i<count ; i++) {
+            currentLayers[i]->setGeometry(&cur[i]);
+        }
+    }
+}
 
 void SurfaceFlinger::handleRepaint()
 {
@@ -869,19 +825,75 @@
         // draw something...
         drawWormhole();
     }
-    const SurfaceFlinger& flinger(*this);
-    const LayerVector& drawingLayers(mDrawingState.layersSortedByZ);
-    const size_t count = drawingLayers.size();
-    sp<LayerBase> const* const layers = drawingLayers.array();
-    for (size_t i=0 ; i<count ; ++i) {
-        const sp<LayerBase>& layer = layers[i];
-        const Region& visibleRegion(layer->visibleRegionScreen);
-        if (!visibleRegion.isEmpty())  {
-            const Region clip(dirty.intersect(visibleRegion));
-            if (!clip.isEmpty()) {
-                layer->draw(clip);
+
+    status_t err = NO_ERROR;
+    const Vector< sp<LayerBase> >& layers(mVisibleLayersSortedByZ);
+    size_t count = layers.size();
+
+    const DisplayHardware& hw(graphicPlane(0).displayHardware());
+    HWComposer& hwc(hw.getHwComposer());
+    hwc_layer_t* const cur(hwc.getLayers());
+
+    LOGE_IF(cur && hwc.getNumLayers() != count,
+            "HAL number of layers (%d) doesn't match surfaceflinger (%d)",
+            hwc.getNumLayers(), count);
+
+    // just to be extra-safe, use the smallest count
+    count = count < hwc.getNumLayers() ? count : hwc.getNumLayers();
+
+    /*
+     *  update the per-frame h/w composer data for each layer
+     *  and build the transparent region of the FB
+     */
+    Region transparent;
+    if (cur) {
+        for (size_t i=0 ; i<count ; i++) {
+            const sp<LayerBase>& layer(layers[i]);
+            layer->setPerFrameData(&cur[i]);
+            if (cur[i].hints & HWC_HINT_CLEAR_FB) {
+                if (!(layer->needsBlending())) {
+                    transparent.orSelf(layer->visibleRegionScreen);
+                }
             }
         }
+        err = hwc.prepare();
+        LOGE_IF(err, "HWComposer::prepare failed (%s)", strerror(-err));
+    }
+
+    /*
+     *  clear the area of the FB that need to be transparent
+     */
+    transparent.andSelf(dirty);
+    if (!transparent.isEmpty()) {
+        glClearColor(0,0,0,0);
+        Region::const_iterator it = transparent.begin();
+        Region::const_iterator const end = transparent.end();
+        const int32_t height = hw.getHeight();
+        while (it != end) {
+            const Rect& r(*it++);
+            const GLint sy = height - (r.top + r.height());
+            glScissor(r.left, sy, r.width(), r.height());
+            glClear(GL_COLOR_BUFFER_BIT);
+        }
+    }
+
+
+    /*
+     * and then, render the layers targeted at the framebuffer
+     */
+    for (size_t i=0 ; i<count ; i++) {
+        if (cur) {
+            if (!(cur[i].compositionType == HWC_FRAMEBUFFER) ||
+                    cur[i].flags & HWC_SKIP_LAYER) {
+                // skip layers handled by the HAL
+                continue;
+            }
+        }
+        const sp<LayerBase>& layer(layers[i]);
+        const Region clip(dirty.intersect(layer->visibleRegionScreen));
+        if (!clip.isEmpty()) {
+            layer->draw(clip);
+        }
     }
 }
 
@@ -1029,8 +1041,7 @@
 
 status_t SurfaceFlinger::addLayer_l(const sp<LayerBase>& layer)
 {
-    ssize_t i = mCurrentState.layersSortedByZ.add(
-                layer, &LayerBase::compareCurrentStateZ);
+    ssize_t i = mCurrentState.layersSortedByZ.add(layer);
     return (i < 0) ? status_t(i) : status_t(NO_ERROR);
 }
 
@@ -1372,9 +1383,10 @@
                     flags |= eTraversalNeeded;
             }
             if (what & eLayerChanged) {
+                ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer);
                 if (layer->setLayer(s.z)) {
-                    mCurrentState.layersSortedByZ.reorder(
-                            layer, &Layer::compareCurrentStateZ);
+                    mCurrentState.layersSortedByZ.removeAt(idx);
+                    mCurrentState.layersSortedByZ.add(layer);
                     // we need traversal (state changed)
                     // AND transaction (list changed)
                     flags |= eTransactionNeeded|eTraversalNeeded;
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 8821e5c..8e286e5 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -40,9 +40,6 @@
 
 #include "MessageQueue.h"
 
-struct copybit_device_t;
-struct overlay_device_t;
-
 namespace android {
 
 // ---------------------------------------------------------------------------
@@ -246,21 +243,19 @@
     status_t setClientState(const sp<Client>& client,
             int32_t count, const layer_state_t* states);
 
-
-    class LayerVector {
+    class LayerVector : public SortedVector< sp<LayerBase> > {
     public:
-        inline              LayerVector() { }
-                            LayerVector(const LayerVector&);
-        inline size_t       size() const { return layers.size(); }
-        inline sp<LayerBase> const* array() const { return layers.array(); }
-        ssize_t             add(const sp<LayerBase>&, Vector< sp<LayerBase> >::compar_t);
-        ssize_t             remove(const sp<LayerBase>&);
-        ssize_t             reorder(const sp<LayerBase>&, Vector< sp<LayerBase> >::compar_t);
-        ssize_t             indexOf(const sp<LayerBase>& key, size_t guess=0) const;
-        inline sp<LayerBase> operator [] (size_t i) const { return layers[i]; }
-    private:
-        KeyedVector< sp<LayerBase> , size_t> lookup;
-        Vector< sp<LayerBase> >              layers;
+        LayerVector() { }
+        LayerVector(const LayerVector& rhs) : SortedVector< sp<LayerBase> >(rhs) { }
+        virtual int do_compare(const void* lhs, const void* rhs) const {
+            const sp<LayerBase>& l(*reinterpret_cast<const sp<LayerBase>*>(lhs));
+            const sp<LayerBase>& r(*reinterpret_cast<const sp<LayerBase>*>(rhs));
+            // sort layers by Z order
+            uint32_t lz = l->currentState().z;
+            uint32_t rz = r->currentState().z;
+            // then by sequence, so we get a stable ordering
+            return (lz != rz) ? (lz - rz) : (l->sequence - r->sequence);
+        }
     };
 
     struct State {
@@ -301,6 +296,7 @@
             void        handlePageFlip();
             bool        lockPageFlip(const LayerVector& currentLayers);
             void        unlockPageFlip(const LayerVector& currentLayers);
+            void        handleWorkList();
             void        handleRepaint();
             void        postFramebuffer();
             void        composeSurfaces(const Region& dirty);
@@ -375,10 +371,13 @@
                 Region                      mInvalidRegion;
                 Region                      mWormholeRegion;
                 bool                        mVisibleRegionsDirty;
+                bool                        mHwWorkListDirty;
                 bool                        mDeferReleaseConsole;
                 bool                        mFreezeDisplay;
                 int32_t                     mFreezeCount;
                 nsecs_t                     mFreezeDisplayTime;
+                Vector< sp<LayerBase> >     mVisibleLayersSortedByZ;
+
 
                 // don't use a lock for these, we don't care
                 int                         mDebugRegion;
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index 03194ff..f5c6909 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -1649,6 +1649,15 @@
     }
 
     /**
+     * @hide
+     * @param number
+     * @return true if number contains @
+     */
+    public static boolean isUriNumber(String number) {
+        return number != null && number.contains("@");
+    }
+
+    /**
      * This function handles the plus code conversion within NANP CDMA network
      * If the number format is
      * 1)+1NANP,remove +,
diff --git a/telephony/java/com/android/internal/telephony/CallManager.java b/telephony/java/com/android/internal/telephony/CallManager.java
index b57f358..819cfbe 100644
--- a/telephony/java/com/android/internal/telephony/CallManager.java
+++ b/telephony/java/com/android/internal/telephony/CallManager.java
@@ -21,12 +21,18 @@
 import android.os.Handler;
 import android.os.Message;
 import android.os.RegistrantList;
+import android.util.Log;
+
 import android.telephony.PhoneStateListener;
+import android.telephony.ServiceState;
+
 
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
+
+
 /**
  * @hide
  *
@@ -46,6 +52,9 @@
  */
 public final class CallManager {
 
+    private static final String LOG_TAG ="GSM";
+    private static final boolean LOCAL_DEBUG = true;
+
     private static final int EVENT_DISCONNECT = 100;
     private static final int EVENT_PRECISE_CALL_STATE_CHANGED = 101;
     private static final int EVENT_NEW_RINGING_CONNECTION = 102;
@@ -180,7 +189,7 @@
     public Phone.State getState() {
         Phone.State s = Phone.State.IDLE;
 
-        for(Phone phone : mPhones) {
+        for (Phone phone : mPhones) {
             if (phone.getState() == Phone.State.RINGING) {
                 return Phone.State.RINGING;
             } else if (phone.getState() == Phone.State.OFFHOOK) {
@@ -191,6 +200,41 @@
     }
 
     /**
+     * @return the service state of CallManager, which represents the
+     * highest priority state of all the service states of phones
+     *
+     * The priority is defined as
+     *
+     * STATE_IN_SERIVCE > STATE_OUT_OF_SERIVCE > STATE_EMERGENCY > STATE_POWER_OFF
+     *
+     */
+
+    public int getServiceState() {
+        int resultState = ServiceState.STATE_OUT_OF_SERVICE;
+
+        for (Phone phone : mPhones) {
+            int serviceState = phone.getServiceState().getState();
+            if (serviceState == ServiceState.STATE_IN_SERVICE) {
+                // IN_SERVICE has the highest priority
+                resultState = serviceState;
+                break;
+            } else if (serviceState == ServiceState.STATE_OUT_OF_SERVICE) {
+                // OUT_OF_SERVICE replaces EMERGENCY_ONLY and POWER_OFF
+                // Note: EMERGENCY_ONLY is not in use at this moment
+                if ( resultState == ServiceState.STATE_EMERGENCY_ONLY ||
+                        resultState == ServiceState.STATE_POWER_OFF) {
+                    resultState = serviceState;
+                }
+            } else if (serviceState == ServiceState.STATE_EMERGENCY_ONLY) {
+                if (resultState == ServiceState.STATE_POWER_OFF) {
+                    resultState = serviceState;
+                }
+            }
+        }
+        return resultState;
+    }
+
+    /**
      * Register phone to CallManager
      * @param phone
      * @return true if register successfully
@@ -211,11 +255,39 @@
     }
 
     /**
+     * return the default phone or null if no phone available
+     */
+    public Phone getDefaultPhone() {
+        return mDefaultPhone;
+    }
+
+    /**
+     * @return the phone associated with the foreground call
+     */
+    public Phone getFgPhone() {
+        return getActiveFgCall().getPhone();
+    }
+
+    /**
+     * @return the phone associated with the background call
+     */
+    public Phone getBgPhone() {
+        return getFirstActiveBgCall().getPhone();
+    }
+
+    /**
+     * @return the phone associated with the ringing call
+     */
+    public Phone getRingingPhone() {
+        return getFirstActiveRingingCall().getPhone();
+    }
+
+    /**
      * unregister phone from CallManager
      * @param phone
      */
     public void unregisterPhone(Phone phone) {
-        if (phone != null && !mPhones.contains(phone)) {
+        if (phone != null && mPhones.contains(phone)) {
             mPhones.remove(phone);
             mRingingCalls.remove(phone.getRingingCall());
             mBackgroundCalls.remove(phone.getBackgroundCall());
@@ -291,9 +363,13 @@
 
         if ( hasActiveFgCall() ) {
             Phone activePhone = getActiveFgCall().getPhone();
-            boolean hasBgCall = activePhone.getBackgroundCall().isIdle();
+            boolean hasBgCall = ! (activePhone.getBackgroundCall().isIdle());
             boolean sameChannel = (activePhone == ringingPhone);
 
+            if (LOCAL_DEBUG) {
+                Log.d(LOG_TAG, "hasBgCall: "+ hasBgCall + "sameChannel:" + sameChannel);
+            }
+
             if (sameChannel && hasBgCall) {
                 getActiveFgCall().hangup();
             } else if (!sameChannel && !hasBgCall) {
@@ -322,14 +398,21 @@
     }
 
     /**
-     * Places any active calls on hold, and makes any held calls
-     *  active. Switch occurs asynchronously and may fail.
+     * Places active call on hold, and makes held call active.
+     * Switch occurs asynchronously and may fail.
+     *
+     * There are 4 scenarios
+     * 1. only active call but no held call, aka, hold
+     * 2. no active call but only held call, aka, unhold
+     * 3. both active and held calls from same phone, aka, swap
+     * 4. active and held calls from different phones, aka, phone swap
+     *
      * Final notification occurs via
      * {@link #registerForPreciseCallStateChanged(android.os.Handler, int,
      * java.lang.Object) registerForPreciseCallStateChanged()}.
      *
      * @exception CallStateException if active call is ringing, waiting, or
-     * dialing/alerting, or heldCall can�t be active.
+     * dialing/alerting, or heldCall can't be active.
      * In these cases, this operation may not be performed.
      */
     public void switchHoldingAndActive(Call heldCall) throws CallStateException {
@@ -344,11 +427,13 @@
             heldPhone = heldCall.getPhone();
         }
 
-        if (activePhone != heldPhone) {
+        if (activePhone != null) {
             activePhone.switchHoldingAndActive();
         }
 
-        heldPhone.switchHoldingAndActive();
+        if (heldPhone != null && heldPhone != activePhone) {
+            heldPhone.switchHoldingAndActive();
+        }
     }
 
     /**
@@ -398,6 +483,24 @@
      * handled asynchronously.
      */
     public Connection dial(Phone phone, String dialString) throws CallStateException {
+        if ( hasActiveFgCall() ) {
+            Phone activePhone = getActiveFgCall().getPhone();
+            boolean hasBgCall = !(activePhone.getBackgroundCall().isIdle());
+
+            if (LOCAL_DEBUG) {
+                Log.d(LOG_TAG, "hasBgCall: "+ hasBgCall + "sameChannel:" + (activePhone != phone));
+            }
+
+            if (activePhone != phone) {
+                if (hasBgCall) {
+                    Log.d(LOG_TAG, "Hangup");
+                    getActiveFgCall().hangup();
+                } else {
+                    Log.d(LOG_TAG, "Switch");
+                    activePhone.switchHoldingAndActive();
+                }
+            }
+        }
         return phone.dial(dialString);
     }
 
@@ -975,7 +1078,7 @@
      * @return list of ringing calls
      */
     public ArrayList<Call> getRingingCalls() {
-        return mBackgroundCalls;
+        return mRingingCalls;
     }
 
     /**
diff --git a/telephony/java/com/android/internal/telephony/Phone.java b/telephony/java/com/android/internal/telephony/Phone.java
index 23325f6..33f7c9d 100644
--- a/telephony/java/com/android/internal/telephony/Phone.java
+++ b/telephony/java/com/android/internal/telephony/Phone.java
@@ -177,6 +177,7 @@
     static final int PHONE_TYPE_NONE = RILConstants.NO_PHONE;
     static final int PHONE_TYPE_GSM = RILConstants.GSM_PHONE;
     static final int PHONE_TYPE_CDMA = RILConstants.CDMA_PHONE;
+    static final int PHONE_TYPE_SIP = RILConstants.SIP_PHONE;
 
     // Used for preferred network type
     // Note NT_* substitute RILConstants.NETWORK_MODE_* above the Phone
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 71a80e0..888f721 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -79,6 +79,7 @@
     int NO_PHONE = 0;
     int GSM_PHONE = 1;
     int CDMA_PHONE = 2;
+    int SIP_PHONE  = 3;
 
     int CDM_TTY_MODE_DISABLED = 0;
     int CDM_TTY_MODE_ENABLED = 1;
diff --git a/telephony/java/com/android/internal/telephony/sip/SipPhone.java b/telephony/java/com/android/internal/telephony/sip/SipPhone.java
index edc1f8a..a94518f 100755
--- a/telephony/java/com/android/internal/telephony/sip/SipPhone.java
+++ b/telephony/java/com/android/internal/telephony/sip/SipPhone.java
@@ -102,6 +102,10 @@
         return mProfile.getProfileName();
     }
 
+    public String getSipUri() {
+        return mProfile.getUriString();
+    }
+
     public boolean canTake(Object incomingCall) {
         synchronized (SipPhone.class) {
             if (!(incomingCall instanceof SipAudioCall)) return false;
diff --git a/telephony/java/com/android/internal/telephony/sip/SipPhoneFactory.java b/telephony/java/com/android/internal/telephony/sip/SipPhoneFactory.java
index a1bdd2f..611e3ea 100644
--- a/telephony/java/com/android/internal/telephony/sip/SipPhoneFactory.java
+++ b/telephony/java/com/android/internal/telephony/sip/SipPhoneFactory.java
@@ -42,7 +42,7 @@
             SipProfile profile = new SipProfile.Builder(sipUri).build();
             return new SipPhone(context, phoneNotifier, profile);
         } catch (ParseException e) {
-            Log.w("SipPhoneProxy", "setPhone", e);
+            Log.w("SipPhoneFactory", "makePhone", e);
             return null;
         }
     }
diff --git a/telephony/java/com/android/internal/telephony/sip/SipPhoneProxy.java b/telephony/java/com/android/internal/telephony/sip/SipPhoneProxy.java
deleted file mode 100644
index 7cc1a9b..0000000
--- a/telephony/java/com/android/internal/telephony/sip/SipPhoneProxy.java
+++ /dev/null
@@ -1,749 +0,0 @@
-/*
- * Copyright (C) 2010 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.telephony.sip;
-
-import com.android.internal.telephony.*;
-import com.android.internal.telephony.gsm.NetworkInfo;
-import com.android.internal.telephony.test.SimulatedRadioControl;
-
-import android.content.Context;
-import android.os.Handler;
-import android.os.Message;
-import android.telephony.CellLocation;
-import android.telephony.ServiceState;
-import android.telephony.SignalStrength;
-import android.util.Log;
-
-import java.util.List;
-
-/**
- * Temporary. Will be removed after integrating with CallManager.
- * (TODO)
- * @hide
- */
-public class SipPhoneProxy implements Phone {
-    private static final String LOG_TAG = "PHONE";
-
-    private static SipPhoneProxy sPhoneProxy = new SipPhoneProxy();
-
-    public static SipPhoneProxy getInstance() {
-        return sPhoneProxy;
-    }
-
-    private SipPhone mActivePhone;
-    private CallProxy mRingingCall = new CallProxy();
-    private CallProxy mForegroundCall = new CallProxy();
-    private CallProxy mBackgroundCall = new CallProxy();
-
-    private SipPhoneProxy() {
-    }
-
-    public void onNewCall(Object call) {
-        if (mActivePhone.canTake(call)) {
-            Log.v("SipPhoneProxy", "onNewCall(): call taken: " + call);
-        } else {
-            Log.v("SipPhoneProxy", "onNewCall(): call dropped: " + call);
-        }
-    }
-
-    public synchronized void setPhone(SipPhone phone) {
-        if (phone == null) return;
-        if (mActivePhone != null) phone.migrateFrom(mActivePhone);
-        mActivePhone = phone;
-        mForegroundCall.setTarget(phone.getForegroundCall());
-        mBackgroundCall.setTarget(phone.getBackgroundCall());
-        mRingingCall.setTarget(phone.getRingingCall());
-    }
-
-    public synchronized Call getForegroundCall() {
-        return mForegroundCall;
-    }
-
-    public synchronized Call getBackgroundCall() {
-        return mBackgroundCall;
-    }
-
-    public synchronized Call getRingingCall() {
-        return mRingingCall;
-    }
-
-
-    public ServiceState getServiceState() {
-        return mActivePhone.getServiceState();
-    }
-
-    public CellLocation getCellLocation() {
-        return mActivePhone.getCellLocation();
-    }
-
-    public DataState getDataConnectionState() {
-        return mActivePhone.getDataConnectionState();
-    }
-
-    public DataActivityState getDataActivityState() {
-        return mActivePhone.getDataActivityState();
-    }
-
-    public Context getContext() {
-        return mActivePhone.getContext();
-    }
-
-    public void disableDnsCheck(boolean b) {
-        mActivePhone.disableDnsCheck(b);
-    }
-
-    public boolean isDnsCheckDisabled() {
-        return mActivePhone.isDnsCheckDisabled();
-    }
-
-    public State getState() {
-        return mActivePhone.getState();
-    }
-
-    public String getPhoneName() {
-        return mActivePhone.getPhoneName();
-    }
-
-    public int getPhoneType() {
-        return mActivePhone.getPhoneType();
-    }
-
-    public String[] getActiveApnTypes() {
-        return mActivePhone.getActiveApnTypes();
-    }
-
-    public String getActiveApn() {
-        return mActivePhone.getActiveApn();
-    }
-
-    public SignalStrength getSignalStrength() {
-        return mActivePhone.getSignalStrength();
-    }
-
-    public void registerForUnknownConnection(Handler h, int what, Object obj) {
-        mActivePhone.registerForUnknownConnection(h, what, obj);
-    }
-
-    public void unregisterForUnknownConnection(Handler h) {
-        mActivePhone.unregisterForUnknownConnection(h);
-    }
-
-    public void registerForPreciseCallStateChanged(Handler h, int what, Object obj) {
-        mActivePhone.registerForPreciseCallStateChanged(h, what, obj);
-    }
-
-    public void unregisterForPreciseCallStateChanged(Handler h) {
-        mActivePhone.unregisterForPreciseCallStateChanged(h);
-    }
-
-    public void registerForNewRingingConnection(Handler h, int what, Object obj) {
-        mActivePhone.registerForNewRingingConnection(h, what, obj);
-    }
-
-    public void unregisterForNewRingingConnection(Handler h) {
-        mActivePhone.unregisterForNewRingingConnection(h);
-    }
-
-    public void registerForIncomingRing(Handler h, int what, Object obj) {
-        mActivePhone.registerForIncomingRing(h, what, obj);
-    }
-
-    public void unregisterForIncomingRing(Handler h) {
-        mActivePhone.unregisterForIncomingRing(h);
-    }
-
-    public void registerForDisconnect(Handler h, int what, Object obj) {
-        mActivePhone.registerForDisconnect(h, what, obj);
-    }
-
-    public void unregisterForDisconnect(Handler h) {
-        mActivePhone.unregisterForDisconnect(h);
-    }
-
-    public void registerForMmiInitiate(Handler h, int what, Object obj) {
-        mActivePhone.registerForMmiInitiate(h, what, obj);
-    }
-
-    public void unregisterForMmiInitiate(Handler h) {
-        mActivePhone.unregisterForMmiInitiate(h);
-    }
-
-    public void registerForMmiComplete(Handler h, int what, Object obj) {
-        mActivePhone.registerForMmiComplete(h, what, obj);
-    }
-
-    public void unregisterForMmiComplete(Handler h) {
-        mActivePhone.unregisterForMmiComplete(h);
-    }
-
-    public List<? extends MmiCode> getPendingMmiCodes() {
-        return mActivePhone.getPendingMmiCodes();
-    }
-
-    public void sendUssdResponse(String ussdMessge) {
-        mActivePhone.sendUssdResponse(ussdMessge);
-    }
-
-    public void registerForServiceStateChanged(Handler h, int what, Object obj) {
-        mActivePhone.registerForServiceStateChanged(h, what, obj);
-    }
-
-    public void unregisterForServiceStateChanged(Handler h) {
-        mActivePhone.unregisterForServiceStateChanged(h);
-    }
-
-    public void registerForSuppServiceNotification(Handler h, int what, Object obj) {
-        mActivePhone.registerForSuppServiceNotification(h, what, obj);
-    }
-
-    public void unregisterForSuppServiceNotification(Handler h) {
-        mActivePhone.unregisterForSuppServiceNotification(h);
-    }
-
-    public void registerForSuppServiceFailed(Handler h, int what, Object obj) {
-        mActivePhone.registerForSuppServiceFailed(h, what, obj);
-    }
-
-    public void unregisterForSuppServiceFailed(Handler h) {
-        mActivePhone.unregisterForSuppServiceFailed(h);
-    }
-
-    public void registerForInCallVoicePrivacyOn(Handler h, int what, Object obj){
-        mActivePhone.registerForInCallVoicePrivacyOn(h,what,obj);
-    }
-
-    public void unregisterForInCallVoicePrivacyOn(Handler h){
-        mActivePhone.unregisterForInCallVoicePrivacyOn(h);
-    }
-
-    public void registerForInCallVoicePrivacyOff(Handler h, int what, Object obj){
-        mActivePhone.registerForInCallVoicePrivacyOff(h,what,obj);
-    }
-
-    public void unregisterForInCallVoicePrivacyOff(Handler h){
-        mActivePhone.unregisterForInCallVoicePrivacyOff(h);
-    }
-
-    public void registerForCdmaOtaStatusChange(Handler h, int what, Object obj) {
-        mActivePhone.registerForCdmaOtaStatusChange(h,what,obj);
-    }
-
-    public void unregisterForCdmaOtaStatusChange(Handler h) {
-         mActivePhone.unregisterForCdmaOtaStatusChange(h);
-    }
-
-    public void registerForSubscriptionInfoReady(Handler h, int what, Object obj) {
-        mActivePhone.registerForSubscriptionInfoReady(h, what, obj);
-    }
-
-    public void unregisterForSubscriptionInfoReady(Handler h) {
-        mActivePhone.unregisterForSubscriptionInfoReady(h);
-    }
-
-    public void registerForEcmTimerReset(Handler h, int what, Object obj) {
-        mActivePhone.registerForEcmTimerReset(h,what,obj);
-    }
-
-    public void unregisterForEcmTimerReset(Handler h) {
-        mActivePhone.unregisterForEcmTimerReset(h);
-    }
-
-    public void registerForRingbackTone(Handler h, int what, Object obj) {
-        mActivePhone.registerForRingbackTone(h,what,obj);
-    }
-
-    public void unregisterForRingbackTone(Handler h) {
-        mActivePhone.unregisterForRingbackTone(h);
-    }
-
-    public void registerForResendIncallMute(Handler h, int what, Object obj) {
-        mActivePhone.registerForResendIncallMute(h,what,obj);
-    }
-
-    public void unregisterForResendIncallMute(Handler h) {
-        mActivePhone.unregisterForResendIncallMute(h);
-    }
-
-    public boolean getIccRecordsLoaded() {
-        return mActivePhone.getIccRecordsLoaded();
-    }
-
-    public IccCard getIccCard() {
-        return mActivePhone.getIccCard();
-    }
-
-    public void acceptCall() throws CallStateException {
-        mActivePhone.acceptCall();
-    }
-
-    public void rejectCall() throws CallStateException {
-        mActivePhone.rejectCall();
-    }
-
-    public void switchHoldingAndActive() throws CallStateException {
-        mActivePhone.switchHoldingAndActive();
-    }
-
-    public boolean canConference() {
-        return mActivePhone.canConference();
-    }
-
-    public void conference() throws CallStateException {
-        mActivePhone.conference();
-    }
-
-    public void enableEnhancedVoicePrivacy(boolean enable, Message onComplete) {
-        mActivePhone.enableEnhancedVoicePrivacy(enable, onComplete);
-    }
-
-    public void getEnhancedVoicePrivacy(Message onComplete) {
-        mActivePhone.getEnhancedVoicePrivacy(onComplete);
-    }
-
-    public boolean canTransfer() {
-        return mActivePhone.canTransfer();
-    }
-
-    public void explicitCallTransfer() throws CallStateException {
-        mActivePhone.explicitCallTransfer();
-    }
-
-    public void clearDisconnected() {
-        mActivePhone.clearDisconnected();
-    }
-
-    public Connection dial(String dialString) throws CallStateException {
-        return mActivePhone.dial(dialString);
-    }
-
-    public Connection dial(String dialString, UUSInfo uusInfo) throws CallStateException {
-        return mActivePhone.dial(dialString);
-    }
-
-    public boolean handlePinMmi(String dialString) {
-        return mActivePhone.handlePinMmi(dialString);
-    }
-
-    public boolean handleInCallMmiCommands(String command) throws CallStateException {
-        return mActivePhone.handleInCallMmiCommands(command);
-    }
-
-    public void sendDtmf(char c) {
-        mActivePhone.sendDtmf(c);
-    }
-
-    public void startDtmf(char c) {
-        mActivePhone.startDtmf(c);
-    }
-
-    public void stopDtmf() {
-        mActivePhone.stopDtmf();
-    }
-
-    public void setRadioPower(boolean power) {
-        mActivePhone.setRadioPower(power);
-    }
-
-    public boolean getMessageWaitingIndicator() {
-        return mActivePhone.getMessageWaitingIndicator();
-    }
-
-    public boolean getCallForwardingIndicator() {
-        return mActivePhone.getCallForwardingIndicator();
-    }
-
-    public String getLine1Number() {
-        return mActivePhone.getLine1Number();
-    }
-
-    public String getCdmaMin() {
-        return mActivePhone.getCdmaMin();
-    }
-
-    public boolean isMinInfoReady() {
-        return mActivePhone.isMinInfoReady();
-    }
-
-    public String getCdmaPrlVersion() {
-        return mActivePhone.getCdmaPrlVersion();
-    }
-
-    public String getLine1AlphaTag() {
-        return mActivePhone.getLine1AlphaTag();
-    }
-
-    public void setLine1Number(String alphaTag, String number, Message onComplete) {
-        mActivePhone.setLine1Number(alphaTag, number, onComplete);
-    }
-
-    public String getVoiceMailNumber() {
-        return mActivePhone.getVoiceMailNumber();
-    }
-
-     /** @hide */
-    public int getVoiceMessageCount(){
-        return mActivePhone.getVoiceMessageCount();
-    }
-
-    public String getVoiceMailAlphaTag() {
-        return mActivePhone.getVoiceMailAlphaTag();
-    }
-
-    public void setVoiceMailNumber(String alphaTag,String voiceMailNumber,
-            Message onComplete) {
-        mActivePhone.setVoiceMailNumber(alphaTag, voiceMailNumber, onComplete);
-    }
-
-    public void getCallForwardingOption(int commandInterfaceCFReason,
-            Message onComplete) {
-        mActivePhone.getCallForwardingOption(commandInterfaceCFReason,
-                onComplete);
-    }
-
-    public void setCallForwardingOption(int commandInterfaceCFReason,
-            int commandInterfaceCFAction, String dialingNumber,
-            int timerSeconds, Message onComplete) {
-        mActivePhone.setCallForwardingOption(commandInterfaceCFReason,
-            commandInterfaceCFAction, dialingNumber, timerSeconds, onComplete);
-    }
-
-    public void getOutgoingCallerIdDisplay(Message onComplete) {
-        mActivePhone.getOutgoingCallerIdDisplay(onComplete);
-    }
-
-    public void setOutgoingCallerIdDisplay(int commandInterfaceCLIRMode,
-            Message onComplete) {
-        mActivePhone.setOutgoingCallerIdDisplay(commandInterfaceCLIRMode,
-                onComplete);
-    }
-
-    public void getCallWaiting(Message onComplete) {
-        mActivePhone.getCallWaiting(onComplete);
-    }
-
-    public void setCallWaiting(boolean enable, Message onComplete) {
-        mActivePhone.setCallWaiting(enable, onComplete);
-    }
-
-    public void getAvailableNetworks(Message response) {
-        mActivePhone.getAvailableNetworks(response);
-    }
-
-    public void setNetworkSelectionModeAutomatic(Message response) {
-        mActivePhone.setNetworkSelectionModeAutomatic(response);
-    }
-
-    public void selectNetworkManually(NetworkInfo network, Message response) {
-        mActivePhone.selectNetworkManually(network, response);
-    }
-
-    public void setPreferredNetworkType(int networkType, Message response) {
-        mActivePhone.setPreferredNetworkType(networkType, response);
-    }
-
-    public void getPreferredNetworkType(Message response) {
-        mActivePhone.getPreferredNetworkType(response);
-    }
-
-    public void getNeighboringCids(Message response) {
-        mActivePhone.getNeighboringCids(response);
-    }
-
-    public void setOnPostDialCharacter(Handler h, int what, Object obj) {
-        mActivePhone.setOnPostDialCharacter(h, what, obj);
-    }
-
-    public void setMute(boolean muted) {
-        mActivePhone.setMute(muted);
-    }
-
-    public boolean getMute() {
-        return mActivePhone.getMute();
-    }
-
-    public void invokeOemRilRequestRaw(byte[] data, Message response) {
-        mActivePhone.invokeOemRilRequestRaw(data, response);
-    }
-
-    public void invokeOemRilRequestStrings(String[] strings, Message response) {
-        mActivePhone.invokeOemRilRequestStrings(strings, response);
-    }
-
-    public void getDataCallList(Message response) {
-        mActivePhone.getDataCallList(response);
-    }
-
-    public List<DataConnection> getCurrentDataConnectionList() {
-        return mActivePhone.getCurrentDataConnectionList();
-    }
-
-    public void updateServiceLocation() {
-        mActivePhone.updateServiceLocation();
-    }
-
-    public void enableLocationUpdates() {
-        mActivePhone.enableLocationUpdates();
-    }
-
-    public void disableLocationUpdates() {
-        mActivePhone.disableLocationUpdates();
-    }
-
-    public void setUnitTestMode(boolean f) {
-        mActivePhone.setUnitTestMode(f);
-    }
-
-    public boolean getUnitTestMode() {
-        return mActivePhone.getUnitTestMode();
-    }
-
-    public void setBandMode(int bandMode, Message response) {
-        mActivePhone.setBandMode(bandMode, response);
-    }
-
-    public void queryAvailableBandMode(Message response) {
-        mActivePhone.queryAvailableBandMode(response);
-    }
-
-    public boolean getDataRoamingEnabled() {
-        return mActivePhone.getDataRoamingEnabled();
-    }
-
-    public void setDataRoamingEnabled(boolean enable) {
-        mActivePhone.setDataRoamingEnabled(enable);
-    }
-
-    public void queryCdmaRoamingPreference(Message response) {
-        mActivePhone.queryCdmaRoamingPreference(response);
-    }
-
-    public void setCdmaRoamingPreference(int cdmaRoamingType, Message response) {
-        mActivePhone.setCdmaRoamingPreference(cdmaRoamingType, response);
-    }
-
-    public void setCdmaSubscription(int cdmaSubscriptionType, Message response) {
-        mActivePhone.setCdmaSubscription(cdmaSubscriptionType, response);
-    }
-
-    public SimulatedRadioControl getSimulatedRadioControl() {
-        return mActivePhone.getSimulatedRadioControl();
-    }
-
-    public boolean enableDataConnectivity() {
-        return mActivePhone.enableDataConnectivity();
-    }
-
-    public boolean disableDataConnectivity() {
-        return mActivePhone.disableDataConnectivity();
-    }
-
-    public int enableApnType(String type) {
-        return mActivePhone.enableApnType(type);
-    }
-
-    public int disableApnType(String type) {
-        return mActivePhone.disableApnType(type);
-    }
-
-    public boolean isDataConnectivityEnabled() {
-        return mActivePhone.isDataConnectivityEnabled();
-    }
-
-    public boolean isDataConnectivityPossible() {
-        return mActivePhone.isDataConnectivityPossible();
-    }
-
-    public String getInterfaceName(String apnType) {
-        return mActivePhone.getInterfaceName(apnType);
-    }
-
-    public String getIpAddress(String apnType) {
-        return mActivePhone.getIpAddress(apnType);
-    }
-
-    public String getGateway(String apnType) {
-        return mActivePhone.getGateway(apnType);
-    }
-
-    public String[] getDnsServers(String apnType) {
-        return mActivePhone.getDnsServers(apnType);
-    }
-
-    public String getDeviceId() {
-        return mActivePhone.getDeviceId();
-    }
-
-    public String getDeviceSvn() {
-        return mActivePhone.getDeviceSvn();
-    }
-
-    public String getSubscriberId() {
-        return mActivePhone.getSubscriberId();
-    }
-
-    public String getIccSerialNumber() {
-        return mActivePhone.getIccSerialNumber();
-    }
-
-    public String getEsn() {
-        return mActivePhone.getEsn();
-    }
-
-    public String getMeid() {
-        return mActivePhone.getMeid();
-    }
-
-    public PhoneSubInfo getPhoneSubInfo(){
-        return mActivePhone.getPhoneSubInfo();
-    }
-
-    public IccSmsInterfaceManager getIccSmsInterfaceManager(){
-        return mActivePhone.getIccSmsInterfaceManager();
-    }
-
-    public IccPhoneBookInterfaceManager getIccPhoneBookInterfaceManager(){
-        return mActivePhone.getIccPhoneBookInterfaceManager();
-    }
-
-    public void setTTYMode(int ttyMode, Message onComplete) {
-        mActivePhone.setTTYMode(ttyMode, onComplete);
-    }
-
-    public void queryTTYMode(Message onComplete) {
-        mActivePhone.queryTTYMode(onComplete);
-    }
-
-    public void activateCellBroadcastSms(int activate, Message response) {
-        mActivePhone.activateCellBroadcastSms(activate, response);
-    }
-
-    public void getCellBroadcastSmsConfig(Message response) {
-        mActivePhone.getCellBroadcastSmsConfig(response);
-    }
-
-    public void setCellBroadcastSmsConfig(int[] configValuesArray, Message response) {
-        mActivePhone.setCellBroadcastSmsConfig(configValuesArray, response);
-    }
-
-    public void notifyDataActivity() {
-         mActivePhone.notifyDataActivity();
-    }
-
-    public void getSmscAddress(Message result) {
-        mActivePhone.getSmscAddress(result);
-    }
-
-    public void setSmscAddress(String address, Message result) {
-        mActivePhone.setSmscAddress(address, result);
-    }
-
-    public int getCdmaEriIconIndex() {
-         return mActivePhone.getCdmaEriIconIndex();
-    }
-
-     public String getCdmaEriText() {
-         return mActivePhone.getCdmaEriText();
-     }
-
-    public int getCdmaEriIconMode() {
-         return mActivePhone.getCdmaEriIconMode();
-    }
-
-    public void sendBurstDtmf(String dtmfString, int on, int off, Message onComplete){
-        mActivePhone.sendBurstDtmf(dtmfString, on, off, onComplete);
-    }
-
-    public void exitEmergencyCallbackMode(){
-        mActivePhone.exitEmergencyCallbackMode();
-    }
-
-    public boolean isOtaSpNumber(String dialStr){
-        return mActivePhone.isOtaSpNumber(dialStr);
-    }
-
-    public void registerForCallWaiting(Handler h, int what, Object obj){
-        mActivePhone.registerForCallWaiting(h,what,obj);
-    }
-
-    public void unregisterForCallWaiting(Handler h){
-        mActivePhone.unregisterForCallWaiting(h);
-    }
-
-    public void registerForSignalInfo(Handler h, int what, Object obj) {
-        mActivePhone.registerForSignalInfo(h,what,obj);
-    }
-
-    public void unregisterForSignalInfo(Handler h) {
-        mActivePhone.unregisterForSignalInfo(h);
-    }
-
-    public void registerForDisplayInfo(Handler h, int what, Object obj) {
-        mActivePhone.registerForDisplayInfo(h,what,obj);
-    }
-
-    public void unregisterForDisplayInfo(Handler h) {
-        mActivePhone.unregisterForDisplayInfo(h);
-    }
-
-    public void registerForNumberInfo(Handler h, int what, Object obj) {
-        mActivePhone.registerForNumberInfo(h, what, obj);
-    }
-
-    public void unregisterForNumberInfo(Handler h) {
-        mActivePhone.unregisterForNumberInfo(h);
-    }
-
-    public void registerForRedirectedNumberInfo(Handler h, int what, Object obj) {
-        mActivePhone.registerForRedirectedNumberInfo(h, what, obj);
-    }
-
-    public void unregisterForRedirectedNumberInfo(Handler h) {
-        mActivePhone.unregisterForRedirectedNumberInfo(h);
-    }
-
-    public void registerForLineControlInfo(Handler h, int what, Object obj) {
-        mActivePhone.registerForLineControlInfo( h, what, obj);
-    }
-
-    public void unregisterForLineControlInfo(Handler h) {
-        mActivePhone.unregisterForLineControlInfo(h);
-    }
-
-    public void registerFoT53ClirlInfo(Handler h, int what, Object obj) {
-        mActivePhone.registerFoT53ClirlInfo(h, what, obj);
-    }
-
-    public void unregisterForT53ClirInfo(Handler h) {
-        mActivePhone.unregisterForT53ClirInfo(h);
-    }
-
-    public void registerForT53AudioControlInfo(Handler h, int what, Object obj) {
-        mActivePhone.registerForT53AudioControlInfo( h, what, obj);
-    }
-
-    public void unregisterForT53AudioControlInfo(Handler h) {
-        mActivePhone.unregisterForT53AudioControlInfo(h);
-    }
-
-    public void setOnEcbModeExitResponse(Handler h, int what, Object obj){
-        mActivePhone.setOnEcbModeExitResponse(h,what,obj);
-    }
-
-    public void unsetOnEcbModeExitResponse(Handler h){
-        mActivePhone.unsetOnEcbModeExitResponse(h);
-    }
-}
diff --git a/tools/obbtool/Android.mk b/tools/obbtool/Android.mk
new file mode 100644
index 0000000..b02c1cb
--- /dev/null
+++ b/tools/obbtool/Android.mk
@@ -0,0 +1,30 @@
+#
+# Copyright 2010 The Android Open Source Project
+#
+# Opaque Binary Blob (OBB) Tool
+#
+
+# This tool is prebuilt if we're doing an app-only build.
+ifeq ($(TARGET_BUILD_APPS),)
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	Main.cpp
+
+#LOCAL_C_INCLUDES +=
+
+LOCAL_STATIC_LIBRARIES := \
+	libutils \
+	libcutils
+
+ifeq ($(HOST_OS),linux)
+LOCAL_LDLIBS += -lpthread
+endif
+
+LOCAL_MODULE := obbtool
+
+include $(BUILD_HOST_EXECUTABLE)
+
+endif # TARGET_BUILD_APPS
diff --git a/tools/obbtool/Main.cpp b/tools/obbtool/Main.cpp
new file mode 100644
index 0000000..2a9bf04
--- /dev/null
+++ b/tools/obbtool/Main.cpp
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <utils/ObbFile.h>
+#include <utils/String8.h>
+
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+using namespace android;
+
+static const char* gProgName = "obbtool";
+static const char* gProgVersion = "1.0";
+
+static int wantUsage = 0;
+static int wantVersion = 0;
+
+#define ADD_OPTS "n:v:f:c:"
+static const struct option longopts[] = {
+    {"help",       no_argument, &wantUsage,   1},
+    {"version",    no_argument, &wantVersion, 1},
+
+    /* Args for "add" */
+    {"name",       required_argument, NULL, 'n'},
+    {"version",    required_argument, NULL, 'v'},
+    {"filesystem", required_argument, NULL, 'f'},
+    {"crypto",     required_argument, NULL, 'c'},
+
+    {NULL, 0, NULL, '\0'}
+};
+
+struct package_info_t {
+    char* packageName;
+    int packageVersion;
+    char* filesystem;
+    char* crypto;
+};
+
+/*
+ * Print usage info.
+ */
+void usage(void)
+{
+    fprintf(stderr, "Opaque Binary Blob (OBB) Tool\n\n");
+    fprintf(stderr, "Usage:\n");
+    fprintf(stderr,
+        " %s a[dd] [ OPTIONS ] FILENAME\n"
+        "   Adds an OBB signature to the file.\n\n", gProgName);
+    fprintf(stderr,
+        " %s r[emove] FILENAME\n"
+        "   Removes the OBB signature from the file.\n\n", gProgName);
+    fprintf(stderr,
+        " %s i[nfo] FILENAME\n"
+        "   Prints the OBB signature information of a file.\n\n", gProgName);
+}
+
+void doAdd(const char* filename, struct package_info_t* info) {
+    ObbFile *obb = new ObbFile();
+    if (obb->readFrom(filename)) {
+        fprintf(stderr, "ERROR: %s: OBB signature already present\n", filename);
+        return;
+    }
+
+    obb->setPackageName(String8(info->packageName));
+    obb->setVersion(info->packageVersion);
+
+    if (!obb->writeTo(filename)) {
+        fprintf(stderr, "ERROR: %s: couldn't write OBB signature: %s\n",
+                filename, strerror(errno));
+        return;
+    }
+
+    fprintf(stderr, "OBB signature successfully written\n");
+}
+
+void doRemove(const char* filename) {
+    ObbFile *obb = new ObbFile();
+    if (!obb->readFrom(filename)) {
+        fprintf(stderr, "ERROR: %s: no OBB signature present\n", filename);
+        return;
+    }
+
+    if (!obb->removeFrom(filename)) {
+        fprintf(stderr, "ERROR: %s: couldn't remove OBB signature\n", filename);
+        return;
+    }
+
+    fprintf(stderr, "OBB signature successfully removed\n");
+}
+
+void doInfo(const char* filename) {
+    ObbFile *obb = new ObbFile();
+    if (!obb->readFrom(filename)) {
+        fprintf(stderr, "ERROR: %s: couldn't read OBB signature\n", filename);
+        return;
+    }
+
+    printf("OBB info for '%s':\n", filename);
+    printf("Package name: %s\n", obb->getPackageName().string());
+    printf("     Version: %d\n", obb->getVersion());
+}
+
+/*
+ * Parse args.
+ */
+int main(int argc, char* const argv[])
+{
+    const char *prog = argv[0];
+    struct options *options;
+    int opt;
+    int option_index = 0;
+    struct package_info_t package_info;
+
+    int result = 1;    // pessimistically assume an error.
+
+    if (argc < 2) {
+        wantUsage = 1;
+        goto bail;
+    }
+
+    while ((opt = getopt_long(argc, argv, ADD_OPTS, longopts, &option_index)) != -1) {
+        switch (opt) {
+        case 0:
+            if (longopts[option_index].flag)
+                break;
+            fprintf(stderr, "'%s' requires an argument\n", longopts[option_index].name);
+            wantUsage = 1;
+            goto bail;
+        case 'n':
+            package_info.packageName = optarg;
+            break;
+        case 'v':
+            char *end;
+            package_info.packageVersion = strtol(optarg, &end, 10);
+            if (*optarg == '\0' || *end != '\0') {
+                fprintf(stderr, "ERROR: invalid version; should be integer!\n\n");
+                wantUsage = 1;
+                goto bail;
+            }
+            break;
+        case 'f':
+            package_info.filesystem = optarg;
+            break;
+        case 'c':
+            package_info.crypto = optarg;
+            break;
+        case '?':
+            wantUsage = 1;
+            goto bail;
+        }
+    }
+
+    if (wantVersion) {
+        fprintf(stderr, "%s %s\n", gProgName, gProgVersion);
+    }
+
+    if (wantUsage) {
+        goto bail;
+    }
+
+#define CHECK_OP(name) \
+    if (strncmp(op, name, opsize)) { \
+        fprintf(stderr, "ERROR: unknown function '%s'!\n\n", op); \
+        wantUsage = 1; \
+        goto bail; \
+    }
+
+    if (optind < argc) {
+        const char* op = argv[optind++];
+        const int opsize = strlen(op);
+
+        if (optind >= argc) {
+            fprintf(stderr, "ERROR: filename required!\n\n");
+            wantUsage = 1;
+            goto bail;
+        }
+
+        const char* filename = argv[optind++];
+
+        switch (op[0]) {
+        case 'a':
+            CHECK_OP("add");
+            if (package_info.packageName == NULL) {
+                fprintf(stderr, "ERROR: arguments required 'packageName' and 'version'\n");
+                goto bail;
+            }
+            doAdd(filename, &package_info);
+            break;
+        case 'r':
+            CHECK_OP("remove");
+            doRemove(filename);
+            break;
+        case 'i':
+            CHECK_OP("info");
+            doInfo(filename);
+            break;
+        default:
+            fprintf(stderr, "ERROR: unknown command '%s'!\n\n", op);
+            wantUsage = 1;
+            goto bail;
+        }
+    }
+
+bail:
+    if (wantUsage) {
+        usage();
+        result = 2;
+    }
+
+    return result;
+}
diff --git a/voip/java/android/net/sip/SipAudioCallImpl.java b/voip/java/android/net/sip/SipAudioCallImpl.java
index 57e0bd2..474bc4b 100644
--- a/voip/java/android/net/sip/SipAudioCallImpl.java
+++ b/voip/java/android/net/sip/SipAudioCallImpl.java
@@ -343,8 +343,11 @@
     public synchronized void endCall() throws SipException {
         try {
             stopRinging();
-            if (mSipSession != null) mSipSession.endCall();
             stopCall(true);
+            mInCall = false;
+
+            // perform the above local ops first and then network op
+            if (mSipSession != null) mSipSession.endCall();
         } catch (Throwable e) {
             throwSipException(e);
         }