Merge "Add support for the PointerLocation overlay." into gingerbread
diff --git a/Android.mk b/Android.mk
index b6f2504..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/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/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/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/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/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/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/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/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/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/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/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/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/telephony/java/com/android/internal/telephony/CallManager.java b/telephony/java/com/android/internal/telephony/CallManager.java
index b57f358..4dc1991 100644
--- a/telephony/java/com/android/internal/telephony/CallManager.java
+++ b/telephony/java/com/android/internal/telephony/CallManager.java
@@ -215,7 +215,7 @@
      * @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());
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);
         }