Merge changes I4ad02ee1,I1bafc79b,I2c4df95e

* changes:
  Remove deprecated WebRuntime code
  Fix DumpRenderTree2 to clear AppCache between tests
  Fix DumpRenderTree2 to wait for the WebCore thread to become ready
diff --git a/api/current.xml b/api/current.xml
index 51315ab..07aabed 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -46584,6 +46584,21 @@
 <parameter name="cursor" type="android.database.Cursor">
 </parameter>
 </method>
+<method name="registerContentObserver"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="cursor" type="android.database.Cursor">
+</parameter>
+<parameter name="observer" type="android.database.ContentObserver">
+</parameter>
+</method>
 <method name="setProjection"
  return="void"
  abstract="false"
@@ -72506,6 +72521,146 @@
 >
 </field>
 </class>
+<class name="BitmapRegionDecoder"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="decodeRegion"
+ return="android.graphics.Bitmap"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="rect" type="android.graphics.Rect">
+</parameter>
+<parameter name="options" type="android.graphics.BitmapFactory.Options">
+</parameter>
+</method>
+<method name="getHeight"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getWidth"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isRecycled"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="newInstance"
+ return="android.graphics.BitmapRegionDecoder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="data" type="byte[]">
+</parameter>
+<parameter name="offset" type="int">
+</parameter>
+<parameter name="length" type="int">
+</parameter>
+<parameter name="isShareable" type="boolean">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="newInstance"
+ return="android.graphics.BitmapRegionDecoder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fd" type="java.io.FileDescriptor">
+</parameter>
+<parameter name="isShareable" type="boolean">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="newInstance"
+ return="android.graphics.BitmapRegionDecoder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="is" type="java.io.InputStream">
+</parameter>
+<parameter name="isShareable" type="boolean">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="newInstance"
+ return="android.graphics.BitmapRegionDecoder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="pathName" type="java.lang.String">
+</parameter>
+<parameter name="isShareable" type="boolean">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="recycle"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+</class>
 <class name="BitmapShader"
  extends="android.graphics.Shader"
  abstract="false"
@@ -89655,6 +89810,19 @@
  visibility="public"
 >
 </method>
+<method name="onCurrentInputMethodSubtypeChanged"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+<parameter name="newSubtype" type="android.view.inputmethod.InputMethodSubtype">
+</parameter>
+</method>
 <method name="onDisplayCompletions"
  return="void"
  abstract="false"
@@ -90258,6 +90426,19 @@
 <parameter name="binding" type="android.view.inputmethod.InputBinding">
 </parameter>
 </method>
+<method name="changeInputMethodSubtype"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="subtype" type="android.view.inputmethod.InputMethodSubtype">
+</parameter>
+</method>
 <method name="hideSoftInput"
  return="void"
  abstract="false"
@@ -150134,6 +150315,28 @@
  visibility="public"
 >
 </field>
+<field name="PHOTO_THUMBNAIL_URI"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;photo_thumb_uri&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PHOTO_URI"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;photo_uri&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 </interface>
 <class name="ContactsContract.Data"
  extends="java.lang.Object"
@@ -150665,6 +150868,61 @@
  visibility="public"
 >
 </field>
+<field name="PHOTO_SUPPORT"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;photoSupport&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PHOTO_SUPPORT_FULL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PHOTO_SUPPORT_FULL_SIZE_ONLY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PHOTO_SUPPORT_NONE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PHOTO_SUPPORT_THUMBNAIL_ONLY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="SHORTCUT_SUPPORT"
  type="java.lang.String"
  transient="false"
@@ -156547,6 +156805,17 @@
  visibility="public"
 >
 </field>
+<field name="SELECTED_INPUT_METHOD_SUBTYPE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;selected_input_method_subtype&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="SETTINGS_CLASSNAME"
  type="java.lang.String"
  transient="false"
@@ -157268,6 +157537,17 @@
  visibility="public"
 >
 </field>
+<field name="AUTO_TIME_ZONE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;auto_time_zone&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="BLUETOOTH_DISCOVERABILITY"
  type="java.lang.String"
  transient="false"
@@ -206715,17 +206995,6 @@
  visibility="public"
 >
 </field>
-<field name="FEATURE_HARDWARE_ACCELERATED"
- type="int"
- transient="false"
- volatile="false"
- value="11"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="FEATURE_INDETERMINATE_PROGRESS"
  type="int"
  transient="false"
@@ -213522,6 +213791,19 @@
 <parameter name="binding" type="android.view.inputmethod.InputBinding">
 </parameter>
 </method>
+<method name="changeInputMethodSubtype"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="subtype" type="android.view.inputmethod.InputMethodSubtype">
+</parameter>
+</method>
 <method name="createSession"
  return="void"
  abstract="true"
@@ -213846,7 +214128,7 @@
 >
 </method>
 <method name="getSubtypes"
- return="java.util.ArrayList&lt;android.view.inputmethod.InputMethodInfo.InputMethodSubtype&gt;"
+ return="java.util.ArrayList&lt;android.view.inputmethod.InputMethodSubtype&gt;"
  abstract="false"
  native="false"
  synchronized="false"
@@ -213908,108 +214190,6 @@
 >
 </field>
 </class>
-<class name="InputMethodInfo.InputMethodSubtype"
- extends="java.lang.Object"
- abstract="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<implements name="android.os.Parcelable">
-</implements>
-<method name="describeContents"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getExtraValue"
- return="java.lang.String"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getIconId"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getLocale"
- return="java.lang.String"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getMode"
- return="java.lang.String"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getName"
- return="java.lang.String"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="writeToParcel"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="dest" type="android.os.Parcel">
-</parameter>
-<parameter name="parcelableFlags" type="int">
-</parameter>
-</method>
-<field name="CREATOR"
- type="android.os.Parcelable.Creator"
- transient="false"
- volatile="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
 <class name="InputMethodManager"
  extends="java.lang.Object"
  abstract="false"
@@ -214230,6 +214410,17 @@
  visibility="public"
 >
 </method>
+<method name="showInputMethodSubtypePicker"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="showSoftInput"
  return="boolean"
  abstract="false"
@@ -214644,6 +214835,108 @@
 </parameter>
 </method>
 </interface>
+<class name="InputMethodSubtype"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.os.Parcelable">
+</implements>
+<method name="describeContents"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getExtraValue"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getIconResId"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getLocale"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getModeResId"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getNameResId"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="writeToParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="dest" type="android.os.Parcel">
+</parameter>
+<parameter name="parcelableFlags" type="int">
+</parameter>
+</method>
+<field name="CREATOR"
+ type="android.os.Parcelable.Creator"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
 </package>
 <package name="android.webkit"
 >
diff --git a/core/java/android/content/CursorLoader.java b/core/java/android/content/CursorLoader.java
index 42599ed..7776874 100644
--- a/core/java/android/content/CursorLoader.java
+++ b/core/java/android/content/CursorLoader.java
@@ -16,6 +16,7 @@
 
 package android.content;
 
+import android.database.ContentObserver;
 import android.database.Cursor;
 import android.net.Uri;
 
@@ -37,14 +38,22 @@
     public Cursor loadInBackground() {
         Cursor cursor = getContext().getContentResolver().query(mUri, mProjection, mSelection,
                 mSelectionArgs, mSortOrder);
-        // Ensure the cursor window is filled
         if (cursor != null) {
+            // Ensure the cursor window is filled
             cursor.getCount();
-            cursor.registerContentObserver(mObserver);
+            registerContentObserver(cursor, mObserver);
         }
         return cursor;
     }
 
+    /**
+     * Registers an observer to get notifications from the content provider
+     * when the cursor needs to be refreshed.
+     */
+    public void registerContentObserver(Cursor cursor, ContentObserver observer) {
+        cursor.registerContentObserver(mObserver);
+    }
+
     /* Runs on the UI thread */
     @Override
     public void deliverResult(Cursor cursor) {
diff --git a/core/java/android/inputmethodservice/AbstractInputMethodService.java b/core/java/android/inputmethodservice/AbstractInputMethodService.java
index 3619653..27af013 100644
--- a/core/java/android/inputmethodservice/AbstractInputMethodService.java
+++ b/core/java/android/inputmethodservice/AbstractInputMethodService.java
@@ -177,6 +177,7 @@
      * Implement this to handle {@link android.os.Binder#dump Binder.dump()}
      * calls on your input method.
      */
+    @Override
     protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
     }
 
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index 35fd46f..24ea7d2 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -36,6 +36,7 @@
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputMethod;
 import android.view.inputmethod.InputMethodSession;
+import android.view.inputmethod.InputMethodSubtype;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -64,6 +65,7 @@
     private static final int DO_REVOKE_SESSION = 50;
     private static final int DO_SHOW_SOFT_INPUT = 60;
     private static final int DO_HIDE_SOFT_INPUT = 70;
+    private static final int DO_CHANGE_INPUTMETHOD_SUBTYPE = 80;
    
     final WeakReference<AbstractInputMethodService> mTarget;
     final HandlerCaller mCaller;
@@ -178,6 +180,9 @@
             case DO_HIDE_SOFT_INPUT:
                 inputMethod.hideSoftInput(msg.arg1, (ResultReceiver)msg.obj);
                 return;
+            case DO_CHANGE_INPUTMETHOD_SUBTYPE:
+                inputMethod.changeInputMethodSubtype((InputMethodSubtype)msg.obj);
+                return;
         }
         Log.w(TAG, "Unhandled message code: " + msg.what);
     }
@@ -267,4 +272,9 @@
         mCaller.executeOrSendMessage(mCaller.obtainMessageIO(DO_HIDE_SOFT_INPUT,
                 flags, resultReceiver));
     }
+
+    public void changeInputMethodSubtype(InputMethodSubtype subtype) {
+        mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_CHANGE_INPUTMETHOD_SUBTYPE,
+                subtype));
+    }
 }
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 1a261d3..6089013 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -52,6 +52,7 @@
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputMethod;
 import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.InputMethodSubtype;
 import android.view.inputmethod.EditorInfo;
 import android.widget.Button;
 import android.widget.FrameLayout;
@@ -270,7 +271,7 @@
 
     final Insets mTmpInsets = new Insets();
     final int[] mTmpLocation = new int[2];
-    
+
     final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer =
             new ViewTreeObserver.OnComputeInternalInsetsListener() {
         public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
@@ -394,8 +395,12 @@
                                 : InputMethodManager.RESULT_UNCHANGED_HIDDEN), null);
             }
         }
+
+        public void changeInputMethodSubtype(InputMethodSubtype subtype) {
+            onCurrentInputMethodSubtypeChanged(subtype);
+        }
     }
-    
+
     /**
      * Concrete implementation of
      * {@link AbstractInputMethodService.AbstractInputMethodSessionImpl} that provides
@@ -541,6 +546,7 @@
      * will typically call it in your constructor with the resource ID
      * of your custom theme.
      */
+    @Override
     public void setTheme(int theme) {
         if (mWindow != null) {
             throw new IllegalStateException("Must be called before onCreate()");
@@ -558,7 +564,7 @@
         initViews();
         mWindow.getWindow().setLayout(MATCH_PARENT, WRAP_CONTENT);
     }
-    
+
     /**
      * This is a hook that subclasses can use to perform initialization of
      * their interface.  It is called for you prior to any of your UI objects
@@ -567,14 +573,14 @@
      */
     public void onInitializeInterface() {
     }
-    
+
     void initialize() {
         if (!mInitialized) {
             mInitialized = true;
             onInitializeInterface();
         }
     }
-    
+
     void initViews() {
         mInitialized = false;
         mWindowCreated = false;
@@ -610,7 +616,7 @@
         mCandidatesFrame.setVisibility(mCandidatesVisibility);
         mInputFrame.setVisibility(View.GONE);
     }
-    
+
     @Override public void onDestroy() {
         super.onDestroy();
         mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener(
@@ -677,6 +683,7 @@
      * Implement to return our standard {@link InputMethodImpl}.  Subclasses
      * can override to provide their own customized version.
      */
+    @Override
     public AbstractInputMethodImpl onCreateInputMethodInterface() {
         return new InputMethodImpl();
     }
@@ -685,6 +692,7 @@
      * Implement to return our standard {@link InputMethodSessionImpl}.  Subclasses
      * can override to provide their own customized version.
      */
+    @Override
     public AbstractInputMethodSessionImpl onCreateInputMethodSessionInterface() {
         return new InputMethodSessionImpl();
     }
@@ -1656,6 +1664,7 @@
         return doMovementKey(keyCode, event, MOVEMENT_UP);
     }
 
+    @Override
     public boolean onTrackballEvent(MotionEvent event) {
         return false;
     }
@@ -1694,7 +1703,7 @@
                 dy = count;
                 break;
         }
-        onExtractedCursorMovement(dx, dy);       
+        onExtractedCursorMovement(dx, dy);
     }
     
     boolean doMovementKey(int keyCode, KeyEvent event, int count) {
@@ -2064,7 +2073,24 @@
             }
         }
     }
-    
+
+    // TODO: Handle the subtype change event
+    /**
+     * Called when the subtype was changed.
+     * @param newSubtype the subtype which is being changed to.
+     */
+    protected void onCurrentInputMethodSubtypeChanged(InputMethodSubtype newSubtype) {
+        if (DEBUG) {
+            int nameResId = newSubtype.getNameResId();
+            int modeResId = newSubtype.getModeResId();
+            String output = "changeInputMethodSubtype:"
+                + (nameResId == 0 ? "<none>" : getString(nameResId)) + ","
+                + (modeResId == 0 ? "<none>" : getString(modeResId)) + ","
+                + newSubtype.getLocale() + "," + newSubtype.getExtraValue();
+            Log.v(TAG, "--- " + output);
+        }
+    }
+
     /**
      * Performs a dump of the InputMethodService's internal state.  Override
      * to add your own information to the dump.
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 3876a3e..156da47 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -232,6 +232,8 @@
         /**
          * Sets whether this WakeLock is ref counted.
          *
+         * <p>Wake locks are reference counted by default.
+         *
          * @param value true for ref counted, false for not ref counted.
          */
         public void setReferenceCounted(boolean value)
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index f571c42..3ddaad9 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -30,8 +30,59 @@
 import java.util.HashMap;
 
 /**
- * <p>StrictMode lets you impose stricter rules under which your
- * application runs.</p>
+ * <p>StrictMode is a developer tool which lets you impose stricter
+ * rules under which your application runs.
+ *
+ * <p>StrictMode is most commonly used to catch accidental disk or
+ * network access on the application's main thread, where UI
+ * operations are received and animations take place.  Keeping disk
+ * and network operations off the main thread makes for much smoother,
+ * more responsive applications.
+ *
+ * <p class="note">Note that even though an Android device's disk is
+ * often on flash memory, many devices run a filesystem on top of that
+ * memory with very limited concurrency.  It's often the case that
+ * almost all disk accesses are fast, but may in individual cases be
+ * dramatically slower when certain I/O is happening in the background
+ * from other processes.  If possible, it's best to assume that such
+ * things are not fast.</p>
+ *
+ * <p>Example code to enable from early in your
+ * {@link android.app.Application}, {@link android.app.Activity}, or
+ * other application component's
+ * {@link android.app.Application#onCreate} method:
+ *
+ * <pre>
+ * public void onCreate() {
+ *     if (DEVELOPER_MODE) {
+ *         StrictMode.setThreadPolicy(StrictMode.DISALLOW_DISK_WRITE |
+ *                 StrictMode.DISALLOW_DISK_READ |
+ *                 StrictMode.DISALLOW_NETWORK |
+ *                 StrictMode.PENALTY_LOG);
+ *     }
+ *     super.onCreate();
+ * }
+ * </pre>
+ *
+ * <p>Then you can watch the output of <code>adb logcat</code> while you
+ * use your application.
+ *
+ * <p>If you find violations that you feel are problematic, there are
+ * a variety of tools to help solve them: threads, {@link android.os.Handler},
+ * {@link android.os.AsyncTask}, {@link android.app.IntentService}, etc.
+ * But don't feel compelled to fix everything that StrictMode finds.  In particular,
+ * a lot of disk accesses are often necessary during the normal activity lifecycle.  Use
+ * StrictMode to find things you did on accident.  Network requests on the UI thread
+ * are almost always a problem, though.
+ *
+ * <p class="note">StrictMode is not a security mechanism and is not
+ * guaranteed to find all disk or network accesses.  While it does
+ * propagate its state across process boundaries when doing
+ * {@link android.os.Binder} calls, it's still ultimately a best
+ * effort mechanism.  Notably, disk or network access from JNI calls
+ * won't necessarily trigger it.  Future versions of Android may catch
+ * more (or fewer) operations, so you should never leave StrictMode
+ * enabled in shipping applications on the Android Market.
  */
 public final class StrictMode {
     private static final String TAG = "StrictMode";
@@ -45,8 +96,22 @@
 
     private StrictMode() {}
 
+    /**
+     * Flag for {@link #setThreadPolicy} to signal that you don't intend for this
+     * thread to write to disk.
+     */
     public static final int DISALLOW_DISK_WRITE = 0x01;
+
+    /**
+     * Flag for {@link #setThreadPolicy} to signal that you don't intend for this
+     * thread to read from disk.
+     */
     public static final int DISALLOW_DISK_READ = 0x02;
+
+    /**
+     * Flag for {@link #setThreadPolicy} to signal that you don't intend for this
+     * thread to access the network.
+     */
     public static final int DISALLOW_NETWORK = 0x04;
 
     /** @hide */
@@ -54,23 +119,30 @@
             DISALLOW_DISK_WRITE | DISALLOW_DISK_READ | DISALLOW_NETWORK;
 
     /**
-     * Flag to log to the system log.
+     * Penalty flag for {@link #setThreadPolicy} to log violations to
+     * the system log, visible with <code>adb logcat</code>.
      */
     public static final int PENALTY_LOG = 0x10;  // normal android.util.Log
 
     /**
-     * Show an annoying dialog to the user.  Will be rate-limited to be only
-     * a little annoying.
+     * Penalty flag for {@link #setThreadPolicy} to show an annoying
+     * dialog to the developer, rate-limited to be only a little
+     * annoying.
      */
     public static final int PENALTY_DIALOG = 0x20;
 
     /**
-     * Crash hard if policy is violated.
+     * Penalty flag for {@link #setThreadPolicy} to crash hard if
+     * policy is violated.
      */
     public static final int PENALTY_DEATH = 0x40;
 
     /**
-     * Log a stacktrace to the DropBox on policy violation.
+     * Penalty flag for {@link #setThreadPolicy} to log a stacktrace
+     * and timing data to the
+     * {@link android.os.DropBoxManager DropBox} on policy violation.
+     * Intended mostly for platform integrators doing beta user field
+     * data collection.
      */
     public static final int PENALTY_DROPBOX = 0x80;
 
@@ -109,10 +181,17 @@
     };
 
     /**
-     * Sets the policy for what actions the current thread is denied,
-     * as well as the penalty for violating the policy.
+     * Sets the policy for what actions the current thread isn't
+     * expected to do, as well as the penalty if it does.
      *
-     * @param policyMask a bitmask of DISALLOW_* and PENALTY_* values.
+     * <p>Internally this sets a thread-local integer which is
+     * propagated across cross-process IPC calls, meaning you can
+     * catch violations when a system service or another process
+     * accesses the disk or network on your behalf.
+     *
+     * @param policyMask a bitmask of DISALLOW_* and PENALTY_* values,
+     *     e.g. {@link #DISALLOW_DISK_READ}, {@link #DISALLOW_DISK_WRITE},
+     *     {@link #DISALLOW_NETWORK}, {@link #PENALTY_LOG}.
      */
     public static void setThreadPolicy(final int policyMask) {
         // In addition to the Java-level thread-local in Dalvik's
@@ -160,7 +239,7 @@
     }
 
     /**
-     * Returns the bitmask of the current thread's blocking policy.
+     * Returns the bitmask of the current thread's policy.
      *
      * @return the bitmask of all the DISALLOW_* and PENALTY_* bits currently enabled
      */
@@ -169,8 +248,10 @@
     }
 
     /**
-     * Updates the current thread's policy mask to allow reading &amp;
-     * writing to disk.
+     * A convenience wrapper around {@link #getThreadPolicy} and
+     * {@link #setThreadPolicy}.  Updates the current thread's policy
+     * mask to allow both reading &amp; writing to disk, returning the
+     * old policy so you can restore it at the end of a block.
      *
      * @return the old policy mask, to be passed to setThreadPolicy to
      *         restore the policy.
@@ -185,8 +266,10 @@
     }
 
     /**
-     * Updates the current thread's policy mask to allow reading from
-     * disk.
+     * A convenience wrapper around {@link #getThreadPolicy} and
+     * {@link #setThreadPolicy}.  Updates the current thread's policy
+     * mask to allow reading from disk, returning the old
+     * policy so you can restore it at the end of a block.
      *
      * @return the old policy mask, to be passed to setThreadPolicy to
      *         restore the policy.
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index b2c1c2d..36e2c56 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -445,7 +445,7 @@
 
         /**
          * One of {@link #SHORTCUT_SUPPORT_NONE}, {@link #SHORTCUT_SUPPORT_DATA_ITEMS_ONLY},
-         * {@link #SHORTCUT_SUPPORT_FULL}, This is the expectation the directory
+         * {@link #SHORTCUT_SUPPORT_FULL}. This is the expectation the directory
          * has for shortcuts created for its elements. Clients must obey this setting.
          */
         public static final String SHORTCUT_SUPPORT = "shortcutSupport";
@@ -470,6 +470,37 @@
         public static final int SHORTCUT_SUPPORT_FULL = 2;
 
         /**
+         * One of {@link #PHOTO_SUPPORT_NONE}, {@link #PHOTO_SUPPORT_THUMBNAIL_ONLY},
+         * {@link #PHOTO_SUPPORT_FULL}. This is a feature flag indicating the extent
+         * to which the directory supports contact photos.
+         */
+        public static final String PHOTO_SUPPORT = "photoSupport";
+
+        /**
+         * An {@link #PHOTO_SUPPORT} setting that indicates that the directory
+         * does not provide any photos.
+         */
+        public static final int PHOTO_SUPPORT_NONE = 0;
+
+        /**
+         * An {@link #PHOTO_SUPPORT} setting that indicates that the directory
+         * can only produce small size thumbnails of contact photos.
+         */
+        public static final int PHOTO_SUPPORT_THUMBNAIL_ONLY = 1;
+
+        /**
+         * An {@link #PHOTO_SUPPORT} setting that indicates that the directory
+         * has full-size contact photos, but cannot provide scaled thumbnails.
+         */
+        public static final int PHOTO_SUPPORT_FULL_SIZE_ONLY = 2;
+
+        /**
+         * An {@link #PHOTO_SUPPORT} setting that indicates that the directory
+         * can produce thumbnails as well as full-size contact photos.
+         */
+        public static final int PHOTO_SUPPORT_FULL = 3;
+
+        /**
          * Notifies the system of a change in the list of directories handled by
          * a particular directory provider. The Contacts provider will turn around
          * and send a query to the directory provider for the full list of directories,
@@ -676,12 +707,42 @@
         public static final String NAME_RAW_CONTACT_ID = "name_raw_contact_id";
 
         /**
-         * Reference to the row in the data table holding the photo.
+         * Reference to the row in the data table holding the photo.  A photo can
+         * be referred to either by ID (this field) or by URI (see {@link #PHOTO_THUMBNAIL_URI}
+         * and {@link #PHOTO_URI}).
+         * If PHOTO_ID is null, consult {@link #PHOTO_URI} or {@link #PHOTO_THUMBNAIL_URI},
+         * which is a more generic mechanism for referencing the contact photo, especially for
+         * contacts returned by non-local directories (see {@link Directory}).
+         *
          * <P>Type: INTEGER REFERENCES data(_id)</P>
          */
         public static final String PHOTO_ID = "photo_id";
 
         /**
+         * A URI that can be used to retrieve the contact's full-size photo.
+         * A photo can be referred to either by a URI (this field) or by ID
+         * (see {@link #PHOTO_ID}). If PHOTO_ID is not null, PHOTO_URI and
+         * PHOTO_THUMBNAIL_URI shall not be null (but not necessarily vice versa).
+         * Thus using PHOTO_URI is a more robust method of retrieving contact photos.
+         *
+         * <P>Type: TEXT</P>
+         */
+        public static final String PHOTO_URI = "photo_uri";
+
+        /**
+         * A URI that can be used to retrieve a thumbnail of the contact's photo.
+         * A photo can be referred to either by a URI (this field or {@link #PHOTO_URI})
+         * or by ID (see {@link #PHOTO_ID}). If PHOTO_ID is not null, PHOTO_URI and
+         * PHOTO_THUMBNAIL_URI shall not be null (but not necessarily vice versa).
+         * If the content provider does not differentiate between full-size photos
+         * and thumbnail photos, PHOTO_THUMBNAIL_URI and {@link #PHOTO_URI} can contain
+         * the same value, but either both shell be null or both not null.
+         *
+         * <P>Type: TEXT</P>
+         */
+        public static final String PHOTO_THUMBNAIL_URI = "photo_thumb_uri";
+
+        /**
          * Lookup value that reflects the {@link Groups#GROUP_VISIBLE} state of
          * any {@link CommonDataKinds.GroupMembership} for this contact.
          */
@@ -1032,6 +1093,20 @@
      * that mime type.</td>
      * </tr>
      * <tr>
+     * <td>long</td>
+     * <td>{@link #PHOTO_URI}</td>
+     * <td>read-only</td>
+     * <td>A URI that can be used to retrieve the contact's full-size photo. This
+     * column is the preferred method of retrieving the contact photo.</td>
+     * </tr>
+     * <tr>
+     * <td>long</td>
+     * <td>{@link #PHOTO_THUMBNAIL_URI}</td>
+     * <td>read-only</td>
+     * <td>A URI that can be used to retrieve the thumbnail of contact's photo.  This
+     * column is the preferred method of retrieving the contact photo.</td>
+     * </tr>
+     * <tr>
      * <td>int</td>
      * <td>{@link #IN_VISIBLE_GROUP}</td>
      * <td>read-only</td>
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 1417ef5..da02845 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -255,13 +255,6 @@
         public static final String MIME_TYPE = "mime_type";
 
         /**
-         * The row ID in the MTP object table corresponding to this media file.
-         * <P>Type: INTEGER</P>
-         * @hide
-         */
-        public static final String MTP_OBJECT_ID = "object_id";
-
-        /**
          * The MTP object handle of a newly transfered file.
          * Used to pass the new file's object handle through the media scanner
          * from MTP to the media provider
@@ -327,30 +320,33 @@
             public static final String PARENT = "parent";
 
             /**
-             * Identifier for the media table containing the file.
-             * Used internally by MediaProvider
-             * <P>Type: INTEGER</P>
+             * The MIME type of the file
+             * <P>Type: TEXT</P>
              */
-            public static final String MEDIA_TABLE = "media_table";
+            public static final String MIME_TYPE = "mime_type";
 
             /**
-             * The ID of the file in its media table.
-             * <P>Type: INTEGER</P>
+             * The title of the content
+             * <P>Type: TEXT</P>
              */
-            public static final String MEDIA_ID = "media_id";
+            public static final String TITLE = "title";
+
+            /**
+             * The media type (audio, video, image or playlist)
+             * of the file, or 0 for not a media file
+             * <P>Type: TEXT</P>
+             */
+            public static final String MEDIA_TYPE = "media_type";
+
+            /**
+             * Constants for MEDIA_TYPE
+             */
+            public static final int MEDIA_TYPE_NONE = 0;
+            public static final int MEDIA_TYPE_IMAGE = 1;
+            public static final int MEDIA_TYPE_AUDIO = 2;
+            public static final int MEDIA_TYPE_VIDEO = 3;
+            public static final int MEDIA_TYPE_PLAYLIST = 4;
         }
-
-        /**
-         * The MIME type of the file
-         * <P>Type: TEXT</P>
-         */
-        public static final String MIME_TYPE = "mime_type";
-
-        /**
-         * The title of the content
-         * <P>Type: TEXT</P>
-         */
-        public static final String TITLE = "title";
     }
 
     /**
diff --git a/core/java/android/provider/Mtp.java b/core/java/android/provider/Mtp.java
index bba54e1..de161e7 100644
--- a/core/java/android/provider/Mtp.java
+++ b/core/java/android/provider/Mtp.java
@@ -75,7 +75,7 @@
             return Uri.parse(CONTENT_AUTHORITY_DEVICE_SLASH + deviceID + "/storage");
         }
 
-        public static Uri getContentUri(int deviceID, int storageID) {
+        public static Uri getContentUri(int deviceID, long storageID) {
             return Uri.parse(CONTENT_AUTHORITY_DEVICE_SLASH + deviceID + "/storage/" + storageID);
         }
 
@@ -97,17 +97,17 @@
      */
     public static final class Object implements BaseColumns {
 
-        public static Uri getContentUri(int deviceID, int objectID) {
+        public static Uri getContentUri(int deviceID, long objectID) {
             return Uri.parse(CONTENT_AUTHORITY_DEVICE_SLASH + deviceID
                     + "/object/" + objectID);
         }
 
-        public static Uri getContentUriForObjectChildren(int deviceID, int objectID) {
+        public static Uri getContentUriForObjectChildren(int deviceID, long objectID) {
             return Uri.parse(CONTENT_AUTHORITY_DEVICE_SLASH + deviceID
                     + "/object/" + objectID + "/child");
         }
 
-        public static Uri getContentUriForStorageChildren(int deviceID, int storageID) {
+        public static Uri getContentUriForStorageChildren(int deviceID, long storageID) {
             return Uri.parse(CONTENT_AUTHORITY_DEVICE_SLASH + deviceID
                     + "/storage/" + storageID + "/child");
         }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 4d87b79..e98fa26 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1494,6 +1494,12 @@
         public static final String AUTO_TIME = "auto_time";
 
         /**
+         * Value to specify if the user prefers the time zone
+         * to be automatically fetched from the network (NITZ). 1=yes, 0=no
+         */
+        public static final String AUTO_TIME_ZONE = "auto_time_zone";
+
+        /**
          * Display times as 12 or 24 hours
          *   12
          *   24
@@ -1775,6 +1781,7 @@
             TEXT_AUTO_PUNCTUATE,
             TEXT_SHOW_PASSWORD,
             AUTO_TIME,
+            AUTO_TIME_ZONE,
             TIME_12_24,
             DATE_FORMAT,
             ACCELEROMETER_ROTATION,
@@ -2349,6 +2356,13 @@
         public static final String DEFAULT_INPUT_METHOD = "default_input_method";
 
         /**
+         * Setting to record the input method subtype used by default, holding the ID
+         * of the desired method.
+         */
+        public static final String SELECTED_INPUT_METHOD_SUBTYPE =
+                "selected_input_method_subtype";
+
+        /**
          * Whether the device has been provisioned (0 = false, 1 = true)
          */
         public static final String DEVICE_PROVISIONED = "device_provisioned";
diff --git a/core/java/android/util/Base64InputStream.java b/core/java/android/util/Base64InputStream.java
index da3911d..e9dac24 100644
--- a/core/java/android/util/Base64InputStream.java
+++ b/core/java/android/util/Base64InputStream.java
@@ -112,7 +112,7 @@
         if (outputStart >= outputEnd) {
             return -1;
         } else {
-            return coder.output[outputStart++];
+            return coder.output[outputStart++] & 0xff;
         }
     }
 
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index e71b5b6..0321be0 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -1584,11 +1584,7 @@
         mAttachInfo.mRootView = null;
         mAttachInfo.mSurface = null;
 
-        if (mHwRenderer != null) {
-            mHwRenderer.destroy(true);
-            mHwRenderer = null;
-            mAttachInfo.mHardwareAccelerated = false;
-        }
+        destroyHardwareRenderer();
 
         mSurface.release();
 
@@ -2542,6 +2538,8 @@
         if (LOCAL_LOGV) Log.v(TAG, "DIE in " + this + " of " + mSurface);
         synchronized (this) {
             if (mAdded && !mFirst) {
+                destroyHardwareRenderer();
+
                 int viewVisibility = mView.getVisibility();
                 boolean viewVisibilityChanged = mViewVisibility != viewVisibility;
                 if (mWindowAttributesChanged || viewVisibilityChanged) {
@@ -2566,6 +2564,14 @@
         }
     }
 
+    private void destroyHardwareRenderer() {
+        if (mHwRenderer != null) {
+            mHwRenderer.destroy(true);
+            mHwRenderer = null;
+            mAttachInfo.mHardwareAccelerated = false;
+        }
+    }
+
     public void dispatchFinishedEvent(int seq, boolean handled) {
         Message msg = obtainMessage(FINISHED_EVENT);
         msg.arg1 = seq;
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 6633367..9fadc58 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -78,10 +78,6 @@
      * If overlay is enabled, the action mode UI will be allowed to cover existing window content.
      */
     public static final int FEATURE_ACTION_MODE_OVERLAY = 10;
-    /**
-     * Flag for requesting this window to be hardware accelerated, if possible. 
-     */
-    public static final int FEATURE_HARDWARE_ACCELERATED = 11;
     /** Flag for setting the progress bar's visibility to VISIBLE */
     public static final int PROGRESS_VISIBILITY_ON = -1;
     /** Flag for setting the progress bar's visibility to GONE */
diff --git a/core/java/android/view/WindowOrientationListener.java b/core/java/android/view/WindowOrientationListener.java
index 55d11bb..8ffa158 100755
--- a/core/java/android/view/WindowOrientationListener.java
+++ b/core/java/android/view/WindowOrientationListener.java
@@ -214,9 +214,9 @@
         // background.
 
         // When device is near-vertical (screen approximately facing the horizon)
-        private static final int DEFAULT_TIME_CONSTANT_MS = 50;
+        private static final int DEFAULT_TIME_CONSTANT_MS = 100;
         // When device is partially tilted towards the sky or ground
-        private static final int TILTED_TIME_CONSTANT_MS = 300;
+        private static final int TILTED_TIME_CONSTANT_MS = 500;
         // When device is under external acceleration, i.e. not just gravity.  We heavily distrust
         // such readings.
         private static final int ACCELERATING_TIME_CONSTANT_MS = 2000;
diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java
index 2ddf5f8..5ab3d34 100644
--- a/core/java/android/view/inputmethod/InputMethod.java
+++ b/core/java/android/view/inputmethod/InputMethod.java
@@ -219,4 +219,10 @@
      * {@link InputMethodManager#RESULT_HIDDEN InputMethodManager.RESULT_HIDDEN}.
      */
     public void hideSoftInput(int flags, ResultReceiver resultReceiver);
+
+    /**
+     * Notify that the input method subtype is being changed in the same input method.
+     * @param subtype New subtype of the notified input method
+     */
+    public void changeInputMethodSubtype(InputMethodSubtype subtype);
 }
diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java
index 68e231c..54102f6 100644
--- a/core/java/android/view/inputmethod/InputMethodInfo.java
+++ b/core/java/android/view/inputmethod/InputMethodInfo.java
@@ -44,7 +44,7 @@
  */
 public final class InputMethodInfo implements Parcelable {
     static final String TAG = "InputMethodInfo";
-    
+
     /**
      * The Service that implements this input method component.
      */
@@ -71,102 +71,6 @@
     final int mIsDefaultResId;
 
     /**
-     * InputMethodSubtype is a subtype contained in the input method. Subtype can describe
-     * locales (e.g. en_US, fr_FR...) and modes (e.g. voice, keyboard...), and is used for
-     * IME switch. The subtype allows the system to call the specified subtype of IME directly.
-     */
-    public static class InputMethodSubtype implements Parcelable {
-        private final String mSubtypeName;
-        private final int mSubtypeIconId;
-        private final String mSubtypeLocale;
-        private final String mSubtypeMode;
-        private final String mSubtypeExtraValue;
-
-        /**
-         * Constructor
-         * @param name The name of the subtype
-         * @param iconId The icon of the subtype
-         * @param locale The locale supported by the subtype
-         * @param mode The mode supported by the subtype
-         * @param extraValue The extra value of the subtype
-         */
-        InputMethodSubtype(String name, int iconId, String locale, String mode,
-                String extraValue) {
-            mSubtypeName = name;
-            mSubtypeIconId = iconId;
-            mSubtypeLocale = locale;
-            mSubtypeMode = mode;
-            mSubtypeExtraValue = extraValue;
-        }
-
-        InputMethodSubtype(Parcel source) {
-            mSubtypeName = source.readString();
-            mSubtypeIconId = source.readInt();
-            mSubtypeLocale = source.readString();
-            mSubtypeMode = source.readString();
-            mSubtypeExtraValue = source.readString();
-        }
-
-        /**
-         * @return the name of the subtype
-         */
-        public String getName() {
-            return mSubtypeName;
-        }
-
-        /**
-         * @return the icon of the subtype
-         */
-        public int getIconId() {
-            return mSubtypeIconId;
-        }
-
-        /**
-         * @return the locale of the subtype
-         */
-        public String getLocale() {
-            return mSubtypeLocale;
-        }
-
-        /**
-         * @return the mode of the subtype
-         */
-        public String getMode() {
-            return mSubtypeMode;
-        }
-
-        /**
-         * @return the extra value of the subtype
-         */
-        public String getExtraValue() {
-            return mSubtypeExtraValue;
-        }
-
-        public int describeContents() {
-            return 0;
-        }
-
-        public void writeToParcel(Parcel dest, int parcelableFlags) {
-            dest.writeString(mSubtypeName);
-            dest.writeInt(mSubtypeIconId);
-            dest.writeString(mSubtypeLocale);
-            dest.writeString(mSubtypeMode);
-            dest.writeString(mSubtypeExtraValue);
-        }
-
-        public static final Parcelable.Creator<InputMethodSubtype> CREATOR
-                = new Parcelable.Creator<InputMethodSubtype>() {
-            public InputMethodSubtype createFromParcel(Parcel source) {
-                return new InputMethodSubtype(source);
-            }
-
-            public InputMethodSubtype[] newArray(int size) {
-                return new InputMethodSubtype[size];
-            }
-        };
-    }
-
-    /**
      * The array of the subtypes.
      */
     private final ArrayList<InputMethodSubtype> mSubtypes = new ArrayList<InputMethodSubtype>();
@@ -223,24 +127,27 @@
             // Parse all subtypes
             while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
                     && type != XmlPullParser.END_DOCUMENT) {
-                nodeName = parser.getName();
-                if (!"subtype".equals(nodeName)) {
-                    throw new XmlPullParserException(
-                        "Meta-data in input-method does not start with subtype tag");
+                if (type == XmlPullParser.START_TAG) {
+                    nodeName = parser.getName();
+                    if (!"subtype".equals(nodeName)) {
+                        throw new XmlPullParserException(
+                                "Meta-data in input-method does not start with subtype tag");
+                    }
+                    final TypedArray a = res.obtainAttributes(
+                            attrs, com.android.internal.R.styleable.InputMethod_Subtype);
+                    InputMethodSubtype subtype = new InputMethodSubtype(
+                            a.getResourceId(com.android.internal.R.styleable
+                                    .InputMethod_Subtype_label, 0),
+                            a.getResourceId(com.android.internal.R.styleable
+                                    .InputMethod_Subtype_icon, 0),
+                            a.getString(com.android.internal.R.styleable
+                                    .InputMethod_Subtype_imeSubtypeLocale),
+                            a.getResourceId(com.android.internal.R.styleable
+                                    .InputMethod_Subtype_imeSubtypeMode, 0),
+                            a.getString(com.android.internal.R.styleable
+                                    .InputMethod_Subtype_imeSubtypeExtraValue));
+                    mSubtypes.add(subtype);
                 }
-                final TypedArray a = res.obtainAttributes(
-                        attrs, com.android.internal.R.styleable.InputMethod_Subtype);
-                InputMethodSubtype subtype = new InputMethodSubtype(
-                        a.getString(com.android.internal.R.styleable.InputMethod_Subtype_label),
-                        a.getResourceId(
-                                com.android.internal.R.styleable.InputMethod_Subtype_icon, 0),
-                        a.getString(com.android.internal.R.styleable
-                                .InputMethod_Subtype_imeSubtypeLocale),
-                        a.getString(com.android.internal.R.styleable
-                                .InputMethod_Subtype_imeSubtypeMode),
-                        a.getString(com.android.internal.R.styleable
-                                .InputMethod_Subtype_imeSubtypeExtraValue));
-                mSubtypes.add(subtype);
             }
         } catch (NameNotFoundException e) {
             throw new XmlPullParserException(
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index e30687f..8bd3298 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -1380,7 +1380,7 @@
             }
         }
     }
-    
+
     public void showInputMethodPicker() {
         synchronized (mH) {
             try {
@@ -1391,6 +1391,16 @@
         }
     }
 
+    public void showInputMethodSubtypePicker() {
+        synchronized (mH) {
+            try {
+                mService.showInputMethodSubtypePickerFromClient(mClient);
+            } catch (RemoteException e) {
+                Log.w(TAG, "IME died: " + mCurId, e);
+            }
+        }
+    }
+
     void doDump(FileDescriptor fd, PrintWriter fout, String[] args) {
         final Printer p = new PrintWriterPrinter(fout);
         p.println("Input method client state for " + this + ":");
diff --git a/core/java/android/view/inputmethod/InputMethodSubtype.aidl b/core/java/android/view/inputmethod/InputMethodSubtype.aidl
new file mode 100644
index 0000000..ed82b84
--- /dev/null
+++ b/core/java/android/view/inputmethod/InputMethodSubtype.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.view.inputmethod;
+
+parcelable InputMethodSubtype;
diff --git a/core/java/android/view/inputmethod/InputMethodSubtype.java b/core/java/android/view/inputmethod/InputMethodSubtype.java
new file mode 100644
index 0000000..a1ed044
--- /dev/null
+++ b/core/java/android/view/inputmethod/InputMethodSubtype.java
@@ -0,0 +1,149 @@
+/*
+ * 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.view.inputmethod;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Arrays;
+
+/**
+ * Information given to an {@link InputMethod} about a client connecting
+ * to it.
+ */
+/**
+ * InputMethodSubtype is a subtype contained in the input method. Subtype can describe
+ * locales (e.g. en_US, fr_FR...) and modes (e.g. voice, keyboard...), and is used for
+ * IME switch. The subtype allows the system to call the specified subtype of IME directly.
+ */
+public final class InputMethodSubtype implements Parcelable {
+    private final int mSubtypeNameResId;
+    private final int mSubtypeIconResId;
+    private final String mSubtypeLocale;
+    private final int mSubtypeModeResId;
+    private final String mSubtypeExtraValue;
+    private final int mSubtypeHashCode;
+
+    /**
+     * Constructor
+     * @param nameId The name of the subtype
+     * @param iconId The icon of the subtype
+     * @param locale The locale supported by the subtype
+     * @param modeId The mode supported by the subtype
+     * @param extraValue The extra value of the subtype
+     */
+    InputMethodSubtype(int nameId, int iconId, String locale, int modeId, String extraValue) {
+        mSubtypeNameResId = nameId;
+        mSubtypeIconResId = iconId;
+        mSubtypeLocale = locale;
+        mSubtypeModeResId = modeId;
+        mSubtypeExtraValue = extraValue;
+        mSubtypeHashCode = hashCodeInternal(mSubtypeNameResId, mSubtypeIconResId, mSubtypeLocale,
+                mSubtypeModeResId, mSubtypeExtraValue);
+    }
+
+    InputMethodSubtype(Parcel source) {
+        mSubtypeNameResId = source.readInt();
+        mSubtypeIconResId = source.readInt();
+        mSubtypeLocale = source.readString();
+        mSubtypeModeResId = source.readInt();
+        mSubtypeExtraValue = source.readString();
+        mSubtypeHashCode = hashCodeInternal(mSubtypeNameResId, mSubtypeIconResId, mSubtypeLocale,
+                mSubtypeModeResId, mSubtypeExtraValue);
+    }
+
+    /**
+     * @return the name of the subtype
+     */
+    public int getNameResId() {
+        return mSubtypeNameResId;
+    }
+
+    /**
+     * @return the icon of the subtype
+     */
+    public int getIconResId() {
+        return mSubtypeIconResId;
+    }
+
+    /**
+     * @return the locale of the subtype
+     */
+    public String getLocale() {
+        return mSubtypeLocale;
+    }
+
+    /**
+     * @return the mode of the subtype
+     */
+    public int getModeResId() {
+        return mSubtypeModeResId;
+    }
+
+    /**
+     * @return the extra value of the subtype
+     */
+    public String getExtraValue() {
+        return mSubtypeExtraValue;
+    }
+
+    @Override
+    public int hashCode() {
+        return mSubtypeHashCode;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o instanceof InputMethodSubtype) {
+            InputMethodSubtype subtype = (InputMethodSubtype) o;
+            return (subtype.getNameResId() == getNameResId())
+                && (subtype.getModeResId() == getModeResId())
+                && (subtype.getIconResId() == getIconResId())
+                && (subtype.getLocale().equals(getLocale()))
+                && (subtype.getExtraValue().equals(getExtraValue()));
+        }
+        return false;
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel dest, int parcelableFlags) {
+        dest.writeInt(mSubtypeNameResId);
+        dest.writeInt(mSubtypeIconResId);
+        dest.writeString(mSubtypeLocale);
+        dest.writeInt(mSubtypeModeResId);
+        dest.writeString(mSubtypeExtraValue);
+    }
+
+    public static final Parcelable.Creator<InputMethodSubtype> CREATOR
+            = new Parcelable.Creator<InputMethodSubtype>() {
+        public InputMethodSubtype createFromParcel(Parcel source) {
+            return new InputMethodSubtype(source);
+        }
+
+        public InputMethodSubtype[] newArray(int size) {
+            return new InputMethodSubtype[size];
+        }
+    };
+
+    private static int hashCodeInternal(int nameResId, int iconResId, String locale,
+            int modeResId, String extraValue) {
+        return Arrays.hashCode(new Object[] {nameResId, iconResId, locale, modeResId, extraValue});
+    }
+}
\ No newline at end of file
diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java
index 61812ee..35cfbcb 100644
--- a/core/java/android/webkit/BrowserFrame.java
+++ b/core/java/android/webkit/BrowserFrame.java
@@ -1037,15 +1037,16 @@
      * We delegate the request to CallbackProxy, and route its response to
      * {@link #nativeAuthenticationProceed(int, String, String)} or
      * {@link #nativeAuthenticationCancel(int)}.
+     *
+     * We don't care what thread the callback is invoked on. All threading is
+     * handled on the C++ side, because the WebKit thread may be blocked on a
+     * synchronous call and unable to pump our MessageQueue.
      */
     private void didReceiveAuthenticationChallenge(
             final int handle, String host, String realm, final boolean useCachedCredentials) {
 
         HttpAuthHandler handler = new HttpAuthHandler() {
 
-            private static final int AUTH_PROCEED = 1;
-            private static final int AUTH_CANCEL = 2;
-
             @Override
             public boolean useHttpAuthUsernamePassword() {
                 return useCachedCredentials;
@@ -1053,30 +1054,12 @@
 
             @Override
             public void proceed(String username, String password) {
-                Message msg = obtainMessage(AUTH_PROCEED);
-                msg.getData().putString("username", username);
-                msg.getData().putString("password", password);
-                sendMessage(msg);
+                nativeAuthenticationProceed(handle, username, password);
             }
 
             @Override
             public void cancel() {
-                sendMessage(obtainMessage(AUTH_CANCEL));
-            }
-
-            @Override
-            public void handleMessage(Message msg) {
-                switch (msg.what) {
-                    case AUTH_PROCEED:
-                        String username = msg.getData().getString("username");
-                        String password = msg.getData().getString("password");
-                        nativeAuthenticationProceed(handle, username, password);
-                        break;
-
-                    case AUTH_CANCEL:
-                        nativeAuthenticationCancel(handle);
-                        break;
-                }
+                nativeAuthenticationCancel(handle);
             }
         };
         mCallbackProxy.onReceivedHttpAuthRequest(handler, host, realm);
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index 63fc008..20aafbd 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -207,6 +207,7 @@
     private RenderPriority  mRenderPriority = RenderPriority.NORMAL;
     private int             mOverrideCacheMode = LOAD_DEFAULT;
     private boolean         mSaveFormData = true;
+    private boolean         mAutoFillEnabled = false;
     private boolean         mSavePassword = true;
     private boolean         mLightTouchEnabled = false;
     private boolean         mNeedInitialFocus = true;
@@ -596,6 +597,20 @@
     }
 
     /**
+     * @hide
+     */
+    public void setAutoFillEnabled(boolean enabled) {
+        mAutoFillEnabled = enabled;
+    }
+
+    /**
+     * @hide
+     */
+    public boolean getAutoFillEnabled() {
+        return mAutoFillEnabled;
+    }
+
+    /**
      *  Store whether the WebView is saving password.
      */
     public void setSavePassword(boolean save) {
diff --git a/core/java/android/webkit/WebTextView.java b/core/java/android/webkit/WebTextView.java
index fca5ee1..1aff170 100644
--- a/core/java/android/webkit/WebTextView.java
+++ b/core/java/android/webkit/WebTextView.java
@@ -47,6 +47,7 @@
 import android.view.inputmethod.InputMethodManager;
 import android.view.inputmethod.InputConnection;
 import android.widget.AbsoluteLayout.LayoutParams;
+import android.widget.AdapterView;
 import android.widget.ArrayAdapter;
 import android.widget.AutoCompleteTextView;
 import android.widget.TextView;
@@ -116,15 +117,29 @@
     private int mDelSelStart;
     private int mDelSelEnd;
 
+    // Keep in sync with native constant in
+    // external/webkit/WebKit/android/WebCoreSupport/autofill/WebAutoFill.cpp
+    /* package */ static final int FORM_NOT_AUTOFILLABLE = -1;
+
+    private boolean mAutoFillable; // Is this textview part of an autofillable form?
+    private int mQueryId;
+
     /**
      * Create a new WebTextView.
      * @param   context The Context for this WebTextView.
      * @param   webView The WebView that created this.
      */
-    /* package */ WebTextView(Context context, WebView webView) {
+    /* package */ WebTextView(Context context, WebView webView, int autoFillQueryId) {
         super(context, null, com.android.internal.R.attr.webTextViewStyle);
         mWebView = webView;
         mMaxLength = -1;
+        setAutoFillable(autoFillQueryId);
+    }
+
+    public void setAutoFillable(int queryId) {
+        mAutoFillable = mWebView.getSettings().getAutoFillEnabled()
+                && (queryId != FORM_NOT_AUTOFILLABLE);
+        mQueryId = queryId;
     }
 
     @Override
@@ -673,6 +688,22 @@
             adapter.setTextView(this);
         }
         super.setAdapter(adapter);
+        if (mAutoFillable) {
+            setOnItemClickListener(new AdapterView.OnItemClickListener() {
+                @Override
+                public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+                    if (id == 0 && position == 0) {
+                        // Blank out the text box while we wait for WebCore to fill the form.
+                        replaceText("");
+                        // Call a webview method to tell WebCore to autofill the form.
+                        mWebView.autoFillForm(mQueryId);
+                    }
+                }
+            });
+        } else {
+            setOnItemClickListener(null);
+        }
+        showDropDown();
     }
 
     /**
@@ -964,7 +995,7 @@
             if (type != 2 /* PASSWORD */) {
                 String name = mWebView.nativeFocusCandidateName();
                 if (name != null && name.length() > 0) {
-                    mWebView.requestFormData(name, mNodePointer);
+                    mWebView.requestFormData(name, mNodePointer, mAutoFillable);
                 }
             }
         }
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 127eae2..bca9b36 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -614,6 +614,8 @@
     static final int SET_TOUCH_HIGHLIGHT_RECTS          = 131;
     static final int SAVE_WEBARCHIVE_FINISHED           = 132;
 
+    static final int SET_AUTOFILLABLE                   = 133;
+
     private static final int FIRST_PACKAGE_MSG_ID = SCROLL_TO_MSG_ID;
     private static final int LAST_PACKAGE_MSG_ID = SET_TOUCH_HIGHLIGHT_RECTS;
 
@@ -662,7 +664,8 @@
         "SET_SCROLLBAR_MODES", //            = 129;
         "SELECTION_STRING_CHANGED", //       = 130;
         "SET_TOUCH_HIGHLIGHT_RECTS", //      = 131;
-        "SAVE_WEBARCHIVE_FINISHED" //        = 132;
+        "SAVE_WEBARCHIVE_FINISHED", //       = 132;
+        "SET_AUTOFILLABLE" //                = 133;
     };
 
     // If the site doesn't use the viewport meta tag to specify the viewport,
@@ -735,6 +738,8 @@
     // for event log
     private long mLastTouchUpTime = 0;
 
+    private int mAutoFillQueryId = WebTextView.FORM_NOT_AUTOFILLABLE;
+
     /**
      * URI scheme for telephone number
      */
@@ -3868,7 +3873,7 @@
         // At this point, we know we have found an input field, so go ahead
         // and create the WebTextView if necessary.
         if (mWebTextView == null) {
-            mWebTextView = new WebTextView(mContext, WebView.this);
+            mWebTextView = new WebTextView(mContext, WebView.this, mAutoFillQueryId);
             // Initialize our generation number.
             mTextGeneration = 0;
         }
@@ -3936,13 +3941,15 @@
      * @param nodePointer Pointer to the node of the textfield, so it can be
      *          compared to the currently focused textfield when the data is
      *          retrieved.
+     * @param autoFillable true if WebKit has determined this field is part of
+     *          a form that can be auto filled.
      */
-    /* package */ void requestFormData(String name, int nodePointer) {
+    /* package */ void requestFormData(String name, int nodePointer, boolean autoFillable) {
         if (mWebViewCore.getSettings().getSaveFormData()) {
             Message update = mPrivateHandler.obtainMessage(REQUEST_FORM_DATA);
             update.arg1 = nodePointer;
             RequestFormData updater = new RequestFormData(name, getUrl(),
-                    update);
+                    update, autoFillable);
             Thread t = new Thread(updater);
             t.start();
         }
@@ -3968,15 +3975,28 @@
         private String mName;
         private String mUrl;
         private Message mUpdateMessage;
+        private boolean mAutoFillable;
 
-        public RequestFormData(String name, String url, Message msg) {
+        public RequestFormData(String name, String url, Message msg, boolean autoFillable) {
             mName = name;
             mUrl = url;
             mUpdateMessage = msg;
+            mAutoFillable = autoFillable;
         }
 
         public void run() {
-            ArrayList<String> pastEntries = mDatabase.getFormData(mUrl, mName);
+            ArrayList<String> pastEntries = new ArrayList();
+
+            if (mAutoFillable) {
+                // Note that code inside the adapter click handler in WebTextView depends
+                // on the AutoFill item being at the top of the drop down list. If you change
+                // the order, make sure to do it there too!
+                pastEntries.add(getResources().getText(
+                        com.android.internal.R.string.autofill_this_form).toString());
+            }
+
+            pastEntries.addAll(mDatabase.getFormData(mUrl, mName));
+
             if (pastEntries.size() > 0) {
                 AutoCompleteAdapter adapter = new
                         AutoCompleteAdapter(mContext, pastEntries);
@@ -6942,6 +6962,14 @@
                     }
                     break;
 
+                case SET_AUTOFILLABLE:
+                    mAutoFillQueryId = msg.arg1;
+                    if (mWebTextView != null) {
+                        mWebTextView.setAutoFillable(mAutoFillQueryId);
+                        rebuildWebTextView();
+                    }
+                    break;
+
                 default:
                     super.handleMessage(msg);
                     break;
@@ -7475,6 +7503,10 @@
         nativeUpdateCachedTextfield(updatedText, mTextGeneration);
     }
 
+    /*package*/ void autoFillForm(int autoFillQueryId) {
+        mWebViewCore.sendMessage(EventHub.AUTOFILL_FORM, autoFillQueryId, /* unused */0);
+    }
+
     private native int nativeCacheHitFramePointer();
     private native Rect nativeCacheHitNodeBounds();
     private native int nativeCacheHitNodePointer();
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index af7d95e..122cf6a 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -882,6 +882,8 @@
 
         static final int USE_MOCK_DEVICE_ORIENTATION = 191;
 
+        static final int AUTOFILL_FORM = 192;
+
         // private message ids
         private static final int DESTROY =     200;
 
@@ -1417,6 +1419,10 @@
                         case USE_MOCK_DEVICE_ORIENTATION:
                             useMockDeviceOrientation();
                             break;
+
+                        case AUTOFILL_FORM:
+                            nativeAutoFillForm(msg.arg1);
+                            break;
                     }
                 }
             };
@@ -1829,9 +1835,11 @@
                 draw.mViewState = mInitialViewState;
                 if (mViewportWidth == -1 && mSettings.getUseFixedViewport() &&
                     mSettings.getUseWideViewPort()) {
+                    final int fixedViewportMargin = mContext.getResources().getDimensionPixelSize(
+                      com.android.internal.R.dimen.fixed_viewport_margin);
                     // Use website's initial preferred width as the fixed viewport width.
                     mViewportWidth = Math.min(mSettings.getMaxFixedViewportWidth(),
-                        Math.max(draw.mWidthHeight.x, draw.mMinPrefWidth));
+                        draw.mMinPrefWidth + 2 * fixedViewportMargin);
                     draw.mViewState.mViewportWidth = mViewportWidth;
                 }
                 mInitialViewState = null;
@@ -2364,6 +2372,13 @@
         }
     }
 
+    private void setWebTextViewAutoFillable(int queryId) {
+        if (mWebView != null) {
+            Message.obtain(mWebView.mPrivateHandler, WebView.SET_AUTOFILLABLE, queryId,
+                    /* unused */0).sendToTarget();
+        }
+    }
+
     // called by JNI
     private Context getContext() {
         return mContext;
@@ -2531,4 +2546,6 @@
 
     private native ArrayList<Rect> nativeGetTouchHighlightRects(int x, int y,
             int slop);
+
+   private native void nativeAutoFillForm(int queryId);
 }
diff --git a/core/java/com/android/internal/view/IInputMethod.aidl b/core/java/com/android/internal/view/IInputMethod.aidl
index 8ff18ed..c7fcab8 100644
--- a/core/java/com/android/internal/view/IInputMethod.aidl
+++ b/core/java/com/android/internal/view/IInputMethod.aidl
@@ -23,6 +23,7 @@
 import android.view.MotionEvent;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputBinding;
+import android.view.inputmethod.InputMethodSubtype;
 import com.android.internal.view.IInputContext;
 import com.android.internal.view.IInputMethodCallback;
 import com.android.internal.view.IInputMethodSession;
@@ -52,4 +53,6 @@
     void showSoftInput(int flags, in ResultReceiver resultReceiver);
     
     void hideSoftInput(int flags, in ResultReceiver resultReceiver);
+
+    void changeInputMethodSubtype(in InputMethodSubtype subtype);
 }
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index adec0a7..d012b0f 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -48,6 +48,7 @@
             int softInputMode, boolean first, int windowFlags);
             
     void showInputMethodPickerFromClient(in IInputMethodClient client);
+    void showInputMethodSubtypePickerFromClient(in IInputMethodClient client);
     void setInputMethod(in IBinder token, String id);
     void hideMySoftInput(in IBinder token, int flags);
     void showMySoftInput(in IBinder token, int flags);
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 8310e56..fffd185 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -105,12 +105,13 @@
 	android_graphics_PixelFormat.cpp \
 	android/graphics/Picture.cpp \
 	android/graphics/PorterDuff.cpp \
-	android/graphics/LargeBitmap.cpp \
+	android/graphics/BitmapRegionDecoder.cpp \
 	android/graphics/Rasterizer.cpp \
 	android/graphics/Region.cpp \
 	android/graphics/Shader.cpp \
 	android/graphics/TextLayout.cpp \
 	android/graphics/Typeface.cpp \
+	android/graphics/Utils.cpp \
 	android/graphics/Xfermode.cpp \
 	android/graphics/YuvToJpegEncoder.cpp \
 	android_media_AudioRecord.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 4da73d7..ff62e0d 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -53,7 +53,7 @@
 extern int register_android_os_Process(JNIEnv* env);
 extern int register_android_graphics_Bitmap(JNIEnv*);
 extern int register_android_graphics_BitmapFactory(JNIEnv*);
-extern int register_android_graphics_LargeBitmap(JNIEnv*);
+extern int register_android_graphics_BitmapRegionDecoder(JNIEnv*);
 extern int register_android_graphics_Camera(JNIEnv* env);
 extern int register_android_graphics_Graphics(JNIEnv* env);
 extern int register_android_graphics_Interpolator(JNIEnv* env);
@@ -1209,7 +1209,7 @@
 
     REG_JNI(register_android_graphics_Bitmap),
     REG_JNI(register_android_graphics_BitmapFactory),
-    REG_JNI(register_android_graphics_LargeBitmap),
+    REG_JNI(register_android_graphics_BitmapRegionDecoder),
     REG_JNI(register_android_graphics_Camera),
     REG_JNI(register_android_graphics_Canvas),
     REG_JNI(register_android_graphics_ColorFilter),
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 6745b24..90a0243 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -10,6 +10,7 @@
 #include "SkUtils.h"
 #include "CreateJavaOutputStreamAdaptor.h"
 #include "AutoDecodeCancel.h"
+#include "Utils.h"
 
 #include <android_runtime/AndroidRuntime.h>
 #include <utils/Asset.h>
@@ -99,57 +100,6 @@
     }
 };
 
-class AssetStreamAdaptor : public SkStream {
-public:
-    AssetStreamAdaptor(Asset* a) : fAsset(a) {}
-    
-    virtual bool rewind() {
-        off_t pos = fAsset->seek(0, SEEK_SET);
-        if (pos == (off_t)-1) {
-            SkDebugf("----- fAsset->seek(rewind) failed\n");
-            return false;
-        }
-        return true;
-    }
-    
-    virtual size_t read(void* buffer, size_t size) {
-        ssize_t amount;
-        
-        if (NULL == buffer) {
-            if (0 == size) {  // caller is asking us for our total length
-                return fAsset->getLength();
-            }
-            // asset->seek returns new total offset
-            // we want to return amount that was skipped
-
-            off_t oldOffset = fAsset->seek(0, SEEK_CUR);
-            if (-1 == oldOffset) {
-                SkDebugf("---- fAsset->seek(oldOffset) failed\n");
-                return 0;
-            }
-            off_t newOffset = fAsset->seek(size, SEEK_CUR);
-            if (-1 == newOffset) {
-                SkDebugf("---- fAsset->seek(%d) failed\n", size);
-                return 0;
-            }
-            amount = newOffset - oldOffset;
-        } else {
-            amount = fAsset->read(buffer, size);
-            if (amount <= 0) {
-                SkDebugf("---- fAsset->read(%d) returned %d\n", size, amount);
-            }
-        }
-        
-        if (amount < 0) {
-            amount = 0;
-        }
-        return amount;
-    }
-    
-private:
-    Asset*  fAsset;
-};
-
 ///////////////////////////////////////////////////////////////////////////////
 
 static inline int32_t validOrNeg1(bool isValid, int32_t value) {
@@ -201,12 +151,6 @@
             !env->GetBooleanField(options, gOptions_nativeAllocFieldID);
 }
 
-static jobject nullObjectReturn(const char msg[]) {
-    if (msg) {
-        SkDebugf("--- %s\n", msg);
-    }
-    return NULL;
-}
 
 static SkPixelRef* installPixelRef(SkBitmap* bitmap, SkStream* stream,
                                    int sampleSize, bool ditherImage) {
@@ -377,23 +321,6 @@
     return size;
 }
 
-/** Restore the file descriptor's offset in our destructor
- */
-class AutoFDSeek {
-public:
-    AutoFDSeek(int fd) : fFD(fd) {
-        fCurr = ::lseek(fd, 0, SEEK_CUR);
-    }
-    ~AutoFDSeek() {
-        if (fCurr >= 0) {
-            ::lseek(fFD, fCurr, SEEK_SET);
-        }
-    }
-private:
-    int     fFD;
-    off_t   fCurr;
-};
-
 static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz,
                                           jobject fileDescriptor,
                                           jobject padding,
@@ -560,134 +487,6 @@
     }
 }
 
-static SkMemoryStream* buildSkMemoryStream(SkStream *stream) {
-    size_t bufferSize = 4096;
-    size_t streamLen = 0;
-    size_t len;
-    char* data = (char*)sk_malloc_throw(bufferSize);
-
-    while ((len = stream->read(data + streamLen,
-                    bufferSize - streamLen)) != 0) {
-        streamLen += len;
-        if (streamLen == bufferSize) {
-            bufferSize *= 2;
-            data = (char*)sk_realloc_throw(data, bufferSize);
-        }
-    }
-    data = (char*)sk_realloc_throw(data, streamLen);
-    SkMemoryStream* streamMem = new SkMemoryStream();
-    streamMem->setMemoryOwned(data, streamLen);
-    return streamMem;
-}
-
-static jobject doBuildTileIndex(JNIEnv* env, SkStream* stream) {
-    SkImageDecoder* decoder = SkImageDecoder::Factory(stream);
-    int width, height;
-    if (NULL == decoder) {
-        doThrowIOE(env, "Image format not supported");
-        return nullObjectReturn("SkImageDecoder::Factory returned null");
-    }
-
-    JavaPixelAllocator *javaAllocator = new JavaPixelAllocator(env, true);
-    decoder->setAllocator(javaAllocator);
-    JavaMemoryUsageReporter *javaMemoryReporter = new JavaMemoryUsageReporter(env);
-    decoder->setReporter(javaMemoryReporter);
-    javaAllocator->unref();
-    javaMemoryReporter->unref();
-
-    if (!decoder->buildTileIndex(stream, &width, &height)) {
-        char msg[100];
-        snprintf(msg, sizeof(msg), "Image failed to decode using %s decoder",
-                decoder->getFormatName());
-        doThrowIOE(env, msg);
-        return nullObjectReturn("decoder->buildTileIndex returned false");
-    }
-
-    SkLargeBitmap *bm = new SkLargeBitmap(decoder, width, height);
-
-    return GraphicsJNI::createLargeBitmap(env, bm);
-}
-
-static jobject nativeCreateLargeBitmapFromByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
-                                     int offset, int length, jboolean isShareable) {
-    /*  If isShareable we could decide to just wrap the java array and
-        share it, but that means adding a globalref to the java array object
-        For now we just always copy the array's data if isShareable.
-     */
-    AutoJavaByteArray ar(env, byteArray);
-    SkStream* stream = new SkMemoryStream(ar.ptr() + offset, length, true);
-    return doBuildTileIndex(env, stream);
-}
-
-static jobject nativeCreateLargeBitmapFromFileDescriptor(JNIEnv* env, jobject clazz,
-                                          jobject fileDescriptor, jboolean isShareable) {
-    NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
-
-    jint descriptor = env->GetIntField(fileDescriptor,
-                                       gFileDescriptor_descriptor);
-    SkStream *stream = NULL;
-    struct stat fdStat;
-    int newFD;
-    if (fstat(descriptor, &fdStat) == -1) {
-        doThrowIOE(env, "broken file descriptor");
-        return nullObjectReturn("fstat return -1");
-    }
-
-    if (isShareable &&
-            S_ISREG(fdStat.st_mode) &&
-            (newFD = ::dup(descriptor)) != -1) {
-        SkFDStream* fdStream = new SkFDStream(newFD, true);
-        if (!fdStream->isValid()) {
-            fdStream->unref();
-            return NULL;
-        }
-        stream = fdStream;
-    } else {
-        SkFDStream* fdStream = new SkFDStream(descriptor, false);
-        if (!fdStream->isValid()) {
-            fdStream->unref();
-            return NULL;
-        }
-        stream = buildSkMemoryStream(fdStream);
-        fdStream->unref();
-    }
-
-    /* Restore our offset when we leave, so we can be called more than once
-       with the same descriptor. This is only required if we didn't dup the
-       file descriptor, but it is OK to do it all the time.
-    */
-    AutoFDSeek as(descriptor);
-
-    return doBuildTileIndex(env, stream);
-}
-
-static jobject nativeCreateLargeBitmapFromStream(JNIEnv* env, jobject clazz,
-                                  jobject is,       // InputStream
-                                  jbyteArray storage, // byte[]
-                                  jboolean isShareable) {
-    jobject largeBitmap = NULL;
-    SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage, 1024);
-
-    if (stream) {
-        // for now we don't allow shareable with java inputstreams
-        SkMemoryStream *mStream = buildSkMemoryStream(stream);
-        largeBitmap = doBuildTileIndex(env, mStream);
-        stream->unref();
-    }
-    return largeBitmap;
-}
-
-static jobject nativeCreateLargeBitmapFromAsset(JNIEnv* env, jobject clazz,
-                                 jint native_asset, // Asset
-                                 jboolean isShareable) {
-    SkStream* stream, *assStream;
-    Asset* asset = reinterpret_cast<Asset*>(native_asset);
-    assStream = new AssetStreamAdaptor(asset);
-    stream = buildSkMemoryStream(assStream);
-    assStream->unref();
-    return doBuildTileIndex(env, stream);
-}
-
 ///////////////////////////////////////////////////////////////////////////////
 
 static JNINativeMethod gMethods[] = {
@@ -717,26 +516,6 @@
     },
 
     {   "nativeSetDefaultConfig", "(I)V", (void*)nativeSetDefaultConfig },
-
-    {   "nativeCreateLargeBitmap",
-        "([BIIZ)Landroid/graphics/LargeBitmap;",
-        (void*)nativeCreateLargeBitmapFromByteArray
-    },
-
-    {   "nativeCreateLargeBitmap",
-        "(Ljava/io/InputStream;[BZ)Landroid/graphics/LargeBitmap;",
-        (void*)nativeCreateLargeBitmapFromStream
-    },
-
-    {   "nativeCreateLargeBitmap",
-        "(Ljava/io/FileDescriptor;Z)Landroid/graphics/LargeBitmap;",
-        (void*)nativeCreateLargeBitmapFromFileDescriptor
-    },
-
-    {   "nativeCreateLargeBitmap",
-        "(IZ)Landroid/graphics/LargeBitmap;",
-        (void*)nativeCreateLargeBitmapFromAsset
-    },
 };
 
 static JNINativeMethod gOptionsMethods[] = {
diff --git a/core/jni/android/graphics/BitmapRegionDecoder.cpp b/core/jni/android/graphics/BitmapRegionDecoder.cpp
new file mode 100644
index 0000000..4503852
--- /dev/null
+++ b/core/jni/android/graphics/BitmapRegionDecoder.cpp
@@ -0,0 +1,305 @@
+/*
+ * 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 "BitmapRegionDecoder"
+
+#include "SkBitmap.h"
+#include "SkImageEncoder.h"
+#include "GraphicsJNI.h"
+#include "SkUtils.h"
+#include "SkTemplates.h"
+#include "SkPixelRef.h"
+#include "SkStream.h"
+#include "BitmapFactory.h"
+#include "AutoDecodeCancel.h"
+#include "SkBitmapRegionDecoder.h"
+#include "CreateJavaOutputStreamAdaptor.h"
+#include "Utils.h"
+
+#include <android_runtime/AndroidRuntime.h>
+#include "android_util_Binder.h"
+#include "android_nio_utils.h"
+#include "CreateJavaOutputStreamAdaptor.h"
+
+#include <binder/Parcel.h>
+#include <jni.h>
+#include <utils/Asset.h>
+#include <sys/stat.h>
+
+static jclass gFileDescriptor_class;
+static jfieldID gFileDescriptor_descriptor;
+
+#if 0
+    #define TRACE_BITMAP(code)  code
+#else
+    #define TRACE_BITMAP(code)
+#endif
+
+using namespace android;
+
+static SkMemoryStream* buildSkMemoryStream(SkStream *stream) {
+    size_t bufferSize = 4096;
+    size_t streamLen = 0;
+    size_t len;
+    char* data = (char*)sk_malloc_throw(bufferSize);
+
+    while ((len = stream->read(data + streamLen,
+                    bufferSize - streamLen)) != 0) {
+        streamLen += len;
+        if (streamLen == bufferSize) {
+            bufferSize *= 2;
+            data = (char*)sk_realloc_throw(data, bufferSize);
+        }
+    }
+    data = (char*)sk_realloc_throw(data, streamLen);
+    SkMemoryStream* streamMem = new SkMemoryStream();
+    streamMem->setMemoryOwned(data, streamLen);
+    return streamMem;
+}
+
+static jobject doBuildTileIndex(JNIEnv* env, SkStream* stream) {
+    SkImageDecoder* decoder = SkImageDecoder::Factory(stream);
+    int width, height;
+    if (NULL == decoder) {
+        doThrowIOE(env, "Image format not supported");
+        return nullObjectReturn("SkImageDecoder::Factory returned null");
+    }
+
+    JavaPixelAllocator *javaAllocator = new JavaPixelAllocator(env, true);
+    decoder->setAllocator(javaAllocator);
+    JavaMemoryUsageReporter *javaMemoryReporter = new JavaMemoryUsageReporter(env);
+    decoder->setReporter(javaMemoryReporter);
+    javaAllocator->unref();
+    javaMemoryReporter->unref();
+
+    if (!decoder->buildTileIndex(stream, &width, &height)) {
+        char msg[100];
+        snprintf(msg, sizeof(msg), "Image failed to decode using %s decoder",
+                decoder->getFormatName());
+        doThrowIOE(env, msg);
+        return nullObjectReturn("decoder->buildTileIndex returned false");
+    }
+
+    SkBitmapRegionDecoder *bm = new SkBitmapRegionDecoder(decoder, width, height);
+
+    return GraphicsJNI::createBitmapRegionDecoder(env, bm);
+}
+
+static jobject nativeNewInstanceFromByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
+                                     int offset, int length, jboolean isShareable) {
+    /*  If isShareable we could decide to just wrap the java array and
+        share it, but that means adding a globalref to the java array object
+        For now we just always copy the array's data if isShareable.
+     */
+    AutoJavaByteArray ar(env, byteArray);
+    SkStream* stream = new SkMemoryStream(ar.ptr() + offset, length, true);
+    return doBuildTileIndex(env, stream);
+}
+
+static jobject nativeNewInstanceFromFileDescriptor(JNIEnv* env, jobject clazz,
+                                          jobject fileDescriptor, jboolean isShareable) {
+    NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
+
+    jint descriptor = env->GetIntField(fileDescriptor,
+                                       gFileDescriptor_descriptor);
+    SkStream *stream = NULL;
+    struct stat fdStat;
+    int newFD;
+    if (fstat(descriptor, &fdStat) == -1) {
+        doThrowIOE(env, "broken file descriptor");
+        return nullObjectReturn("fstat return -1");
+    }
+
+    if (isShareable &&
+            S_ISREG(fdStat.st_mode) &&
+            (newFD = ::dup(descriptor)) != -1) {
+        SkFDStream* fdStream = new SkFDStream(newFD, true);
+        if (!fdStream->isValid()) {
+            fdStream->unref();
+            return NULL;
+        }
+        stream = fdStream;
+    } else {
+        SkFDStream* fdStream = new SkFDStream(descriptor, false);
+        if (!fdStream->isValid()) {
+            fdStream->unref();
+            return NULL;
+        }
+        stream = buildSkMemoryStream(fdStream);
+        fdStream->unref();
+    }
+
+    /* Restore our offset when we leave, so we can be called more than once
+       with the same descriptor. This is only required if we didn't dup the
+       file descriptor, but it is OK to do it all the time.
+    */
+    AutoFDSeek as(descriptor);
+
+    return doBuildTileIndex(env, stream);
+}
+
+static jobject nativeNewInstanceFromStream(JNIEnv* env, jobject clazz,
+                                  jobject is,       // InputStream
+                                  jbyteArray storage, // byte[]
+                                  jboolean isShareable) {
+    jobject largeBitmap = NULL;
+    SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage, 1024);
+
+    if (stream) {
+        // for now we don't allow shareable with java inputstreams
+        SkMemoryStream *mStream = buildSkMemoryStream(stream);
+        largeBitmap = doBuildTileIndex(env, mStream);
+        stream->unref();
+    }
+    return largeBitmap;
+}
+
+static jobject nativeNewInstanceFromAsset(JNIEnv* env, jobject clazz,
+                                 jint native_asset, // Asset
+                                 jboolean isShareable) {
+    SkStream* stream, *assStream;
+    Asset* asset = reinterpret_cast<Asset*>(native_asset);
+    assStream = new AssetStreamAdaptor(asset);
+    stream = buildSkMemoryStream(assStream);
+    assStream->unref();
+    return doBuildTileIndex(env, stream);
+}
+
+/*
+ * nine patch not supported
+ *
+ * purgeable not supported
+ * reportSizeToVM not supported
+ */
+static jobject nativeDecodeRegion(JNIEnv* env, jobject, SkBitmapRegionDecoder *brd,
+        int start_x, int start_y, int width, int height, jobject options) {
+    SkImageDecoder *decoder = brd->getDecoder();
+    int sampleSize = 1;
+    SkBitmap::Config prefConfig = SkBitmap::kNo_Config;
+    bool doDither = true;
+
+    if (NULL != options) {
+        sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
+        // initialize these, in case we fail later on
+        env->SetIntField(options, gOptions_widthFieldID, -1);
+        env->SetIntField(options, gOptions_heightFieldID, -1);
+        env->SetObjectField(options, gOptions_mimeFieldID, 0);
+
+        jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
+        prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig);
+        doDither = env->GetBooleanField(options, gOptions_ditherFieldID);
+    }
+
+    decoder->setDitherImage(doDither);
+    SkBitmap*           bitmap = new SkBitmap;
+    SkAutoTDelete<SkBitmap>       adb(bitmap);
+    AutoDecoderCancel   adc(options, decoder);
+
+    // To fix the race condition in case "requestCancelDecode"
+    // happens earlier than AutoDecoderCancel object is added
+    // to the gAutoDecoderCancelMutex linked list.
+    if (NULL != options && env->GetBooleanField(options, gOptions_mCancelID)) {
+        return nullObjectReturn("gOptions_mCancelID");;
+    }
+
+    SkIRect region;
+    region.fLeft = start_x;
+    region.fTop = start_y;
+    region.fRight = start_x + width;
+    region.fBottom = start_y + height;
+
+    if (!brd->decodeRegion(bitmap, region, prefConfig, sampleSize)) {
+        return nullObjectReturn("decoder->decodeRegion returned false");
+    }
+
+    // update options (if any)
+    if (NULL != options) {
+        env->SetIntField(options, gOptions_widthFieldID, bitmap->width());
+        env->SetIntField(options, gOptions_heightFieldID, bitmap->height());
+        // TODO: set the mimeType field with the data from the codec.
+        // but how to reuse a set of strings, rather than allocating new one
+        // each time?
+        env->SetObjectField(options, gOptions_mimeFieldID,
+                            getMimeTypeString(env, decoder->getFormat()));
+    }
+
+    // detach bitmap from its autotdeleter, since we want to own it now
+    adb.detach();
+
+    SkPixelRef* pr;
+    pr = bitmap->pixelRef();
+    // promise we will never change our pixels (great for sharing and pictures)
+    pr->setImmutable();
+    // now create the java bitmap
+    return GraphicsJNI::createBitmap(env, bitmap, false, NULL);
+}
+
+static int nativeGetHeight(JNIEnv* env, jobject, SkBitmapRegionDecoder *brd) {
+    return brd->getHeight();
+}
+
+static int nativeGetWidth(JNIEnv* env, jobject, SkBitmapRegionDecoder *brd) {
+    return brd->getWidth();
+}
+
+static void nativeClean(JNIEnv* env, jobject, SkBitmapRegionDecoder *brd) {
+    delete brd;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include <android_runtime/AndroidRuntime.h>
+
+static JNINativeMethod gBitmapRegionDecoderMethods[] = {
+    {   "nativeDecodeRegion",
+        "(IIIIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
+        (void*)nativeDecodeRegion},
+
+    {   "nativeGetHeight", "(I)I", (void*)nativeGetHeight},
+
+    {   "nativeGetWidth", "(I)I", (void*)nativeGetWidth},
+
+    {   "nativeClean", "(I)V", (void*)nativeClean},
+
+    {   "nativeNewInstance",
+        "([BIIZ)Landroid/graphics/BitmapRegionDecoder;",
+        (void*)nativeNewInstanceFromByteArray
+    },
+
+    {   "nativeNewInstance",
+        "(Ljava/io/InputStream;[BZ)Landroid/graphics/BitmapRegionDecoder;",
+        (void*)nativeNewInstanceFromStream
+    },
+
+    {   "nativeNewInstance",
+        "(Ljava/io/FileDescriptor;Z)Landroid/graphics/BitmapRegionDecoder;",
+        (void*)nativeNewInstanceFromFileDescriptor
+    },
+
+    {   "nativeNewInstance",
+        "(IZ)Landroid/graphics/BitmapRegionDecoder;",
+        (void*)nativeNewInstanceFromAsset
+    },
+};
+
+#define kClassPathName  "android/graphics/BitmapRegionDecoder"
+
+int register_android_graphics_BitmapRegionDecoder(JNIEnv* env);
+int register_android_graphics_BitmapRegionDecoder(JNIEnv* env)
+{
+    return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
+            gBitmapRegionDecoderMethods, SK_ARRAY_COUNT(gBitmapRegionDecoderMethods));
+}
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 72cea65..1ebe14b 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -168,8 +168,8 @@
 static jclass   gBitmapConfig_class;
 static jfieldID gBitmapConfig_nativeInstanceID;
 
-static jclass   gLargeBitmap_class;
-static jmethodID gLargeBitmap_constructorMethodID;
+static jclass   gBitmapRegionDecoder_class;
+static jmethodID gBitmapRegionDecoder_constructorMethodID;
 
 static jclass   gCanvas_class;
 static jfieldID gCanvas_nativeInstanceID;
@@ -376,17 +376,18 @@
     }
     return obj;
 }
-jobject GraphicsJNI::createLargeBitmap(JNIEnv* env, SkLargeBitmap* bitmap)
+
+jobject GraphicsJNI::createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap)
 {
     SkASSERT(bitmap != NULL);
 
-    jobject obj = env->AllocObject(gLargeBitmap_class);
+    jobject obj = env->AllocObject(gBitmapRegionDecoder_class);
     if (hasException(env)) {
         obj = NULL;
         return obj;
     }
     if (obj) {
-        env->CallVoidMethod(obj, gLargeBitmap_constructorMethodID, (jint)bitmap);
+        env->CallVoidMethod(obj, gBitmapRegionDecoder_constructorMethodID, (jint)bitmap);
         if (hasException(env)) {
             obj = NULL;
         }
@@ -612,8 +613,8 @@
     gBitmap_constructorMethodID = env->GetMethodID(gBitmap_class, "<init>",
                                             "(IZ[BI)V");
 
-    gLargeBitmap_class = make_globalref(env, "android/graphics/LargeBitmap");
-    gLargeBitmap_constructorMethodID = env->GetMethodID(gLargeBitmap_class, "<init>", "(I)V");
+    gBitmapRegionDecoder_class = make_globalref(env, "android/graphics/BitmapRegionDecoder");
+    gBitmapRegionDecoder_constructorMethodID = env->GetMethodID(gBitmapRegionDecoder_class, "<init>", "(I)V");
 
     gBitmapConfig_class = make_globalref(env, "android/graphics/Bitmap$Config");
     gBitmapConfig_nativeInstanceID = getFieldIDCheck(env, gBitmapConfig_class,
@@ -651,4 +652,3 @@
 
     return 0;
 }
-
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index 1a43a3e..d0f9125 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -4,7 +4,7 @@
 #include "SkPoint.h"
 #include "SkRect.h"
 #include "SkBitmap.h"
-#include "../images/SkLargeBitmap.h"
+#include "../images/SkBitmapRegionDecoder.h"
 #include "../images/SkImageDecoder.h"
 #include <jni.h>
 
@@ -56,7 +56,7 @@
     
     static jobject createRegion(JNIEnv* env, SkRegion* region);
 
-    static jobject createLargeBitmap(JNIEnv* env, SkLargeBitmap* bitmap);
+    static jobject createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap);
 
     /** Set a pixelref for the bitmap (needs setConfig to already be called)
         Returns true on success. If it returns false, then it failed, and the
@@ -181,4 +181,3 @@
     do { if (NULL == (object)) { doThrowNPE(env); return; } } while (0)
 
 #endif
-
diff --git a/core/jni/android/graphics/LargeBitmap.cpp b/core/jni/android/graphics/LargeBitmap.cpp
deleted file mode 100644
index 4cf5dfa..0000000
--- a/core/jni/android/graphics/LargeBitmap.cpp
+++ /dev/null
@@ -1,138 +0,0 @@
-#define LOG_TAG "LargeBitmap"
-
-#include "SkBitmap.h"
-#include "SkImageEncoder.h"
-#include "SkColorPriv.h"
-#include "GraphicsJNI.h"
-#include "SkDither.h"
-#include "SkUnPreMultiply.h"
-#include "SkUtils.h"
-#include "SkTemplates.h"
-#include "SkPixelRef.h"
-#include "BitmapFactory.h"
-#include "AutoDecodeCancel.h"
-#include "SkLargeBitmap.h"
-
-#include <binder/Parcel.h>
-#include "android_util_Binder.h"
-#include "android_nio_utils.h"
-#include "CreateJavaOutputStreamAdaptor.h"
-
-#include <jni.h>
-
-#if 0
-    #define TRACE_BITMAP(code)  code
-#else
-    #define TRACE_BITMAP(code)
-#endif
-
-static jobject nullObjectReturn(const char msg[]) {
-    if (msg) {
-        SkDebugf("--- %s\n", msg);
-    }
-    return NULL;
-}
-
-/*
- * nine patch not supported
- *
- * purgeable not supported
- * reportSizeToVM not supported
- */
-static jobject nativeDecodeRegion(JNIEnv* env, jobject, SkLargeBitmap *bm,
-        int start_x, int start_y, int width, int height, jobject options) {
-    SkImageDecoder *decoder = bm->getDecoder();
-    int sampleSize = 1;
-    SkBitmap::Config prefConfig = SkBitmap::kNo_Config;
-    bool doDither = true;
-
-    if (NULL != options) {
-        sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
-        // initialize these, in case we fail later on
-        env->SetIntField(options, gOptions_widthFieldID, -1);
-        env->SetIntField(options, gOptions_heightFieldID, -1);
-        env->SetObjectField(options, gOptions_mimeFieldID, 0);
-
-        jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
-        prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig);
-        doDither = env->GetBooleanField(options, gOptions_ditherFieldID);
-    }
-
-    decoder->setDitherImage(doDither);
-    SkBitmap*           bitmap = new SkBitmap;
-    SkAutoTDelete<SkBitmap>       adb(bitmap);
-    AutoDecoderCancel   adc(options, decoder);
-
-    // To fix the race condition in case "requestCancelDecode"
-    // happens earlier than AutoDecoderCancel object is added
-    // to the gAutoDecoderCancelMutex linked list.
-    if (NULL != options && env->GetBooleanField(options, gOptions_mCancelID)) {
-        return nullObjectReturn("gOptions_mCancelID");;
-    }
-
-    SkIRect region;
-    region.fLeft = start_x;
-    region.fTop = start_y;
-    region.fRight = start_x + width;
-    region.fBottom = start_y + height;
-
-    if (!bm->decodeRegion(bitmap, region, prefConfig, sampleSize)) {
-        return nullObjectReturn("decoder->decodeRegion returned false");
-    }
-
-    // update options (if any)
-    if (NULL != options) {
-        env->SetIntField(options, gOptions_widthFieldID, bitmap->width());
-        env->SetIntField(options, gOptions_heightFieldID, bitmap->height());
-        // TODO: set the mimeType field with the data from the codec.
-        // but how to reuse a set of strings, rather than allocating new one
-        // each time?
-        env->SetObjectField(options, gOptions_mimeFieldID,
-                            getMimeTypeString(env, decoder->getFormat()));
-    }
-
-    // detach bitmap from its autotdeleter, since we want to own it now
-    adb.detach();
-
-    SkPixelRef* pr;
-    pr = bitmap->pixelRef();
-    // promise we will never change our pixels (great for sharing and pictures)
-    pr->setImmutable();
-    // now create the java bitmap
-    return GraphicsJNI::createBitmap(env, bitmap, false, NULL);
-}
-
-static int nativeGetHeight(JNIEnv* env, jobject, SkLargeBitmap *bm) {
-    return bm->getHeight();
-}
-
-static int nativeGetWidth(JNIEnv* env, jobject, SkLargeBitmap *bm) {
-    return bm->getWidth();
-}
-
-static void nativeClean(JNIEnv* env, jobject, SkLargeBitmap *bm) {
-    delete bm;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-#include <android_runtime/AndroidRuntime.h>
-
-static JNINativeMethod gLargeBitmapMethods[] = {
-    {   "nativeDecodeRegion",
-        "(IIIIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
-        (void*)nativeDecodeRegion},
-    {   "nativeGetHeight", "(I)I", (void*)nativeGetHeight},
-    {   "nativeGetWidth", "(I)I", (void*)nativeGetWidth},
-    {   "nativeClean", "(I)V", (void*)nativeClean},
-};
-
-#define kClassPathName  "android/graphics/LargeBitmap"
-
-int register_android_graphics_LargeBitmap(JNIEnv* env);
-int register_android_graphics_LargeBitmap(JNIEnv* env)
-{
-    return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
-                                gLargeBitmapMethods, SK_ARRAY_COUNT(gLargeBitmapMethods));
-}
-
diff --git a/core/jni/android/graphics/Utils.cpp b/core/jni/android/graphics/Utils.cpp
new file mode 100644
index 0000000..b6ead19
--- /dev/null
+++ b/core/jni/android/graphics/Utils.cpp
@@ -0,0 +1,70 @@
+/*
+ * 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.h"
+#include "SkUtils.h"
+
+using namespace android;
+
+bool AssetStreamAdaptor::rewind() {
+    off_t pos = fAsset->seek(0, SEEK_SET);
+    if (pos == (off_t)-1) {
+        SkDebugf("----- fAsset->seek(rewind) failed\n");
+        return false;
+    }
+    return true;
+}
+
+size_t AssetStreamAdaptor::read(void* buffer, size_t size) {
+    ssize_t amount;
+
+    if (NULL == buffer) {
+        if (0 == size) {  // caller is asking us for our total length
+            return fAsset->getLength();
+        }
+        // asset->seek returns new total offset
+        // we want to return amount that was skipped
+
+        off_t oldOffset = fAsset->seek(0, SEEK_CUR);
+        if (-1 == oldOffset) {
+            SkDebugf("---- fAsset->seek(oldOffset) failed\n");
+            return 0;
+        }
+        off_t newOffset = fAsset->seek(size, SEEK_CUR);
+        if (-1 == newOffset) {
+            SkDebugf("---- fAsset->seek(%d) failed\n", size);
+            return 0;
+        }
+        amount = newOffset - oldOffset;
+    } else {
+        amount = fAsset->read(buffer, size);
+        if (amount <= 0) {
+            SkDebugf("---- fAsset->read(%d) returned %d\n", size, amount);
+        }
+    }
+
+    if (amount < 0) {
+        amount = 0;
+    }
+    return amount;
+}
+
+jobject android::nullObjectReturn(const char msg[]) {
+    if (msg) {
+        SkDebugf("--- %s\n", msg);
+    }
+    return NULL;
+}
diff --git a/core/jni/android/graphics/Utils.h b/core/jni/android/graphics/Utils.h
new file mode 100644
index 0000000..2de41a1
--- /dev/null
+++ b/core/jni/android/graphics/Utils.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2006 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 UTILS_DEFINED
+#define UTILS_DEFINED
+
+#include "SkStream.h"
+
+#include "android_util_Binder.h"
+
+#include <jni.h>
+#include <utils/Asset.h>
+
+namespace android {
+
+class AssetStreamAdaptor : public SkStream {
+public:
+    AssetStreamAdaptor(Asset* a) : fAsset(a) {}
+    virtual bool rewind();
+    virtual size_t read(void* buffer, size_t size);
+
+private:
+    Asset*  fAsset;
+};
+
+
+/** Restore the file descriptor's offset in our destructor
+ */
+class AutoFDSeek {
+public:
+    AutoFDSeek(int fd) : fFD(fd) {
+        fCurr = ::lseek(fd, 0, SEEK_CUR);
+    }
+    ~AutoFDSeek() {
+        if (fCurr >= 0) {
+            ::lseek(fFD, fCurr, SEEK_SET);
+        }
+    }
+private:
+    int     fFD;
+    off_t   fCurr;
+};
+
+jobject nullObjectReturn(const char msg[]);
+
+}; // namespace android
+
+#endif
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 539a986..171bb45 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -45,6 +45,8 @@
     <dimen name="fastscroll_thumb_height">52dp</dimen>
     <!-- Min width for a tablet device -->
     <dimen name="min_xlarge_screen_width">800dp</dimen>
+    <!-- Fixed viewport margin for website content width change -->
+    <dimen name="fixed_viewport_margin">7dp</dimen>
     <!-- Default height of a key in the password keyboard for alpha -->
     <dimen name="password_keyboard_key_height_alpha">56dip</dimen>
     <!-- Default height of a key in the password keyboard for numeric -->
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 95934d5..2d4de8b 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1629,6 +1629,9 @@
     <!-- Toast for double-tap -->
     <string name="double_tap_toast">Tip: double-tap to zoom in and out.</string>
 
+    <!-- Text to show in the auto complete drop down list on a text view when the browser can auto fill the entire form -->
+    <string name="autofill_this_form">AutoFill this form</string>
+
     <!-- Title of an application permission, listed so the user can choose whether
         they want to allow the application to do this. -->
     <string name="permlab_readHistoryBookmarks">read Browser\'s history and bookmarks</string>
diff --git a/core/tests/coretests/src/android/util/Base64Test.java b/core/tests/coretests/src/android/util/Base64Test.java
index 0f5b090..53368d4 100644
--- a/core/tests/coretests/src/android/util/Base64Test.java
+++ b/core/tests/coretests/src/android/util/Base64Test.java
@@ -16,6 +16,9 @@
 
 package android.util;
 
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
 import junit.framework.TestCase;
 
 import java.io.ByteArrayInputStream;
@@ -404,6 +407,14 @@
         }
     }
 
+    /** http://b/3026478 */
+    public void testSingleByteReads() throws IOException {
+        InputStream in = new Base64InputStream(
+                new ByteArrayInputStream("/v8=".getBytes()), Base64.DEFAULT);
+        assertEquals(254, in.read());
+        assertEquals(255, in.read());
+    }
+
     /**
      * Tests that Base64OutputStream produces exactly the same results
      * as calling Base64.encode/.decode on an in-memory array.
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index dc21a72..ea5ed6b 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -566,132 +566,6 @@
         nativeSetDefaultConfig(config.nativeInt);
     }
 
-    /**
-     * Create a LargeBitmap from the specified byte array.
-     * Currently only the Jpeg format is supported.
-     *
-     * @param data byte array of compressed image data.
-     * @param offset offset into data for where the decoder should begin
-     *               parsing.
-     * @param length the number of bytes, beginning at offset, to parse
-     * @param isShareable If this is true, then the LargeBitmap may keep a
-     *                    shallow reference to the input. If this is false,
-     *                    then the LargeBitmap will explicitly make a copy of the
-     *                    input data, and keep that. Even if sharing is allowed,
-     *                    the implementation may still decide to make a deep
-     *                    copy of the input data. If an image is progressively encoded,
-     *                    allowing sharing may degrade the decoding speed.
-     * @return LargeBitmap, or null if the image data could not be decoded.
-     * @throws IOException if the image format is not supported or can not be decoded.
-     * @hide
-     */
-    public static LargeBitmap createLargeBitmap(byte[] data,
-            int offset, int length, boolean isShareable) throws IOException {
-        if ((offset | length) < 0 || data.length < offset + length) {
-            throw new ArrayIndexOutOfBoundsException();
-        }
-        return nativeCreateLargeBitmap(data, offset, length, isShareable);
-    }
-
-    /**
-     * Create a LargeBitmap from the file descriptor.
-     * The position within the descriptor will not be changed when
-     * this returns, so the descriptor can be used again as is.
-     * Currently only the Jpeg format is supported.
-     *
-     * @param fd The file descriptor containing the data to decode
-     * @param isShareable If this is true, then the LargeBitmap may keep a
-     *                    shallow reference to the input. If this is false,
-     *                    then the LargeBitmap will explicitly make a copy of the
-     *                    input data, and keep that. Even if sharing is allowed,
-     *                    the implementation may still decide to make a deep
-     *                    copy of the input data. If an image is progressively encoded,
-     *                    allowing sharing may degrade the decoding speed.
-     * @return LargeBitmap, or null if the image data could not be decoded.
-     * @throws IOException if the image format is not supported or can not be decoded.
-     * @hide
-     */
-    public static LargeBitmap createLargeBitmap(
-            FileDescriptor fd, boolean isShareable) throws IOException {
-        return nativeCreateLargeBitmap(fd, isShareable);
-    }
-
-    /**
-     * Create a LargeBitmap from an input stream.
-     * The stream's position will be where ever it was after the encoded data
-     * was read.
-     * Currently only the Jpeg format is supported.
-     *
-     * @param is The input stream that holds the raw data to be decoded into a
-     *           LargeBitmap.
-     * @param isShareable If this is true, then the LargeBitmap may keep a
-     *                    shallow reference to the input. If this is false,
-     *                    then the LargeBitmap will explicitly make a copy of the
-     *                    input data, and keep that. Even if sharing is allowed,
-     *                    the implementation may still decide to make a deep
-     *                    copy of the input data. If an image is progressively encoded,
-     *                    allowing sharing may degrade the decoding speed.
-     * @return LargeBitmap, or null if the image data could not be decoded.
-     * @throws IOException if the image format is not supported or can not be decoded.
-     * @hide
-     */
-    public static LargeBitmap createLargeBitmap(InputStream is,
-            boolean isShareable) throws IOException {
-        // we need mark/reset to work properly in JNI
-
-        if (!is.markSupported()) {
-            is = new BufferedInputStream(is, 16 * 1024);
-        }
-
-        if (is instanceof AssetManager.AssetInputStream) {
-            return nativeCreateLargeBitmap(
-                    ((AssetManager.AssetInputStream) is).getAssetInt(),
-                    isShareable);
-        } else {
-            // pass some temp storage down to the native code. 1024 is made up,
-            // but should be large enough to avoid too many small calls back
-            // into is.read(...).
-            byte [] tempStorage = new byte[16 * 1024];
-            return nativeCreateLargeBitmap(is, tempStorage, isShareable);
-        }
-    }
-
-    /**
-     * Create a LargeBitmap from a file path.
-     * Currently only the Jpeg format is supported.
-     *
-     * @param pathName complete path name for the file to be decoded.
-     * @param isShareable If this is true, then the LargeBitmap may keep a
-     *                    shallow reference to the input. If this is false,
-     *                    then the LargeBitmap will explicitly make a copy of the
-     *                    input data, and keep that. Even if sharing is allowed,
-     *                    the implementation may still decide to make a deep
-     *                    copy of the input data. If an image is progressively encoded,
-     *                    allowing sharing may degrade the decoding speed.
-     * @return LargeBitmap, or null if the image data could not be decoded.
-     * @throws IOException if the image format is not supported or can not be decoded.
-     * @hide
-     */
-    public static LargeBitmap createLargeBitmap(String pathName, boolean isShareable)
-            throws IOException {
-        LargeBitmap bm = null;
-        InputStream stream = null;
-
-        try {
-            stream = new FileInputStream(pathName);
-            bm = createLargeBitmap(stream, isShareable);
-        } finally {
-            if (stream != null) {
-                try {
-                    stream.close();
-                } catch (IOException e) {
-                    // do nothing here
-                }
-            }
-        }
-        return bm;
-    }
-
     private static native void nativeSetDefaultConfig(int nativeConfig);
     private static native Bitmap nativeDecodeStream(InputStream is, byte[] storage,
             Rect padding, Options opts);
@@ -701,14 +575,4 @@
     private static native Bitmap nativeDecodeByteArray(byte[] data, int offset,
             int length, Options opts);
     private static native byte[] nativeScaleNinePatch(byte[] chunk, float scale, Rect pad);
-
-    private static native LargeBitmap nativeCreateLargeBitmap(
-            byte[] data, int offset, int length, boolean isShareable);
-    private static native LargeBitmap nativeCreateLargeBitmap(
-            FileDescriptor fd, boolean isShareable);
-    private static native LargeBitmap nativeCreateLargeBitmap(
-            InputStream is, byte[] storage, boolean isShareable);
-    private static native LargeBitmap nativeCreateLargeBitmap(
-            int asset, boolean isShareable);
 }
-
diff --git a/graphics/java/android/graphics/BitmapRegionDecoder.java b/graphics/java/android/graphics/BitmapRegionDecoder.java
new file mode 100644
index 0000000..454eb4a
--- /dev/null
+++ b/graphics/java/android/graphics/BitmapRegionDecoder.java
@@ -0,0 +1,263 @@
+/* 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.graphics;
+
+import android.content.res.AssetManager;
+
+import java.io.BufferedInputStream;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * BitmapRegionDecoder can be used to decode a rectangle region from an image.
+ * BitmapRegionDecoder is particularly useful when an original image is large and
+ * you only need parts of the image.
+ *
+ * <p>To create a BitmapRegionDecoder, call newInstance(...).
+ * Given a BitmapRegionDecoder, users can call decodeRegion() repeatedly
+ * to get a decoded Bitmap of the specified region.
+ *
+ */
+public final class BitmapRegionDecoder {
+    private int mNativeBitmapRegionDecoder;
+    private boolean mRecycled;
+
+    /**
+     * Create a BitmapRegionDecoder from the specified byte array.
+     * Currently only the Jpeg format is supported.
+     *
+     * @param data byte array of compressed image data.
+     * @param offset offset into data for where the decoder should begin
+     *               parsing.
+     * @param length the number of bytes, beginning at offset, to parse
+     * @param isShareable If this is true, then the BitmapRegionDecoder may keep a
+     *                    shallow reference to the input. If this is false,
+     *                    then the BitmapRegionDecoder will explicitly make a copy of the
+     *                    input data, and keep that. Even if sharing is allowed,
+     *                    the implementation may still decide to make a deep
+     *                    copy of the input data. If an image is progressively encoded,
+     *                    allowing sharing may degrade the decoding speed.
+     * @return BitmapRegionDecoder, or null if the image data could not be decoded.
+     * @throws IOException if the image format is not supported or can not be decoded.
+     */
+    public static BitmapRegionDecoder newInstance(byte[] data,
+            int offset, int length, boolean isShareable) throws IOException {
+        if ((offset | length) < 0 || data.length < offset + length) {
+            throw new ArrayIndexOutOfBoundsException();
+        }
+        return nativeNewInstance(data, offset, length, isShareable);
+    }
+
+    /**
+     * Create a BitmapRegionDecoder from the file descriptor.
+     * The position within the descriptor will not be changed when
+     * this returns, so the descriptor can be used again as is.
+     * Currently only the Jpeg format is supported.
+     *
+     * @param fd The file descriptor containing the data to decode
+     * @param isShareable If this is true, then the BitmapRegionDecoder may keep a
+     *                    shallow reference to the input. If this is false,
+     *                    then the BitmapRegionDecoder will explicitly make a copy of the
+     *                    input data, and keep that. Even if sharing is allowed,
+     *                    the implementation may still decide to make a deep
+     *                    copy of the input data. If an image is progressively encoded,
+     *                    allowing sharing may degrade the decoding speed.
+     * @return BitmapRegionDecoder, or null if the image data could not be decoded.
+     * @throws IOException if the image format is not supported or can not be decoded.
+     */
+    public static BitmapRegionDecoder newInstance(
+            FileDescriptor fd, boolean isShareable) throws IOException {
+        return nativeNewInstance(fd, isShareable);
+    }
+
+    /**
+     * Create a BitmapRegionDecoder from an input stream.
+     * The stream's position will be where ever it was after the encoded data
+     * was read.
+     * Currently only the Jpeg format is supported.
+     *
+     * @param is The input stream that holds the raw data to be decoded into a
+     *           BitmapRegionDecoder.
+     * @param isShareable If this is true, then the BitmapRegionDecoder may keep a
+     *                    shallow reference to the input. If this is false,
+     *                    then the BitmapRegionDecoder will explicitly make a copy of the
+     *                    input data, and keep that. Even if sharing is allowed,
+     *                    the implementation may still decide to make a deep
+     *                    copy of the input data. If an image is progressively encoded,
+     *                    allowing sharing may degrade the decoding speed.
+     * @return BitmapRegionDecoder, or null if the image data could not be decoded.
+     * @throws IOException if the image format is not supported or can not be decoded.
+     */
+    public static BitmapRegionDecoder newInstance(InputStream is,
+            boolean isShareable) throws IOException {
+        // we need mark/reset to work properly in JNI
+
+        if (!is.markSupported()) {
+            is = new BufferedInputStream(is, 16 * 1024);
+        }
+
+        if (is instanceof AssetManager.AssetInputStream) {
+            return nativeNewInstance(
+                    ((AssetManager.AssetInputStream) is).getAssetInt(),
+                    isShareable);
+        } else {
+            // pass some temp storage down to the native code. 1024 is made up,
+            // but should be large enough to avoid too many small calls back
+            // into is.read(...).
+            byte [] tempStorage = new byte[16 * 1024];
+            return nativeNewInstance(is, tempStorage, isShareable);
+        }
+    }
+
+    /**
+     * Create a BitmapRegionDecoder from a file path.
+     * Currently only the Jpeg format is supported.
+     *
+     * @param pathName complete path name for the file to be decoded.
+     * @param isShareable If this is true, then the BitmapRegionDecoder may keep a
+     *                    shallow reference to the input. If this is false,
+     *                    then the BitmapRegionDecoder will explicitly make a copy of the
+     *                    input data, and keep that. Even if sharing is allowed,
+     *                    the implementation may still decide to make a deep
+     *                    copy of the input data. If an image is progressively encoded,
+     *                    allowing sharing may degrade the decoding speed.
+     * @return BitmapRegionDecoder, or null if the image data could not be decoded.
+     * @throws IOException if the image format is not supported or can not be decoded.
+     */
+    public static BitmapRegionDecoder newInstance(String pathName,
+            boolean isShareable) throws IOException {
+        BitmapRegionDecoder decoder = null;
+        InputStream stream = null;
+
+        try {
+            stream = new FileInputStream(pathName);
+            decoder = newInstance(stream, isShareable);
+        } finally {
+            if (stream != null) {
+                try {
+                    stream.close();
+                } catch (IOException e) {
+                    // do nothing here
+                }
+            }
+        }
+        return decoder;
+    }
+
+    /*  Private constructor that must receive an already allocated native
+        region decoder int (pointer).
+
+        This can be called from JNI code.
+    */
+    private BitmapRegionDecoder(int decoder) {
+        mNativeBitmapRegionDecoder = decoder;
+        mRecycled = false;
+    }
+
+    /**
+     * Decodes a rectangle region in the image specified by rect.
+     *
+     * @param rect The rectangle that specified the region to be decode.
+     * @param options null-ok; Options that control downsampling.
+     *             inPurgeable is not supported.
+     * @return The decoded bitmap, or null if the image data could not be
+     *         decoded.
+     */
+    public Bitmap decodeRegion(Rect rect, BitmapFactory.Options options) {
+        checkRecycled("decodeRegion called on recycled region decoder");
+        if (rect.left < 0 || rect.top < 0 || rect.right > getWidth()
+                || rect.bottom > getHeight())
+            throw new IllegalArgumentException("rectangle is not inside the image");
+        return nativeDecodeRegion(mNativeBitmapRegionDecoder, rect.left, rect.top,
+                rect.right - rect.left, rect.bottom - rect.top, options);
+    }
+
+    /** Returns the original image's width */
+    public int getWidth() {
+        checkRecycled("getWidth called on recycled region decoder");
+        return nativeGetWidth(mNativeBitmapRegionDecoder);
+    }
+
+    /** Returns the original image's height */
+    public int getHeight() {
+        checkRecycled("getHeight called on recycled region decoder");
+        return nativeGetHeight(mNativeBitmapRegionDecoder);
+    }
+
+    /**
+     * Frees up the memory associated with this region decoder, and mark the
+     * region decoder as "dead", meaning it will throw an exception if decodeRegion(),
+     * getWidth() or getHeight() is called.
+     *
+     * <p>This operation cannot be reversed, so it should only be called if you are
+     * sure there are no further uses for the region decoder. This is an advanced call,
+     * and normally need not be called, since the normal GC process will free up this
+     * memory when there are no more references to this region decoder.
+     */
+    public void recycle() {
+        if (!mRecycled) {
+            nativeClean(mNativeBitmapRegionDecoder);
+            mRecycled = true;
+        }
+    }
+
+    /**
+     * Returns true if this region decoder has been recycled.
+     * If so, then it is an error to try use its method.
+     *
+     * @return true if the region decoder has been recycled
+     */
+    public final boolean isRecycled() {
+        return mRecycled;
+    }
+
+    /**
+     * Called by methods that want to throw an exception if the region decoder
+     * has already been recycled.
+     */
+    private void checkRecycled(String errorMessage) {
+        if (mRecycled) {
+            throw new IllegalStateException(errorMessage);
+        }
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            recycle();
+        } finally {
+            super.finalize();
+        }
+    }
+
+    private static native Bitmap nativeDecodeRegion(int lbm,
+            int start_x, int start_y, int width, int height,
+            BitmapFactory.Options options);
+    private static native int nativeGetWidth(int lbm);
+    private static native int nativeGetHeight(int lbm);
+    private static native void nativeClean(int lbm);
+
+    private static native BitmapRegionDecoder nativeNewInstance(
+            byte[] data, int offset, int length, boolean isShareable);
+    private static native BitmapRegionDecoder nativeNewInstance(
+            FileDescriptor fd, boolean isShareable);
+    private static native BitmapRegionDecoder nativeNewInstance(
+            InputStream is, byte[] storage, boolean isShareable);
+    private static native BitmapRegionDecoder nativeNewInstance(
+            int asset, boolean isShareable);
+}
diff --git a/graphics/java/android/renderscript/Mesh.java b/graphics/java/android/renderscript/Mesh.java
index b74c1f8..d36b2f1 100644
--- a/graphics/java/android/renderscript/Mesh.java
+++ b/graphics/java/android/renderscript/Mesh.java
@@ -69,7 +69,7 @@
         int[] primitives = new int[idxCount];
 
         mRS.nMeshGetVertices(mID, vtxIDs, vtxCount);
-        mRS.nMeshGetIndices(mID, idxIDs, primitives, vtxCount);
+        mRS.nMeshGetIndices(mID, idxIDs, primitives, idxCount);
 
         mVertexBuffers = new Allocation[vtxCount];
         mIndexBuffers = new Allocation[idxCount];
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index 9c67885..5ef66f3 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -31,6 +31,7 @@
 #include "ProgramCache.h"
 #include "PathCache.h"
 #include "TextDropShadowCache.h"
+#include "Line.h"
 
 namespace android {
 namespace uirenderer {
@@ -64,6 +65,8 @@
     PatchCache patchCache;
     TextDropShadowCache dropShadowCache;
     GammaFontRenderer fontRenderer;
+
+    Line line;
 }; // class Caches
 
 }; // namespace uirenderer
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index c527038..60523db 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -82,6 +82,10 @@
      * Indicates whether this layer should be blended.
      */
     bool blend;
+    /**
+     * Indicates that this layer has never been used before.
+     */
+    bool empty;
 }; // struct Layer
 
 }; // namespace uirenderer
diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp
index 39c3111..2770868 100644
--- a/libs/hwui/LayerCache.cpp
+++ b/libs/hwui/LayerCache.cpp
@@ -109,18 +109,16 @@
 
         layer = new Layer;
         layer->blend = true;
+        layer->empty = true;
 
         glGenTextures(1, &layer->texture);
         glBindTexture(GL_TEXTURE_2D, layer->texture);
 
-        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
 
         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-
-        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.width, size.height, 0,
-                GL_RGBA, GL_UNSIGNED_BYTE, NULL);
     }
 
     return layer;
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 02b1425..e032085 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -133,6 +133,8 @@
 
 OpenGLRenderer::~OpenGLRenderer() {
     LOGD("Destroy OpenGLRenderer");
+    // The context has already been destroyed at this point, do not call
+    // GL APIs. All GL state should be kept in Caches.h
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -379,8 +381,15 @@
 
     // Copy the framebuffer into the layer
     glBindTexture(GL_TEXTURE_2D, layer->texture);
-    glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bounds.left, mHeight - bounds.bottom,
-            bounds.getWidth(), bounds.getHeight(), 0);
+
+    if (layer->empty) {
+        glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bounds.left, mHeight - bounds.bottom,
+                bounds.getWidth(), bounds.getHeight(), 0);
+        layer->empty = false;
+    } else {
+        glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bounds.left, mHeight - bounds.bottom,
+                bounds.getWidth(), bounds.getHeight());
+    }
 
     if (flags & SkCanvas::kClipToLayer_SaveFlag) {
         if (mSnapshot->clipTransformed(bounds)) setScissorFromClip();
@@ -615,14 +624,14 @@
     const bool isAA = paint->isAntiAlias();
     if (isAA) {
         GLuint textureUnit = 0;
-        setupTextureAlpha8(mLine.getTexture(), 0, 0, textureUnit, 0.0f, 0.0f, r, g, b, a,
-                mode, false, true, mLine.getVertices(), mLine.getTexCoords());
+        setupTextureAlpha8(mCaches.line.getTexture(), 0, 0, textureUnit, 0.0f, 0.0f, r, g, b, a,
+                mode, false, true, mCaches.line.getVertices(), mCaches.line.getTexCoords());
     } else {
         setupColorRect(0.0f, 0.0f, 1.0f, 1.0f, r, g, b, a, mode, false);
     }
 
     const float strokeWidth = paint->getStrokeWidth();
-    const GLsizei elementsCount = isAA ? mLine.getElementsCount() : gMeshCount;
+    const GLsizei elementsCount = isAA ? mCaches.line.getElementsCount() : gMeshCount;
     const GLenum drawMode = isAA ? GL_TRIANGLES : GL_TRIANGLE_STRIP;
 
     for (int i = 0; i < count; i += 4) {
@@ -630,7 +639,7 @@
         float ty = 0.0f;
 
         if (isAA) {
-            mLine.update(points[i], points[i + 1], points[i + 2], points[i + 3],
+            mCaches.line.update(points[i], points[i + 1], points[i + 2], points[i + 3],
                     strokeWidth, tx, ty);
         } else {
             ty = strokeWidth <= 1.0f ? 0.0f : -strokeWidth * 0.5f;
@@ -647,7 +656,8 @@
         }
         mModelView.translate(tx, ty, 0.0f);
         if (!isAA) {
-            float length = mLine.getLength(points[i], points[i + 1], points[i + 2], points[i + 3]);
+            float length = mCaches.line.getLength(points[i], points[i + 1],
+                    points[i + 2], points[i + 3]);
             mModelView.scale(length, strokeWidth, 1.0f);
         }
         mCaches.currentProgram->set(mOrthoMatrix, mModelView, *mSnapshot->transform);
@@ -659,7 +669,9 @@
         glDrawArrays(drawMode, 0, elementsCount);
     }
 
-    glDisableVertexAttribArray(mCaches.currentProgram->getAttrib("texCoords"));
+    if (isAA) {
+        glDisableVertexAttribArray(mCaches.currentProgram->getAttrib("texCoords"));
+    }
 }
 
 void OpenGLRenderer::drawColor(int color, SkXfermode::Mode mode) {
@@ -956,25 +968,36 @@
         }
 
         if (underlineWidth > 0.0f) {
-            float textSize = paint->getTextSize();
-            float height = textSize * kStdUnderline_Thickness;
+            const float textSize = paint->getTextSize();
+            const float strokeWidth = textSize * kStdUnderline_Thickness;
 
-            float left = x - offsetX;
+            const float left = x - offsetX;
             float top = 0.0f;
-            float right = left + underlineWidth;
-            float bottom = 0.0f;
+
+            const int pointsCount = 4 * (flags & SkPaint::kStrikeThruText_Flag ? 2 : 1);
+            float points[pointsCount];
+            int currentPoint = 0;
 
             if (flags & SkPaint::kUnderlineText_Flag) {
                 top = y + textSize * kStdUnderline_Offset;
-                bottom = top + height;
-                drawRect(left, top, right, bottom, paint);
+                points[currentPoint++] = left;
+                points[currentPoint++] = top;
+                points[currentPoint++] = left + underlineWidth;
+                points[currentPoint++] = top;
             }
 
             if (flags & SkPaint::kStrikeThruText_Flag) {
                 top = y + textSize * kStdStrikeThru_Offset;
-                bottom = top + height;
-                drawRect(left, top, right, bottom, paint);
+                points[currentPoint++] = left;
+                points[currentPoint++] = top;
+                points[currentPoint++] = left + underlineWidth;
+                points[currentPoint++] = top;
             }
+
+            SkPaint linesPaint(*paint);
+            linesPaint.setStrokeWidth(strokeWidth);
+
+            drawLines(&points[0], pointsCount, &linesPaint);
         }
     }
 }
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index eba0f41..f903505 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -40,7 +40,6 @@
 #include "SkiaShader.h"
 #include "SkiaColorFilter.h"
 #include "Caches.h"
-#include "Line.h"
 
 namespace android {
 namespace uirenderer {
@@ -420,9 +419,6 @@
     // List of rectangles to clear due to calls to saveLayer()
     Vector<Rect*> mLayers;
 
-    // Single object used to draw lines
-    Line mLine;
-
     // Misc
     GLint mMaxTextureSize;
 
diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp
index 9e1f6c2..165c0da 100644
--- a/libs/hwui/SkiaShader.cpp
+++ b/libs/hwui/SkiaShader.cpp
@@ -94,6 +94,11 @@
         description.isBitmapNpot = true;
         description.bitmapWrapS = gTileModes[mTileX];
         description.bitmapWrapT = gTileModes[mTileY];
+        mWrapS = GL_CLAMP_TO_EDGE;
+        mWrapT = GL_CLAMP_TO_EDGE;
+    } else {
+        mWrapS = gTileModes[mTileX];
+        mWrapT = gTileModes[mTileY];
     }
 }
 
@@ -121,7 +126,7 @@
     }
 
     // Uniforms
-    bindTexture(texture->id, gTileModes[mTileX], gTileModes[mTileY], textureSlot);
+    bindTexture(texture->id, mWrapS, mWrapT, textureSlot);
     glUniform1i(program->getUniform("bitmapSampler"), textureSlot);
     glUniformMatrix4fv(program->getUniform("textureTransform"), 1,
             GL_FALSE, &textureTransform.data[0]);
diff --git a/libs/hwui/SkiaShader.h b/libs/hwui/SkiaShader.h
index 0023c46..9f8778f 100644
--- a/libs/hwui/SkiaShader.h
+++ b/libs/hwui/SkiaShader.h
@@ -122,6 +122,8 @@
 
     SkBitmap* mBitmap;
     const Texture* mTexture;
+    GLenum mWrapS;
+    GLenum mWrapT;
 }; // struct SkiaBitmapShader
 
 /**
diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp
index 927070a..adf6ee2 100644
--- a/libs/hwui/TextureCache.cpp
+++ b/libs/hwui/TextureCache.cpp
@@ -159,32 +159,35 @@
         return;
     }
 
-    if (!regenerate) {
-        texture->generation = bitmap->getGenerationID();
-        texture->width = bitmap->width();
-        texture->height = bitmap->height();
+    const bool resize = !regenerate || bitmap->width() != int(texture->width) ||
+            bitmap->height() != int(texture->height);
 
+    if (!regenerate) {
         glGenTextures(1, &texture->id);
     }
 
+    texture->generation = bitmap->getGenerationID();
+    texture->width = bitmap->width();
+    texture->height = bitmap->height();
+
     glBindTexture(GL_TEXTURE_2D, texture->id);
     glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel());
 
     switch (bitmap->getConfig()) {
     case SkBitmap::kA8_Config:
-        texture->blend = true;
         glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
-        glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, bitmap->rowBytesAsPixels(), texture->height, 0,
-                GL_ALPHA, GL_UNSIGNED_BYTE, bitmap->getPixels());
+        uploadToTexture(resize, GL_ALPHA, bitmap->rowBytesAsPixels(), texture->height,
+                GL_UNSIGNED_BYTE, bitmap->getPixels());
+        texture->blend = true;
         break;
     case SkBitmap::kRGB_565_Config:
+        uploadToTexture(resize, GL_RGB, bitmap->rowBytesAsPixels(), texture->height,
+                GL_UNSIGNED_SHORT_5_6_5, bitmap->getPixels());
         texture->blend = false;
-        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, bitmap->rowBytesAsPixels(), texture->height, 0,
-                GL_RGB, GL_UNSIGNED_SHORT_5_6_5, bitmap->getPixels());
         break;
     case SkBitmap::kARGB_8888_Config:
-        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bitmap->rowBytesAsPixels(), texture->height, 0,
-                GL_RGBA, GL_UNSIGNED_BYTE, bitmap->getPixels());
+        uploadToTexture(resize, GL_RGBA, bitmap->rowBytesAsPixels(), texture->height,
+                GL_UNSIGNED_BYTE, bitmap->getPixels());
         // Do this after calling getPixels() to make sure Skia's deferred
         // decoding happened
         texture->blend = !bitmap->isOpaque();
@@ -201,5 +204,14 @@
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 }
 
+void TextureCache::uploadToTexture(bool resize, GLenum format, GLsizei width, GLsizei height,
+        GLenum type, const GLvoid * data) {
+    if (resize) {
+        glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, data);
+    } else {
+        glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, data);
+    }
+}
+
 }; // namespace uirenderer
 }; // namespace android
diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h
index a63789a..7cf66d9 100644
--- a/libs/hwui/TextureCache.h
+++ b/libs/hwui/TextureCache.h
@@ -79,6 +79,9 @@
      */
     void generateTexture(SkBitmap* bitmap, Texture* texture, bool regenerate = false);
 
+    void uploadToTexture(bool resize, GLenum format, GLsizei width, GLsizei height,
+            GLenum type, const GLvoid * data);
+
     void init();
 
     GenerationCache<SkBitmap*, Texture*> mCache;
diff --git a/libs/rs/rs.spec b/libs/rs/rs.spec
index 31d70c7..21cbc50 100644
--- a/libs/rs/rs.spec
+++ b/libs/rs/rs.spec
@@ -354,10 +354,6 @@
 	}
 
 
-ScriptCSetScript {
-	param void * codePtr
-	}
-
 ScriptCSetText {
 	param const char * text
 	param uint32_t length
diff --git a/libs/rs/rsMesh.cpp b/libs/rs/rsMesh.cpp
index 810e4ff..8e43f24 100644
--- a/libs/rs/rsMesh.cpp
+++ b/libs/rs/rsMesh.cpp
@@ -124,8 +124,6 @@
             mPrimitives[ct]->mIndexBuffer->deferedUploadToBufferObject(rsc);
         }
     }
-
-    rsc->checkError("Mesh::uploadAll");
 }
 
 void Mesh::updateGLPrimitives()
diff --git a/libs/rs/rsProgramFragment.cpp b/libs/rs/rsProgramFragment.cpp
index 9817fca..8f5c653 100644
--- a/libs/rs/rsProgramFragment.cpp
+++ b/libs/rs/rsProgramFragment.cpp
@@ -78,8 +78,6 @@
     mConstantColor[2] = 1.f;
     mConstantColor[3] = 1.f;
 
-    LOGE("Custom FP");
-
     mTextureEnableMask = (1 << mTextureCount) -1;
 
     init(rsc);
diff --git a/libs/rs/rsScriptC.cpp b/libs/rs/rsScriptC.cpp
index cbc5df9..4b484b1 100644
--- a/libs/rs/rsScriptC.cpp
+++ b/libs/rs/rsScriptC.cpp
@@ -67,12 +67,15 @@
             ptr = mSlots[ct]->getPtr();
         }
         void **dest = ((void ***)mEnviroment.mFieldAddress)[ct];
-        //LOGE("setupScript %i %p = %p    %p %i", ct, dest, ptr, mSlots[ct]->getType(), mSlots[ct]->getType()->getDimX());
 
-        //const uint32_t *p32 = (const uint32_t *)ptr;
-        //for (uint32_t ct2=0; ct2 < mSlots[ct]->getType()->getDimX(); ct2++) {
-            //LOGE("  %i = 0x%08x ", ct2, p32[ct2]);
-        //}
+        if (rsc->props.mLogScripts) {
+            LOGV("%p ScriptC::setupScript slot=%i  dst=%p  src=%p  type=%p", rsc, ct, dest, ptr, mSlots[ct]->getType());
+
+            //const uint32_t *p32 = (const uint32_t *)ptr;
+            //for (uint32_t ct2=0; ct2 < mSlots[ct]->getType()->getDimX(); ct2++) {
+                //LOGE("  %i = 0x%08x ", ct2, p32[ct2]);
+            //}
+        }
 
         if (dest) {
             *dest = ptr;
@@ -136,10 +139,18 @@
 
     uint32_t ret = 0;
     Script * oldTLS = setTLS(this);
-    //LOGE("ScriptC::run %p", mProgram.mRoot);
+
+    if (rsc->props.mLogScripts) {
+        LOGV("%p ScriptC::run invoking root,  ptr %p", rsc, mProgram.mRoot);
+    }
+
     ret = mProgram.mRoot();
+
+    if (rsc->props.mLogScripts) {
+        LOGV("%p ScriptC::run invoking complete, ret=%i", rsc, ret);
+    }
+
     setTLS(oldTLS);
-    //LOGE("ScriptC::run ret %i", ret);
     return ret;
 }
 
@@ -325,8 +336,14 @@
     setupScript(rsc);
     Script * oldTLS = setTLS(this);
 
+    if (rsc->props.mLogScripts) {
+        LOGV("%p ScriptC::Invoke invoking slot %i,  ptr %p", rsc, slot, mEnviroment.mInvokeFunctions[slot]);
+    }
     ((void (*)(const void *, uint32_t))
         mEnviroment.mInvokeFunctions[slot])(data, len);
+    if (rsc->props.mLogScripts) {
+        LOGV("%p ScriptC::Invoke complete", rsc);
+    }
 
     setTLS(oldTLS);
 }
@@ -377,7 +394,7 @@
 
 void ScriptCState::runCompiler(Context *rsc, ScriptC *s)
 {
-    LOGV("ScriptCState::runCompiler ");
+    LOGV("%p ScriptCState::runCompiler ", rsc);
 
     s->mBccScript = bccCreateScript();
     s->mEnviroment.mIsThreadable = true;
@@ -386,7 +403,7 @@
     bccCompileScript(s->mBccScript);
     bccGetScriptLabel(s->mBccScript, "root", (BCCvoid**) &s->mProgram.mRoot);
     bccGetScriptLabel(s->mBccScript, "init", (BCCvoid**) &s->mProgram.mInit);
-    LOGV("root %p,  init %p", s->mProgram.mRoot, s->mProgram.mInit);
+    LOGV("%p ScriptCState::runCompiler root %p,  init %p", rsc, s->mProgram.mRoot, s->mProgram.mInit);
 
     if (s->mProgram.mInit) {
         s->mProgram.mInit();
@@ -407,9 +424,6 @@
         s->mEnviroment.mFieldAddress = (void **) calloc(s->mEnviroment.mFieldCount, sizeof(void *));
         bccGetExportVars(s->mBccScript, NULL, s->mEnviroment.mFieldCount, (BCCvoid **) s->mEnviroment.mFieldAddress);
     }
-    //for (int ct2=0; ct2 < s->mEnviroment.mFieldCount; ct2++ ) {
-        //LOGE("Script field %i = %p", ct2, s->mEnviroment.mFieldAddress[ct2]);
-    //}
 
     s->mEnviroment.mFragment.set(rsc->getDefaultProgramFragment());
     s->mEnviroment.mVertex.set(rsc->getDefaultProgramVertex());
@@ -491,13 +505,6 @@
     ss->clear();
 }
 
-void rsi_ScriptCSetScript(Context * rsc, void *vp)
-{
-    rsAssert(0);
-    //ScriptCState *ss = &rsc->mScriptC;
-    //ss->mProgram.mScript = reinterpret_cast<ScriptC::RunScript_t>(vp);
-}
-
 void rsi_ScriptCSetText(Context *rsc, const char *text, uint32_t len)
 {
     ScriptCState *ss = &rsc->mScriptC;
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index b2a0a46..f72eaa4 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -644,8 +644,9 @@
             } else if (MediaFile.isAudioFileType(mFileType)) {
                 map.put(Audio.Media.ARTIST, (mArtist != null && mArtist.length() > 0) ?
                         mArtist : MediaStore.UNKNOWN_STRING);
-                map.put(Audio.Media.ALBUM_ARTIST, (mAlbumArtist != null &&
-                        mAlbumArtist.length() > 0) ? mAlbumArtist : null);
+// disable album artist support until MediaProvider really supports it
+//                map.put(Audio.Media.ALBUM_ARTIST, (mAlbumArtist != null &&
+//                        mAlbumArtist.length() > 0) ? mAlbumArtist : null);
                 map.put(Audio.Media.ALBUM, (mAlbum != null && mAlbum.length() > 0) ?
                         mAlbum : MediaStore.UNKNOWN_STRING);
                 map.put(Audio.Media.COMPOSER, mComposer);
@@ -695,7 +696,7 @@
                 }
             }
             long rowId = entry.mRowId;
-            if (MediaFile.isAudioFileType(mFileType) && rowId == 0) {
+            if (MediaFile.isAudioFileType(mFileType) && (rowId == 0 || mMtpObjectHandle != 0)) {
                 // Only set these for new entries. For existing entries, they
                 // may have been modified later, and we want to keep the current
                 // values so that custom ringtones still show up in the ringtone
@@ -927,136 +928,6 @@
                     c = null;
                 }
             }
-
-            // Read existing files from the audio table and update FileCacheEntry
-            c = mMediaProvider.query(mAudioUri, MEDIA_PRESCAN_PROJECTION,
-                    where, selectionArgs, null);
-            if (c != null) {
-                while (c.moveToNext()) {
-                    long rowId = c.getLong(MEDIA_PRESCAN_ID_COLUMN_INDEX);
-                    String path = c.getString(MEDIA_PRESCAN_PATH_COLUMN_INDEX);
-                    long lastModified = c.getLong(MEDIA_PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
-
-                    // Only consider entries with absolute path names.
-                    // This allows storing URIs in the database without the
-                    // media scanner removing them.
-                    if (path.startsWith("/")) {
-                        String key = path;
-                        if (mCaseInsensitivePaths) {
-                            key = path.toLowerCase();
-                        }
-                        FileCacheEntry entry = mFileCache.get(path);
-                        if (entry == null) {
-                            mFileCache.put(key, new FileCacheEntry(mAudioUri, rowId, path,
-                                    lastModified, 0));
-                        } else {
-                            // update the entry
-                            entry.mTableUri = mAudioUri;
-                            entry.mRowId = rowId;
-                        }
-                    }
-                }
-                c.close();
-                c = null;
-            }
-
-            // Read existing files from the video table and update FileCacheEntry
-            c = mMediaProvider.query(mVideoUri, MEDIA_PRESCAN_PROJECTION,
-                    where, selectionArgs, null);
-            if (c != null) {
-                while (c.moveToNext()) {
-                    long rowId = c.getLong(MEDIA_PRESCAN_ID_COLUMN_INDEX);
-                    String path = c.getString(MEDIA_PRESCAN_PATH_COLUMN_INDEX);
-                    long lastModified = c.getLong(MEDIA_PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
-
-                    // Only consider entries with absolute path names.
-                    // This allows storing URIs in the database without the
-                    // media scanner removing them.
-                    if (path.startsWith("/")) {
-                        String key = path;
-                        if (mCaseInsensitivePaths) {
-                            key = path.toLowerCase();
-                        }
-                        FileCacheEntry entry = mFileCache.get(path);
-                        if (entry == null) {
-                            mFileCache.put(key, new FileCacheEntry(mVideoUri, rowId, path,
-                                    lastModified, 0));
-                        } else {
-                            // update the entry
-                            entry.mTableUri = mVideoUri;
-                            entry.mRowId = rowId;
-                        }
-                    }
-                }
-                c.close();
-                c = null;
-            }
-
-            // Read existing files from the video table and update FileCacheEntry
-            c = mMediaProvider.query(mImagesUri, MEDIA_PRESCAN_PROJECTION,
-                    where, selectionArgs, null);
-            if (c != null) {
-                while (c.moveToNext()) {
-                    long rowId = c.getLong(MEDIA_PRESCAN_ID_COLUMN_INDEX);
-                    String path = c.getString(MEDIA_PRESCAN_PATH_COLUMN_INDEX);
-                    long lastModified = c.getLong(MEDIA_PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
-
-                    // Only consider entries with absolute path names.
-                    // This allows storing URIs in the database without the
-                    // media scanner removing them.
-                    if (path.startsWith("/")) {
-                        String key = path;
-                        if (mCaseInsensitivePaths) {
-                            key = path.toLowerCase();
-                        }
-                        FileCacheEntry entry = mFileCache.get(path);
-                        if (entry == null) {
-                            mFileCache.put(key, new FileCacheEntry(mImagesUri, rowId, path,
-                                    lastModified, 0));
-                        } else {
-                            // update the entry
-                            entry.mTableUri = mImagesUri;
-                            entry.mRowId = rowId;
-                        }
-                    }
-                }
-                c.close();
-                c = null;
-            }
-
-            if (mProcessPlaylists) {
-                // Read existing files from the playlists table and update FileCacheEntry
-                c = mMediaProvider.query(mPlaylistsUri, MEDIA_PRESCAN_PROJECTION,
-                        where, selectionArgs, null);
-                if (c != null) {
-                    while (c.moveToNext()) {
-                        long rowId = c.getLong(MEDIA_PRESCAN_ID_COLUMN_INDEX);
-                        String path = c.getString(MEDIA_PRESCAN_PATH_COLUMN_INDEX);
-                        long lastModified = c.getLong(MEDIA_PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
-
-                        // Only consider entries with absolute path names.
-                        // This allows storing URIs in the database without the
-                        // media scanner removing them.
-                        if (path.startsWith("/")) {
-                            String key = path;
-                            if (mCaseInsensitivePaths) {
-                                key = path.toLowerCase();
-                            }
-                            FileCacheEntry entry = mFileCache.get(path);
-                            if (entry == null) {
-                                mFileCache.put(key, new FileCacheEntry(mPlaylistsUri, rowId, path,
-                                        lastModified, 0));
-                            } else {
-                                // update the entry
-                                entry.mTableUri = mPlaylistsUri;
-                                entry.mRowId = rowId;
-                            }
-                        }
-                    }
-                    c.close();
-                    c = null;
-                }
-            }
         }
         finally {
             if (c != null) {
diff --git a/media/java/android/media/MtpClient.java b/media/java/android/media/MtpClient.java
index f7c0ce2..98da1f6 100644
--- a/media/java/android/media/MtpClient.java
+++ b/media/java/android/media/MtpClient.java
@@ -57,20 +57,20 @@
         native_stop();
     }
 
-    public boolean deleteObject(int deviceID, int objectID) {
+    public boolean deleteObject(int deviceID, long objectID) {
         return native_delete_object(deviceID, objectID);
     }
 
-    public int getParent(int deviceID, int objectID) {
+    public long getParent(int deviceID, long objectID) {
         return native_get_parent(deviceID, objectID);
     }
 
-    public int getStorageID(int deviceID, int objectID) {
+    public long getStorageID(int deviceID, long objectID) {
         return native_get_storage_id(deviceID, objectID);
     }
 
     // create a file descriptor for reading the contents of an object over MTP
-    public ParcelFileDescriptor openFile(int deviceID, int objectID) {
+    public ParcelFileDescriptor openFile(int deviceID, long objectID) {
         return native_open_file(deviceID, objectID);
     }
 
@@ -101,8 +101,8 @@
     private native final void native_finalize();
     private native boolean native_start();
     private native void native_stop();
-    private native boolean native_delete_object(int deviceID, int objectID);
-    private native int native_get_parent(int deviceID, int objectID);
-    private native int native_get_storage_id(int deviceID, int objectID);
-    private native ParcelFileDescriptor native_open_file(int deviceID, int objectID);
+    private native boolean native_delete_object(int deviceID, long objectID);
+    private native long native_get_parent(int deviceID, long objectID);
+    private native long native_get_storage_id(int deviceID, long objectID);
+    private native ParcelFileDescriptor native_open_file(int deviceID, long objectID);
 }
diff --git a/media/java/android/media/MtpCursor.java b/media/java/android/media/MtpCursor.java
index d4142d8..b9dd03e 100644
--- a/media/java/android/media/MtpCursor.java
+++ b/media/java/android/media/MtpCursor.java
@@ -43,8 +43,8 @@
 
     private int mQueryType;
     private int mDeviceID;
-    private int mStorageID;
-    private int mQbjectID;
+    private long mStorageID;
+    private long mQbjectID;
 
     /** The names of the columns in the projection */
     private String[] mColumns;
@@ -54,7 +54,7 @@
 
     private final MtpClient mClient;
 
-    public MtpCursor(MtpClient client, int queryType, int deviceID, int storageID, int objectID,
+    public MtpCursor(MtpClient client, int queryType, int deviceID, long storageID, long objectID,
             String[] projection) {
 
         mClient = client;
@@ -220,7 +220,7 @@
     private int mNativeContext;
 
     private native final void native_setup(MtpClient client, int queryType,
-            int deviceID, int storageID, int objectID, int[] columns);
+            int deviceID, long storageID, long objectID, int[] columns);
     private native final void native_finalize();
     private native void native_wait_for_event();
     private native int native_fill_window(CursorWindow window, int startPos);
diff --git a/media/jni/android_media_MtpClient.cpp b/media/jni/android_media_MtpClient.cpp
index 67740bc..d23185b 100644
--- a/media/jni/android_media_MtpClient.cpp
+++ b/media/jni/android_media_MtpClient.cpp
@@ -161,7 +161,7 @@
 
 static jboolean
 android_media_MtpClient_delete_object(JNIEnv *env, jobject thiz,
-        jint device_id, jint object_id)
+        jint device_id, jlong object_id)
 {
 #ifdef HAVE_ANDROID_OS
     MyClient *client = (MyClient *)env->GetIntField(thiz, field_context);
@@ -173,9 +173,9 @@
         return NULL;
 }
 
-static jint
+static jlong
 android_media_MtpClient_get_parent(JNIEnv *env, jobject thiz,
-        jint device_id, jint object_id)
+        jint device_id, jlong object_id)
 {
 #ifdef HAVE_ANDROID_OS
     MyClient *client = (MyClient *)env->GetIntField(thiz, field_context);
@@ -187,9 +187,9 @@
         return -1;
 }
 
-static jint
+static jlong
 android_media_MtpClient_get_storage_id(JNIEnv *env, jobject thiz,
-        jint device_id, jint object_id)
+        jint device_id, jlong object_id)
 {
  #ifdef HAVE_ANDROID_OS
     MyClient *client = (MyClient *)env->GetIntField(thiz, field_context);
@@ -203,7 +203,7 @@
 
 static jobject
 android_media_MtpClient_open_file(JNIEnv *env, jobject thiz,
-        jint device_id, jint object_id)
+        jint device_id, jlong object_id)
 {
 #ifdef HAVE_ANDROID_OS
     MyClient *client = (MyClient *)env->GetIntField(thiz, field_context);
@@ -240,10 +240,10 @@
     {"native_finalize",         "()V",  (void *)android_media_MtpClient_finalize},
     {"native_start",            "()Z",  (void *)android_media_MtpClient_start},
     {"native_stop",             "()V",  (void *)android_media_MtpClient_stop},
-    {"native_delete_object",   "(II)Z", (void *)android_media_MtpClient_delete_object},
-    {"native_get_parent",      "(II)I", (void *)android_media_MtpClient_get_parent},
-    {"native_get_storage_id",  "(II)I", (void *)android_media_MtpClient_get_storage_id},
-    {"native_open_file",       "(II)Landroid/os/ParcelFileDescriptor;",
+    {"native_delete_object",   "(IJ)Z", (void *)android_media_MtpClient_delete_object},
+    {"native_get_parent",      "(IJ)J", (void *)android_media_MtpClient_get_parent},
+    {"native_get_storage_id",  "(IJ)J", (void *)android_media_MtpClient_get_storage_id},
+    {"native_open_file",       "(IJ)Landroid/os/ParcelFileDescriptor;",
                                         (void *)android_media_MtpClient_open_file},
 };
 
diff --git a/media/jni/android_media_MtpCursor.cpp b/media/jni/android_media_MtpCursor.cpp
index 6228b5d..6a65ffd 100644
--- a/media/jni/android_media_MtpCursor.cpp
+++ b/media/jni/android_media_MtpCursor.cpp
@@ -49,7 +49,7 @@
 
 static void
 android_media_MtpCursor_setup(JNIEnv *env, jobject thiz, jobject javaClient,
-        jint queryType, jint deviceID, jint storageID, jint objectID, jintArray javaColumns)
+        jint queryType, jint deviceID, jlong storageID, jlong objectID, jintArray javaColumns)
 {
 #ifdef HAVE_ANDROID_OS
     LOGD("android_media_MtpCursor_setup queryType: %d deviceID: %d storageID: %d objectID: %d\n",
@@ -104,7 +104,7 @@
 // ----------------------------------------------------------------------------
 
 static JNINativeMethod gMethods[] = {
-    {"native_setup",            "(Landroid/media/MtpClient;IIII[I)V",
+    {"native_setup",            "(Landroid/media/MtpClient;IIJJ[I)V",
                                         (void *)android_media_MtpCursor_setup},
     {"native_finalize",         "()V",  (void *)android_media_MtpCursor_finalize},
     {"native_fill_window",      "(Landroid/database/CursorWindow;I)I",
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 1b2be41..7a29bd2 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -999,7 +999,10 @@
     status_t err = mOMX->getParameter(
             mNode, OMX_IndexParamVideoErrorCorrection,
             &errorCorrectionType, sizeof(errorCorrectionType));
-    CHECK_EQ(err, OK);
+    if (err != OK) {
+        LOGW("Error correction param query is not supported");
+        return OK;  // Optional feature. Ignore this failure
+    }
 
     errorCorrectionType.bEnableHEC = OMX_FALSE;
     errorCorrectionType.bEnableResync = OMX_TRUE;
@@ -1010,7 +1013,11 @@
     err = mOMX->setParameter(
             mNode, OMX_IndexParamVideoErrorCorrection,
             &errorCorrectionType, sizeof(errorCorrectionType));
-    CHECK_EQ(err, OK);
+    if (err != OK) {
+        LOGW("Error correction param configuration is not supported");
+    }
+
+    // Optional feature. Ignore the failure.
     return OK;
 }
 
@@ -1108,8 +1115,8 @@
 
     // Check profile and level parameters
     CodecProfileLevel defaultProfileLevel, profileLevel;
-    defaultProfileLevel.mProfile = OMX_VIDEO_H263ProfileBaseline;
-    defaultProfileLevel.mLevel = OMX_VIDEO_H263Level45;
+    defaultProfileLevel.mProfile = h263type.eProfile;
+    defaultProfileLevel.mLevel = h263type.eLevel;
     err = getVideoProfileLevel(meta, defaultProfileLevel, profileLevel);
     if (err != OK) return err;
     h263type.eProfile = static_cast<OMX_VIDEO_H263PROFILETYPE>(profileLevel.mProfile);
@@ -1165,8 +1172,8 @@
 
     // Check profile and level parameters
     CodecProfileLevel defaultProfileLevel, profileLevel;
-    defaultProfileLevel.mProfile = OMX_VIDEO_MPEG4ProfileSimple;
-    defaultProfileLevel.mLevel = OMX_VIDEO_MPEG4Level2;
+    defaultProfileLevel.mProfile = mpeg4type.eProfile;
+    defaultProfileLevel.mLevel = mpeg4type.eLevel;
     err = getVideoProfileLevel(meta, defaultProfileLevel, profileLevel);
     if (err != OK) return err;
     mpeg4type.eProfile = static_cast<OMX_VIDEO_MPEG4PROFILETYPE>(profileLevel.mProfile);
diff --git a/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp b/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp
index 7483d60..3c0b736 100644
--- a/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp
+++ b/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp
@@ -232,6 +232,53 @@
     }
 }
 
+MediaBuffer *AVCDecoder::drainOutputBuffer() {
+    int32_t index;
+    int32_t Release;
+    AVCFrameIO Output;
+    Output.YCbCr[0] = Output.YCbCr[1] = Output.YCbCr[2] = NULL;
+    AVCDec_Status status = PVAVCDecGetOutput(mHandle, &index, &Release, &Output);
+
+    if (status != AVCDEC_SUCCESS) {
+        LOGV("PVAVCDecGetOutput returned error %d", status);
+        return NULL;
+    }
+
+    CHECK(index >= 0);
+    CHECK(index < (int32_t)mFrames.size());
+
+    MediaBuffer *mbuf = mFrames.editItemAt(index);
+
+    bool skipFrame = false;
+
+    if (mTargetTimeUs >= 0) {
+        int64_t timeUs;
+        CHECK(mbuf->meta_data()->findInt64(kKeyTime, &timeUs));
+        CHECK(timeUs <= mTargetTimeUs);
+
+        if (timeUs < mTargetTimeUs) {
+            // We're still waiting for the frame with the matching
+            // timestamp and we won't return the current one.
+            skipFrame = true;
+
+            LOGV("skipping frame at %lld us", timeUs);
+        } else {
+            LOGV("found target frame at %lld us", timeUs);
+
+            mTargetTimeUs = -1;
+        }
+    }
+
+    if (!skipFrame) {
+        mbuf->set_range(0, mbuf->size());
+        mbuf->add_ref();
+
+        return mbuf;
+    }
+
+    return new MediaBuffer(0);
+}
+
 status_t AVCDecoder::read(
         MediaBuffer **out, const ReadOptions *options) {
     *out = NULL;
@@ -279,7 +326,8 @@
                 seekOptions.clearSeekTo();
 
                 if (err != OK) {
-                    return err;
+                    *out = drainOutputBuffer();
+                    return (*out == NULL)  ? err : (status_t)OK;
                 }
 
                 if (mInputBuffer->range_length() > 0) {
@@ -415,51 +463,12 @@
                         fragSize);
 
                 if (res == AVCDEC_PICTURE_OUTPUT_READY) {
-                    int32_t index;
-                    int32_t Release;
-                    AVCFrameIO Output;
-                    Output.YCbCr[0] = Output.YCbCr[1] = Output.YCbCr[2] = NULL;
-
-                    AVCDec_Status status =
-                        PVAVCDecGetOutput(mHandle, &index, &Release, &Output);
-
-                    if (status != AVCDEC_SUCCESS) {
-                        LOGV("PVAVCDecGetOutput returned error %d", status);
+                    MediaBuffer *mbuf = drainOutputBuffer();
+                    if (mbuf == NULL) {
                         break;
                     }
 
-                    CHECK(index >= 0);
-                    CHECK(index < (int32_t)mFrames.size());
-
-                    MediaBuffer *mbuf = mFrames.editItemAt(index);
-
-                    bool skipFrame = false;
-
-                    if (mTargetTimeUs >= 0) {
-                        int64_t timeUs;
-                        CHECK(mbuf->meta_data()->findInt64(kKeyTime, &timeUs));
-                        CHECK(timeUs <= mTargetTimeUs);
-
-                        if (timeUs < mTargetTimeUs) {
-                            // We're still waiting for the frame with the matching
-                            // timestamp and we won't return the current one.
-                            skipFrame = true;
-
-                            LOGV("skipping frame at %lld us", timeUs);
-                        } else {
-                            LOGV("found target frame at %lld us", timeUs);
-
-                            mTargetTimeUs = -1;
-                        }
-                    }
-
-                    if (!skipFrame) {
-                        *out = mbuf;
-                        (*out)->set_range(0, (*out)->size());
-                        (*out)->add_ref();
-                    } else {
-                        *out = new MediaBuffer(0);
-                    }
+                    *out = mbuf;
 
                     // Do _not_ release input buffer yet.
 
@@ -496,6 +505,7 @@
 
             case AVC_NALTYPE_AUD:
             case AVC_NALTYPE_FILL:
+            case AVC_NALTYPE_EOSEQ:
             {
                 *out = new MediaBuffer(0);
 
diff --git a/media/libstagefright/include/AVCDecoder.h b/media/libstagefright/include/AVCDecoder.h
index 898c90a..eb3b142 100644
--- a/media/libstagefright/include/AVCDecoder.h
+++ b/media/libstagefright/include/AVCDecoder.h
@@ -82,6 +82,8 @@
 
     void releaseFrames();
 
+    MediaBuffer *drainOutputBuffer();
+
     AVCDecoder(const AVCDecoder &);
     AVCDecoder &operator=(const AVCDecoder &);
 };
diff --git a/media/libstagefright/omx/tests/OMXHarness.cpp b/media/libstagefright/omx/tests/OMXHarness.cpp
index fcf506d..4f28855 100644
--- a/media/libstagefright/omx/tests/OMXHarness.cpp
+++ b/media/libstagefright/omx/tests/OMXHarness.cpp
@@ -286,7 +286,7 @@
         return OK;
     }
 
-    sp<MemoryDealer> dealer = new MemoryDealer(8 * 1024 * 1024, "OMXHarness");
+    sp<MemoryDealer> dealer = new MemoryDealer(16 * 1024 * 1024, "OMXHarness");
     IOMX::node_id node;
 
     status_t err =
diff --git a/media/tests/CameraBrowser/Android.mk b/media/tests/CameraBrowser/Android.mk
index 33d2976..1d81129 100644
--- a/media/tests/CameraBrowser/Android.mk
+++ b/media/tests/CameraBrowser/Android.mk
@@ -1,6 +1,8 @@
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
+LOCAL_MODULE_TAGS := tests
+
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
 LOCAL_PACKAGE_NAME := CameraBrowser
diff --git a/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectBrowser.java b/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectBrowser.java
index 329aa40..6d34fd4 100644
--- a/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectBrowser.java
+++ b/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectBrowser.java
@@ -44,8 +44,8 @@
     private Cursor mCursor;
     private ObjectCursorAdapter mAdapter;
     private int mDeviceID;
-    private int mStorageID;
-    private int mObjectID;
+    private long mStorageID;
+    private long mObjectID;
 
     private static final String[] OBJECT_COLUMNS =
         new String[] { Mtp.Object._ID, Mtp.Object.NAME, Mtp.Object.FORMAT, Mtp.Object.THUMB };
@@ -65,8 +65,8 @@
         super.onResume();
 
         mDeviceID = getIntent().getIntExtra("device", 0);
-        mStorageID = getIntent().getIntExtra("storage", 0);
-        mObjectID = getIntent().getIntExtra("object", 0);
+        mStorageID = getIntent().getLongExtra("storage", 0);
+        mObjectID = getIntent().getLongExtra("object", 0);
         if (mDeviceID != 0 && mStorageID != 0) {
             Cursor c;
             Uri uri;
@@ -88,7 +88,7 @@
 
     @Override
     protected void onListItemClick(ListView l, View v, int position, long id) {
-        int rowID = (int)mAdapter.getItemId(position);
+        long rowID = mAdapter.getItemId(position);
         Cursor c = getContentResolver().query(
                         Mtp.Object.getContentUri(mDeviceID, rowID),
                         OBJECT_COLUMNS, null, null, null);
@@ -111,7 +111,7 @@
             Intent intent = new Intent(this, ObjectViewer.class);
             intent.putExtra("device", mDeviceID);
             intent.putExtra("storage", mStorageID);
-            intent.putExtra("object",rowID);
+            intent.putExtra("object", rowID);
             startActivity(intent);
         }
     }
diff --git a/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectViewer.java b/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectViewer.java
index 408f15e..5899bc1 100644
--- a/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectViewer.java
+++ b/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectViewer.java
@@ -51,8 +51,8 @@
     private static final String TAG = "ObjectViewer";
 
     private int mDeviceID;
-    private int mStorageID;
-    private int mObjectID;
+    private long mStorageID;
+    private long mObjectID;
 
     private static final String[] OBJECT_COLUMNS =
         new String[] {  Mtp.Object._ID,
@@ -84,8 +84,8 @@
         super.onResume();
 
         mDeviceID = getIntent().getIntExtra("device", 0);
-        mStorageID = getIntent().getIntExtra("storage", 0);
-        mObjectID = getIntent().getIntExtra("object", 0);
+        mStorageID = getIntent().getLongExtra("storage", 0);
+        mObjectID = getIntent().getLongExtra("object", 0);
 
         if (mDeviceID != 0 && mObjectID != 0) {
         Cursor c = getContentResolver().query(
diff --git a/media/tests/CameraBrowser/src/com/android/camerabrowser/StorageBrowser.java b/media/tests/CameraBrowser/src/com/android/camerabrowser/StorageBrowser.java
index 6ddf4e7..63e036e 100644
--- a/media/tests/CameraBrowser/src/com/android/camerabrowser/StorageBrowser.java
+++ b/media/tests/CameraBrowser/src/com/android/camerabrowser/StorageBrowser.java
@@ -70,7 +70,7 @@
     protected void onListItemClick(ListView l, View v, int position, long id) {
         Intent intent = new Intent(this, ObjectBrowser.class);
         intent.putExtra("device", mDeviceID);
-        intent.putExtra("storage", (int)mAdapter.getItemId(position));
+        intent.putExtra("storage", mAdapter.getItemId(position));
         startActivity(intent);
     }
 }
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaBassBoostTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaBassBoostTest.java
index aca729e..b5b1c3e 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaBassBoostTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaBassBoostTest.java
@@ -45,6 +45,7 @@
     private String TAG = "MediaBassBoostTest";
     private final static int MIN_ENERGY_RATIO_2 = 3;
     private final static short TEST_STRENGTH = 500;
+    private final static int TEST_VOLUME = 4;
 
     private BassBoost mBassBoost = null;
     private int mSession = -1;
@@ -192,7 +193,7 @@
         AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
         int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
         am.setStreamVolume(AudioManager.STREAM_MUSIC,
-                           am.getStreamMaxVolume(AudioManager.STREAM_MUSIC),
+                           TEST_VOLUME,
                            0);
 
         try {
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEnvReverbTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEnvReverbTest.java
index db0db70..d5538f1 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEnvReverbTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEnvReverbTest.java
@@ -414,13 +414,13 @@
         EnergyProbe probe = null;
         AudioEffect vc = null;
         MediaPlayer mp = null;
+        AudioEffect rvb = null;
         AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
         int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
         am.setStreamVolume(AudioManager.STREAM_MUSIC,
                            am.getStreamMaxVolume(AudioManager.STREAM_MUSIC),
                            0);
         try {
-            probe = new EnergyProbe(0);
             // creating a volume controller on output mix ensures that ro.audio.silent mutes
             // audio after the effects and not before
             vc = new AudioEffect(
@@ -433,11 +433,24 @@
             mp = new MediaPlayer();
             mp.setDataSource(MediaNames.SINE_200_1000);
             mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
-            getReverb(mp.getAudioSessionId());
-            mReverb.setRoomLevel((short)0);
-            mReverb.setReverbLevel((short)0);
-            mReverb.setDecayTime(2000);
-            mReverb.setEnabled(true);
+
+            // create reverb with UUID instead of EnvironmentalReverb constructor otherwise an
+            // auxiliary reverb will be chosen by the effect framework as we are on session 0
+            rvb = new AudioEffect(
+                        AudioEffect.EFFECT_TYPE_NULL,
+                        UUID.fromString("c7a511a0-a3bb-11df-860e-0002a5d5c51b"),
+                        0,
+                        0);
+
+            rvb.setParameter(EnvironmentalReverb.PARAM_ROOM_LEVEL, (short)0);
+            rvb.setParameter(EnvironmentalReverb.PARAM_REVERB_LEVEL, (short)0);
+            rvb.setParameter(EnvironmentalReverb.PARAM_DECAY_TIME, 2000);
+            rvb.setEnabled(true);
+
+            // create probe after reverb so that it is chained behind the reverb in the
+            // effect chain
+            probe = new EnergyProbe(0);
+
             mp.prepare();
             mp.start();
             Thread.sleep(1000);
@@ -460,13 +473,15 @@
             loge(msg, "sleep() interrupted");
         }
         finally {
-            releaseReverb();
             if (mp != null) {
                 mp.release();
             }
             if (vc != null) {
                 vc.release();
             }
+            if (rvb != null) {
+                rvb.release();
+            }
             if (probe != null) {
                 probe.release();
             }
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEqualizerTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEqualizerTest.java
index 7b3945d..a78668c 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEqualizerTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEqualizerTest.java
@@ -48,6 +48,7 @@
     private final static int MAX_BAND_LEVEL = 1500;
     private final static int TEST_FREQUENCY_MILLIHERTZ = 1000000;
     private final static int MIN_NUMBER_OF_PRESETS = 4;
+    private final static int TEST_VOLUME = 4;
     private Equalizer mEqualizer = null;
     private int mSession = -1;
 
@@ -259,7 +260,7 @@
         AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
         int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
         am.setStreamVolume(AudioManager.STREAM_MUSIC,
-                           am.getStreamMaxVolume(AudioManager.STREAM_MUSIC),
+                           TEST_VOLUME,
                            0);
         try {
             probe = new EnergyProbe(0);
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPresetReverbTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPresetReverbTest.java
index c14319a..fbd8a78 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPresetReverbTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPresetReverbTest.java
@@ -257,13 +257,13 @@
         EnergyProbe probe = null;
         AudioEffect vc = null;
         MediaPlayer mp = null;
+        AudioEffect rvb = null;
         AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
         int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
         am.setStreamVolume(AudioManager.STREAM_MUSIC,
                            am.getStreamMaxVolume(AudioManager.STREAM_MUSIC),
                            0);
         try {
-            probe = new EnergyProbe(0);
             // creating a volume controller on output mix ensures that ro.audio.silent mutes
             // audio after the effects and not before
             vc = new AudioEffect(
@@ -279,6 +279,22 @@
             getReverb(mp.getAudioSessionId());
             mReverb.setPreset((short)PresetReverb.PRESET_PLATE);
             mReverb.setEnabled(true);
+
+            // create reverb with UUID instead of PresetReverb constructor otherwise an auxiliary
+            // reverb will be chosen by the effect framework as we are on session 0
+            rvb = new AudioEffect(
+                        AudioEffect.EFFECT_TYPE_NULL,
+                        UUID.fromString("172cdf00-a3bc-11df-a72f-0002a5d5c51b"),
+                        0,
+                        0);
+
+            rvb.setParameter(PresetReverb.PARAM_PRESET, PresetReverb.PRESET_PLATE);
+            rvb.setEnabled(true);
+
+            // create probe after reverb so that it is chained behind the reverb in the
+            // effect chain
+            probe = new EnergyProbe(0);
+
             mp.prepare();
             mp.start();
             Thread.sleep(1000);
@@ -308,6 +324,9 @@
             if (vc != null) {
                 vc.release();
             }
+            if (rvb != null) {
+                rvb.release();
+            }
             if (probe != null) {
                 probe.release();
             }
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVirtualizerTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVirtualizerTest.java
index 517d575..7123db4 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVirtualizerTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVirtualizerTest.java
@@ -45,6 +45,7 @@
     private String TAG = "MediaVirtualizerTest";
     private final static int MIN_ENERGY_RATIO_2 = 3;
     private final static short TEST_STRENGTH = 500;
+    private final static int TEST_VOLUME = 4;
 
     private Virtualizer mVirtualizer = null;
     private int mSession = -1;
@@ -193,7 +194,7 @@
         AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
         int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
         am.setStreamVolume(AudioManager.STREAM_MUSIC,
-                           am.getStreamMaxVolume(AudioManager.STREAM_MUSIC),
+                           TEST_VOLUME,
                            0);
 
         try {
diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp
index 66f9e2d..a8200be 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -1365,16 +1365,18 @@
 EGLint eglGetError(void)
 {
     EGLint result = EGL_SUCCESS;
+    EGLint err;
     for (int i=0 ; i<IMPL_NUM_IMPLEMENTATIONS ; i++) {
-        EGLint err = EGL_SUCCESS;
+        err = EGL_SUCCESS;
         egl_connection_t* const cnx = &gEGLImpl[i];
         if (cnx->dso)
             err = cnx->egl.eglGetError();
         if (err!=EGL_SUCCESS && result==EGL_SUCCESS)
             result = err;
     }
+    err = getError();
     if (result == EGL_SUCCESS)
-        result = getError();
+        result = err;
     return result;
 }
 
@@ -1816,16 +1818,16 @@
 {
     EGLContext ctx = eglGetCurrentContext();
     ContextRef _c(ctx);
-    if (!_c.get()) return setError(EGL_BAD_CONTEXT, EGL_NO_IMAGE_KHR);
+    if (!_c.get()) return setError(EGL_BAD_CONTEXT, EGL_NO_SYNC_KHR);
     if (!validate_display_context(dpy, ctx))
-        return EGL_NO_IMAGE_KHR;
+        return EGL_NO_SYNC_KHR;
     egl_display_t const * const dp = get_display(dpy);
     egl_context_t * const c = get_context(ctx);
-    EGLSyncKHR result = EGL_NO_IMAGE_KHR;
+    EGLSyncKHR result = EGL_NO_SYNC_KHR;
     if (c->cnx->egl.eglCreateSyncKHR) {
         EGLSyncKHR sync = c->cnx->egl.eglCreateSyncKHR(
                 dp->disp[c->impl].dpy, type, attrib_list);
-        if (sync == EGL_NO_IMAGE_KHR)
+        if (sync == EGL_NO_SYNC_KHR)
             return sync;
         result = (egl_sync_t*)new egl_sync_t(dpy, ctx, sync);
     }
diff --git a/opengl/tests/testLatency/Android.mk b/opengl/tests/testLatency/Android.mk
new file mode 100644
index 0000000..96417c7
--- /dev/null
+++ b/opengl/tests/testLatency/Android.mk
@@ -0,0 +1,20 @@
+#########################################################################
+# Test end-to-end latency.
+#########################################################################
+
+TOP_LOCAL_PATH:= $(call my-dir)
+
+# Build activity
+
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SDK_VERSION := 8
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := TestLatency
+
+include $(BUILD_PACKAGE)
diff --git a/opengl/tests/testLatency/AndroidManifest.xml b/opengl/tests/testLatency/AndroidManifest.xml
new file mode 100644
index 0000000..741266e
--- /dev/null
+++ b/opengl/tests/testLatency/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.testlatency">
+    <uses-sdk android:targetSdkVersion="8" android:minSdkVersion="8" />
+
+    <application
+            android:label="@string/testLatency_activity">
+        <activity android:name="TestLatencyActivity"
+                android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
+                android:launchMode="singleTask"
+                android:configChanges="orientation|keyboardHidden">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/opengl/tests/testLatency/res/values/strings.xml b/opengl/tests/testLatency/res/values/strings.xml
new file mode 100644
index 0000000..0309991
--- /dev/null
+++ b/opengl/tests/testLatency/res/values/strings.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2006, 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.
+*/
+-->
+
+<!-- This file contains resource definitions for displayed strings, allowing
+     them to be changed based on the locale and options. -->
+
+<resources>
+    <!-- Simple strings. -->
+    <string name="testLatency_activity">TestLatency</string>
+
+</resources>
+
diff --git a/opengl/tests/testLatency/src/com/android/testlatency/TestLatencyActivity.java b/opengl/tests/testLatency/src/com/android/testlatency/TestLatencyActivity.java
new file mode 100644
index 0000000..ed993cb
--- /dev/null
+++ b/opengl/tests/testLatency/src/com/android/testlatency/TestLatencyActivity.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.testlatency;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.WindowManager;
+
+import java.io.File;
+
+
+public class TestLatencyActivity extends Activity {
+
+    TestLatencyView mView;
+
+    @Override protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        mView = new TestLatencyView(getApplication());
+        setContentView(mView);
+        mView.setFocusableInTouchMode(true);
+    }
+
+    @Override protected void onPause() {
+        super.onPause();
+        mView.onPause();
+    }
+
+    @Override protected void onResume() {
+        super.onResume();
+        mView.onResume();
+    }
+}
diff --git a/opengl/tests/testLatency/src/com/android/testlatency/TestLatencyView.java b/opengl/tests/testLatency/src/com/android/testlatency/TestLatencyView.java
new file mode 100644
index 0000000..d62bf17
--- /dev/null
+++ b/opengl/tests/testLatency/src/com/android/testlatency/TestLatencyView.java
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.testlatency;
+
+import android.content.Context;
+import android.opengl.GLSurfaceView;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLContext;
+import javax.microedition.khronos.egl.EGLDisplay;
+import javax.microedition.khronos.opengles.GL10;
+
+import android.opengl.GLES20;
+
+/**
+ * An implementation of SurfaceView that uses the dedicated surface for
+ * displaying an OpenGL animation.  This allows the animation to run in a
+ * separate thread, without requiring that it be driven by the update mechanism
+ * of the view hierarchy.
+ *
+ * The application-specific rendering code is delegated to a GLView.Renderer
+ * instance.
+ */
+class TestLatencyView extends GLSurfaceView {
+    private static String TAG = "TestLatencyiew";
+    private float mX;
+    private float mY;
+    private float mDX;
+    private float mDY;
+    private long  mT;
+    private long  mDT;
+
+    public TestLatencyView(Context context) {
+        super(context);
+        setEGLContextClientVersion(2);
+        setRenderer(new Renderer());
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        switch (event.getAction()) {
+        case MotionEvent.ACTION_MOVE:
+            float x = event.getX();
+            float y = event.getY();
+            long  t = event.getEventTime();
+            synchronized(this) {
+                mDT = t - mT;
+                mT = t;
+                mDX = x - mX;
+                mX = x;
+                mDY = y - mY;
+                mY = y;
+            }
+            break;
+        default:
+            break;
+        }
+        return true;
+    }
+
+    private class Renderer implements GLSurfaceView.Renderer {
+        private float mScaleX, mScaleY, mOffsetX, mOffsetY;
+        private final float MS_PER_FRAME = 1000 / 60;
+        public Renderer() {
+            mTriangleVertices = ByteBuffer.allocateDirect(mTriangleVerticesData.length * 4)
+                .order(ByteOrder.nativeOrder()).asFloatBuffer();
+        }
+
+
+        public void onDrawFrame(GL10 gl) {
+            GLES20.glClearColor(0.4f, 0.4f, 0.4f, 1.0f);
+            GLES20.glClear( GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
+            GLES20.glUseProgram(mProgram);
+            checkGlError("glUseProgram");
+
+            float x, y, dx, dy;
+            long t, dt;
+            synchronized(TestLatencyView.this) {
+                x = mX;
+                y = mY;
+                dx = mDX;
+                dy = mDY;
+                dt = mDT;
+            }
+
+            if (dt > 0) {
+                dx = dx * MS_PER_FRAME / dt;
+                dy = dy * MS_PER_FRAME / dt;
+            }
+
+            GLES20.glEnableVertexAttribArray(mvPositionHandle);
+            checkGlError("glEnableVertexAttribArray");
+            GLES20.glEnableVertexAttribArray(mvColorHandle);
+            checkGlError("glEnableVertexAttribArray");
+            for(int step = 0; step < 8; step++) {
+                float sx = (x + dx * step) * mScaleX + mOffsetX;
+                float sy = (y + dy * step) * mScaleY + mOffsetY;
+                int cbase = step * 4;
+
+                for (int i = 0; i < mTriangleVerticesData.length; i += 6) {
+                    mTriangleVerticesData2[i] = sx + mTriangleVerticesData[i];
+                    mTriangleVerticesData2[i+1] = -sy + mTriangleVerticesData[i+1];
+                    mTriangleVerticesData2[i+2] = mColors[cbase];
+                    mTriangleVerticesData2[i+3] = mColors[cbase+1];
+                    mTriangleVerticesData2[i+4] = mColors[cbase+2];
+                    mTriangleVerticesData2[i+5] = mColors[cbase+3];
+                }
+                mTriangleVertices.position(0);
+                mTriangleVertices.put(mTriangleVerticesData2).position(0);
+
+                GLES20.glVertexAttribPointer(mvPositionHandle, 2, GLES20.GL_FLOAT, false, 6*4, mTriangleVertices);
+                checkGlError("glVertexAttribPointer mvPosition");
+                mTriangleVertices.put(mTriangleVerticesData2).position(2);
+                GLES20.glVertexAttribPointer(mvColorHandle, 4, GLES20.GL_FLOAT, false, 6*4, mTriangleVertices);
+                checkGlError("glVertexAttribPointer mvColor");
+                GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3);
+                checkGlError("glDrawArrays");
+            }
+        }
+
+        public void onSurfaceChanged(GL10 gl, int width, int height) {
+            GLES20.glViewport(0, 0, width, height);
+            mScaleX = 2.0f / width;
+            mScaleY = 2.0f / height;
+            mOffsetX = -1f;
+            mOffsetY = -1f;
+        }
+
+        public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+            mProgram = createProgram(mVertexShader, mFragmentShader);
+            if (mProgram == 0) {
+                return;
+            }
+            mvPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");
+            checkGlError("glGetAttribLocation");
+            if (mvPositionHandle == -1) {
+                throw new RuntimeException("Could not get attrib location for vPosition");
+            }
+            mvColorHandle = GLES20.glGetAttribLocation(mProgram, "aColor");
+            checkGlError("glGetAttribLocation");
+            if (mvColorHandle == -1) {
+                throw new RuntimeException("Could not get attrib location for vColor");
+            }
+        }
+
+        private int loadShader(int shaderType, String source) {
+            int shader = GLES20.glCreateShader(shaderType);
+            if (shader != 0) {
+                GLES20.glShaderSource(shader, source);
+                GLES20.glCompileShader(shader);
+                int[] compiled = new int[1];
+                GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
+                if (compiled[0] == 0) {
+                    Log.e(TAG, "Could not compile shader " + shaderType + ":");
+                    Log.e(TAG, GLES20.glGetShaderInfoLog(shader));
+                    GLES20.glDeleteShader(shader);
+                    shader = 0;
+                }
+            }
+            return shader;
+        }
+
+        private int createProgram(String vertexSource, String fragmentSource) {
+            int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
+            if (vertexShader == 0) {
+                return 0;
+            }
+
+            int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
+            if (pixelShader == 0) {
+                return 0;
+            }
+
+            int program = GLES20.glCreateProgram();
+            if (program != 0) {
+                GLES20.glAttachShader(program, vertexShader);
+                checkGlError("glAttachShader vertexShader");
+                GLES20.glAttachShader(program, pixelShader);
+                checkGlError("glAttachShader pixelShader");
+                GLES20.glLinkProgram(program);
+                int[] linkStatus = new int[1];
+                GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
+                if (linkStatus[0] != GLES20.GL_TRUE) {
+                    Log.e(TAG, "Could not link program: ");
+                    Log.e(TAG, GLES20.glGetProgramInfoLog(program));
+                    GLES20.glDeleteProgram(program);
+                    program = 0;
+                }
+            }
+            return program;
+        }
+
+        private void checkGlError(String op) {
+            int error;
+            while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
+                Log.e(TAG, op + ": glError " + error);
+                throw new RuntimeException(op + ": glError " + error);
+            }
+        }
+
+        // X, Y, R G B A
+        private final float[] mTriangleVerticesData = {
+                -0.025f, 0.3f, 0.0f, 1.0f, 0.0f, 1.0f,
+                 0.0f  , 0.0f, 0.0f, 1.0f, 0.0f, 1.0f,
+                 0.025f, 0.3f, 1.0f, 1.0f, 255.0f, 1.0f
+                };
+
+        // Color cascade:
+        private final float[] mColors = {
+                0.0f, 0.0f, 0.0f, 1.0f,
+                0.5f, 0.0f, 0.0f, 1.0f,
+                0.0f, 0.5f, 0.0f, 1.0f,
+                0.5f, 0.5f, 0.0f, 1.0f,
+
+                0.0f, 0.0f, 0.5f, 1.0f,
+                1.0f, 0.0f, 0.0f, 1.0f,
+                1.0f, 1.0f, 1.0f, 1.0f,
+                0.0f, 1.0f, 0.0f, 1.0f
+        };
+
+        private float[] mTriangleVerticesData2 = new float[mTriangleVerticesData.length];
+        private FloatBuffer mTriangleVertices;
+
+        private final String mVertexShader = "attribute vec4 aPosition;\n"
+            + "attribute vec4 aColor;\n"
+            + "varying vec4 vColor;\n"
+            + "void main() {\n"
+            + "  gl_Position = aPosition;\n"
+            + "  vColor = aColor;\n"
+            + "}\n";
+
+        private final String mFragmentShader = "precision mediump float;\n"
+            + "varying vec4 vColor;\n"
+            + "void main() {\n"
+            + "  gl_FragColor = vColor;\n"
+            + "}\n";
+
+        private int mProgram;
+        private int mvPositionHandle;
+        private int mvColorHandle;
+
+    }
+}
+
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 43bb26a..3980189 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -3,16 +3,16 @@
 /**
  * Copyright (c) 2009, The Android Open Source Project
  *
- * Licensed under the Apache License, Version 2.0 (the "License"); 
- * you may not use this file except in compliance with the License. 
- * You may obtain a copy of the License at 
+ * 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 
+ *     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 
+ * 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.
  */
 -->
@@ -24,6 +24,7 @@
     <string name="def_airplane_mode_radios" translatable="false">cell,bluetooth,wifi</string>
     <string name="airplane_mode_toggleable_radios" translatable="false">bluetooth,wifi</string>
     <bool name="def_auto_time">true</bool>
+    <bool name="def_auto_time_zone">true</bool>
     <bool name="def_accelerometer_rotation">true</bool>
     <!-- Default screen brightness, from 0 to 255.  102 is 40%. -->
     <integer name="def_screen_brightness">102</integer>
@@ -31,12 +32,12 @@
     <fraction name="def_window_animation_scale">100%</fraction>
     <fraction name="def_window_transition_scale">100%</fraction>
     <bool name="def_haptic_feedback">true</bool>
-    
+
     <bool name="def_bluetooth_on">false</bool>
     <bool name="def_install_non_market_apps">false</bool>
-    <!-- Comma-separated list of location providers. 
+    <!-- Comma-separated list of location providers.
          Network location is off by default because it requires
-         user opt-in via Setup Wizard or Settings.  
+         user opt-in via Setup Wizard or Settings.
     -->
     <string name="def_location_providers_allowed" translatable="false">gps</string>
     <bool name="assisted_gps_enabled">true</bool>
@@ -45,11 +46,11 @@
     <bool name="def_usb_mass_storage_enabled">true</bool>
     <bool name="def_wifi_on">false</bool>
     <bool name="def_networks_available_notification_on">true</bool>
-    
+
     <bool name="def_backup_enabled">false</bool>
     <string name="def_backup_transport" translatable="false">android/com.android.internal.backup.LocalTransport</string>
 
-    <!-- Default value for whether or not to pulse the notification LED when there is a 
+    <!-- Default value for whether or not to pulse the notification LED when there is a
          pending notification -->
     <bool name="def_notification_pulse">true</bool>
 
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 8eb3fe6..9ac832b 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -16,6 +16,15 @@
 
 package com.android.providers.settings;
 
+import com.android.internal.content.PackageHelper;
+import com.android.internal.telephony.RILConstants;
+import com.android.internal.util.XmlUtils;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockPatternView;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
 import android.content.ComponentName;
 import android.content.ContentValues;
 import android.content.Context;
@@ -36,14 +45,6 @@
 import android.text.TextUtils;
 import android.util.Log;
 
-import com.android.internal.content.PackageHelper;
-import com.android.internal.telephony.RILConstants;
-import com.android.internal.util.XmlUtils;
-import com.android.internal.widget.LockPatternUtils;
-import com.android.internal.widget.LockPatternView;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
 import java.io.IOException;
 import java.util.HashSet;
 import java.util.List;
@@ -60,7 +61,7 @@
     // database gets upgraded properly. At a minimum, please confirm that 'upgradeVersion'
     // is properly propagated through your change.  Not doing so will result in a loss of user
     // settings.
-    private static final int DATABASE_VERSION = 58;
+    private static final int DATABASE_VERSION = 59;
 
     private Context mContext;
 
@@ -757,6 +758,23 @@
             upgradeVersion = 58;
         }
 
+        if (upgradeVersion == 58) {
+            /* Add default for new Auto Time Zone */
+            db.beginTransaction();
+            SQLiteStatement stmt = null;
+            try {
+                stmt = db.compileStatement("INSERT INTO secure(name,value)"
+                        + " VALUES(?,?);");
+                loadBooleanSetting(stmt, Settings.System.AUTO_TIME_ZONE,
+                        R.bool.def_auto_time_zone); // Sync timezone to NITZ
+                db.setTransactionSuccessful();
+            } finally {
+                db.endTransaction();
+                if (stmt != null) stmt.close();
+            }
+            upgradeVersion = 59;
+        }
+
         // *** Remember to update DATABASE_VERSION above!
 
         if (upgradeVersion != currentVersion) {
@@ -1067,7 +1085,10 @@
     
             loadBooleanSetting(stmt, Settings.System.AUTO_TIME,
                     R.bool.def_auto_time); // Sync time to NITZ
-    
+
+            loadBooleanSetting(stmt, Settings.System.AUTO_TIME_ZONE,
+                    R.bool.def_auto_time_zone); // Sync timezone to NITZ
+
             loadIntegerSetting(stmt, Settings.System.SCREEN_BRIGHTNESS,
                     R.integer.def_screen_brightness);
     
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java
index 0fc092e..0309430 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java
@@ -844,7 +844,7 @@
         int[] iconList;
 
         // Display signal strength while in "emergency calls only" mode
-        if (!hasService() && !mServiceState.isEmergencyOnly()) {
+        if (mServiceState == null || (!hasService() && !mServiceState.isEmergencyOnly())) {
             //Slog.d(TAG, "updateSignalStrength: no service");
             if (Settings.System.getInt(mContext.getContentResolver(),
                     Settings.System.AIRPLANE_MODE_ON, 0) == 1) {
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 886c25b..56de765 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -4656,13 +4656,25 @@
     {
         Mutex::Autolock _l(mLock);
 
+        // check audio settings permission for global effects
+        if (sessionId == AudioSystem::SESSION_OUTPUT_MIX && !settingsAllowed()) {
+            lStatus = PERMISSION_DENIED;
+            goto Exit;
+        }
+
+        // Session AudioSystem::SESSION_OUTPUT_STAGE is reserved for output stage effects
+        // that can only be created by audio policy manager (running in same process)
+        if (sessionId == AudioSystem::SESSION_OUTPUT_STAGE && getpid() != pid) {
+            lStatus = PERMISSION_DENIED;
+            goto Exit;
+        }
+
         // check recording permission for visualizer
-        if (memcmp(&pDesc->type, SL_IID_VISUALIZATION, sizeof(effect_uuid_t)) == 0 ||
-            memcmp(&pDesc->uuid, &VISUALIZATION_UUID_, sizeof(effect_uuid_t)) == 0) {
-            if (!recordingAllowed()) {
-                lStatus = PERMISSION_DENIED;
-                goto Exit;
-            }
+        if ((memcmp(&pDesc->type, SL_IID_VISUALIZATION, sizeof(effect_uuid_t)) == 0 ||
+             memcmp(&pDesc->uuid, &VISUALIZATION_UUID_, sizeof(effect_uuid_t)) == 0) &&
+            !recordingAllowed()) {
+            lStatus = PERMISSION_DENIED;
+            goto Exit;
         }
 
         if (!EffectIsNullUuid(&pDesc->uuid)) {
@@ -4727,14 +4739,6 @@
             goto Exit;
         }
 
-        // Session AudioSystem::SESSION_OUTPUT_STAGE is reserved for output stage effects
-        // that can only be created by audio policy manager (running in same process)
-        if (sessionId == AudioSystem::SESSION_OUTPUT_STAGE &&
-                getpid() != pid) {
-            lStatus = INVALID_OPERATION;
-            goto Exit;
-        }
-
         // return effect descriptor
         memcpy(pDesc, &desc, sizeof(effect_descriptor_t));
 
diff --git a/services/java/com/android/server/DropBoxManagerService.java b/services/java/com/android/server/DropBoxManagerService.java
index 14b7d3e..9829f9a 100644
--- a/services/java/com/android/server/DropBoxManagerService.java
+++ b/services/java/com/android/server/DropBoxManagerService.java
@@ -36,6 +36,7 @@
 
 import com.android.internal.os.IDropBoxManagerService;
 
+import java.io.BufferedOutputStream;
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileOutputStream;
@@ -179,7 +180,10 @@
             // the data in uncompressed form.
 
             temp = new File(mDropBoxDir, "drop" + Thread.currentThread().getId() + ".tmp");
-            output = new FileOutputStream(temp);
+            int bufferSize = mBlockSize;
+            if (bufferSize > 4096) bufferSize = 4096;
+            if (bufferSize < 512) bufferSize = 512;
+            output = new BufferedOutputStream(new FileOutputStream(temp), bufferSize);
             if (read == buffer.length && ((flags & DropBoxManager.IS_GZIPPED) == 0)) {
                 output = new GZIPOutputStream(output);
                 flags = flags | DropBoxManager.IS_GZIPPED;
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 9efc708..675760f 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -61,18 +61,21 @@
 import android.os.SystemClock;
 import android.provider.Settings;
 import android.provider.Settings.Secure;
+import android.provider.Settings.SettingNotFoundException;
 import android.text.TextUtils;
 import android.util.EventLog;
+import android.util.Pair;
 import android.util.Slog;
 import android.util.PrintWriterPrinter;
 import android.util.Printer;
 import android.view.IWindowManager;
 import android.view.WindowManager;
+import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputBinding;
 import android.view.inputmethod.InputMethod;
 import android.view.inputmethod.InputMethodInfo;
 import android.view.inputmethod.InputMethodManager;
-import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodSubtype;
 
 import java.io.FileDescriptor;
 import java.io.IOException;
@@ -93,6 +96,7 @@
     static final String TAG = "InputManagerService";
 
     static final int MSG_SHOW_IM_PICKER = 1;
+    static final int MSG_SHOW_IM_SUBTYPE_PICKER = 2;
 
     static final int MSG_UNBIND_INPUT = 1000;
     static final int MSG_BIND_INPUT = 1010;
@@ -109,6 +113,8 @@
 
     static final long TIME_TO_RECONNECT = 10*1000;
 
+    private static final int NOT_A_SUBTYPE_ID = -1;
+
     final Context mContext;
     final Handler mHandler;
     final SettingsObserver mSettingsObserver;
@@ -224,6 +230,12 @@
     String mCurId;
 
     /**
+     * The current subtype of the current input method.
+     */
+    private InputMethodSubtype mCurrentSubtype;
+
+
+    /**
      * Set to true if our ServiceConnection is currently actively bound to
      * a service (whether or not we have gotten its IBinder back yet).
      */
@@ -292,6 +304,7 @@
     AlertDialog mSwitchingDialog;
     InputMethodInfo[] mIms;
     CharSequence[] mItems;
+    int[] mSubtypeIds;
 
     class SettingsObserver extends ContentObserver {
         SettingsObserver(Handler handler) {
@@ -299,6 +312,8 @@
             ContentResolver resolver = mContext.getContentResolver();
             resolver.registerContentObserver(Settings.Secure.getUriFor(
                     Settings.Secure.DEFAULT_INPUT_METHOD), false, this);
+            resolver.registerContentObserver(Settings.Secure.getUriFor(
+                    Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE), false, this);
         }
 
         @Override public void onChange(boolean selfChange) {
@@ -351,9 +366,9 @@
                                     if (!doit) {
                                         return true;
                                     }
-                                    
                                     Settings.Secure.putString(mContext.getContentResolver(),
                                             Settings.Secure.DEFAULT_INPUT_METHOD, "");
+                                    resetSelectedInputMethodSubtype();
                                     chooseNewDefaultIMELocked();
                                     return true;
                                 }
@@ -409,16 +424,15 @@
                             if (!chooseNewDefaultIMELocked()) {
                                 changed = true;
                                 curIm = null;
-                                curInputMethodId = "";
                                 Slog.i(TAG, "Unsetting current input method");
                                 Settings.Secure.putString(mContext.getContentResolver(),
-                                        Settings.Secure.DEFAULT_INPUT_METHOD,
-                                        curInputMethodId);
+                                        Settings.Secure.DEFAULT_INPUT_METHOD, "");
+                                resetSelectedInputMethodSubtype();
                             }
                         }
                     }
                 }
-                
+
                 if (curIm == null) {
                     // We currently don't have a default input method... is
                     // one now available?
@@ -509,6 +523,7 @@
             if (defIm != null) {
                 Settings.Secure.putString(mContext.getContentResolver(),
                         Settings.Secure.DEFAULT_INPUT_METHOD, defIm.getId());
+                putSelectedInputMethodSubtype(defIm, NOT_A_SUBTYPE_ID);
             }
         }
 
@@ -964,10 +979,10 @@
         // sync, so we will never have a DEFAULT_INPUT_METHOD that is not
         // enabled.
         String id = Settings.Secure.getString(mContext.getContentResolver(),
-            Settings.Secure.DEFAULT_INPUT_METHOD);
+                Settings.Secure.DEFAULT_INPUT_METHOD);
         if (id != null && id.length() > 0) {
             try {
-                setInputMethodLocked(id);
+                setInputMethodLocked(id, getSelectedInputMethodSubtypeId(id));
             } catch (IllegalArgumentException e) {
                 Slog.w(TAG, "Unknown input method from prefs: " + id, e);
                 mCurMethodId = null;
@@ -980,21 +995,44 @@
         }
     }
 
-    void setInputMethodLocked(String id) {
+    /* package */ void setInputMethodLocked(String id, int subtypeId) {
         InputMethodInfo info = mMethodMap.get(id);
         if (info == null) {
             throw new IllegalArgumentException("Unknown id: " + id);
         }
 
         if (id.equals(mCurMethodId)) {
+            if (subtypeId != NOT_A_SUBTYPE_ID) {
+                InputMethodSubtype subtype = info.getSubtypes().get(subtypeId);
+                if (subtype != mCurrentSubtype) {
+                    synchronized (mMethodMap) {
+                        if (mCurMethod != null) {
+                            try {
+                                putSelectedInputMethodSubtype(info, subtypeId);
+                                mCurMethod.changeInputMethodSubtype(subtype);
+                            } catch (RemoteException e) {
+                                return;
+                            }
+                        }
+                    }
+                }
+            }
             return;
         }
 
         final long ident = Binder.clearCallingIdentity();
         try {
             mCurMethodId = id;
+            // Set a subtype to this input method.
+            // subtypeId the name of a subtype which will be set.
+            if (putSelectedInputMethodSubtype(info, subtypeId)) {
+                mCurrentSubtype = info.getSubtypes().get(subtypeId);
+            } else {
+                mCurrentSubtype = null;
+            }
+
             Settings.Secure.putString(mContext.getContentResolver(),
-                Settings.Secure.DEFAULT_INPUT_METHOD, id);
+                    Settings.Secure.DEFAULT_INPUT_METHOD, id);
 
             if (ActivityManagerNative.isSystemReady()) {
                 Intent intent = new Intent(Intent.ACTION_INPUT_METHOD_CHANGED);
@@ -1226,7 +1264,22 @@
         }
     }
 
+    public void showInputMethodSubtypePickerFromClient(IInputMethodClient client) {
+        synchronized (mMethodMap) {
+            if (mCurClient == null || client == null
+                    || mCurClient.client.asBinder() != client.asBinder()) {
+                Slog.w(TAG, "Ignoring showInputSubtypeMethodDialogFromClient of: " + client);
+            }
+
+            mHandler.sendEmptyMessage(MSG_SHOW_IM_SUBTYPE_PICKER);
+        }
+    }
+
     public void setInputMethod(IBinder token, String id) {
+        setInputMethodWithSubtype(token, id, NOT_A_SUBTYPE_ID);
+    }
+
+    private void setInputMethodWithSubtype(IBinder token, String id, int subtypeId) {
         synchronized (mMethodMap) {
             if (token == null) {
                 if (mContext.checkCallingOrSelfPermission(
@@ -1243,7 +1296,7 @@
 
             long ident = Binder.clearCallingIdentity();
             try {
-                setInputMethodLocked(id);
+                setInputMethodLocked(id, subtypeId);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -1307,6 +1360,10 @@
                 showInputMethodMenu();
                 return true;
 
+            case MSG_SHOW_IM_SUBTYPE_PICKER:
+                showInputMethodSubtypeMenu();
+                return true;
+
             // ---------------------------------------------------------
 
             case MSG_UNBIND_INPUT:
@@ -1417,9 +1474,10 @@
                     break;
                 }
             }
+            InputMethodInfo imi = enabled.get(i);
             Settings.Secure.putString(mContext.getContentResolver(),
-                    Settings.Secure.DEFAULT_INPUT_METHOD,
-                    enabled.get(i).getId());
+                    Settings.Secure.DEFAULT_INPUT_METHOD, imi.getId());
+            putSelectedInputMethodSubtype(imi, NOT_A_SUBTYPE_ID);
             return true;
         }
 
@@ -1490,7 +1548,15 @@
 
     // ----------------------------------------------------------------------
 
-    void showInputMethodMenu() {
+    private void showInputMethodMenu() {
+        showInputMethodMenuInternal(false);
+    }
+
+    private void showInputMethodSubtypeMenu() {
+        showInputMethodMenuInternal(true);
+    }
+
+    private void showInputMethodMenuInternal(boolean showSubtypes) {
         if (DEBUG) Slog.v(TAG, "Show switching menu");
 
         final Context context = mContext;
@@ -1499,42 +1565,78 @@
 
         String lastInputMethodId = Settings.Secure.getString(context
                 .getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
+        int lastInputMethodSubtypeId = getSelectedInputMethodSubtypeId(lastInputMethodId);
         if (DEBUG) Slog.v(TAG, "Current IME: " + lastInputMethodId);
 
         final List<InputMethodInfo> immis = getEnabledInputMethodList();
+        ArrayList<Integer> subtypeIds = new ArrayList<Integer>();
 
         if (immis == null) {
             return;
         }
-        
+
         synchronized (mMethodMap) {
             hideInputMethodMenuLocked();
 
             int N = immis.size();
 
-            final Map<CharSequence, InputMethodInfo> imMap =
-                new TreeMap<CharSequence, InputMethodInfo>(Collator.getInstance());
+            final Map<CharSequence, Pair<InputMethodInfo, Integer>> imMap =
+                new TreeMap<CharSequence, Pair<InputMethodInfo, Integer>>(Collator.getInstance());
 
             for (int i = 0; i < N; ++i) {
                 InputMethodInfo property = immis.get(i);
                 if (property == null) {
                     continue;
                 }
-                imMap.put(property.loadLabel(pm), property);
+                // TODO: Show only enabled subtypes
+                ArrayList<InputMethodSubtype> subtypes = property.getSubtypes();
+                CharSequence label = property.loadLabel(pm);
+                if (showSubtypes && subtypes.size() > 0) {
+                    for (int j = 0; j < subtypes.size(); ++j) {
+                        InputMethodSubtype subtype = subtypes.get(j);
+                        CharSequence title;
+                        int nameResId = subtype.getNameResId();
+                        int modeResId = subtype.getModeResId();
+                        if (nameResId != 0) {
+                            title = pm.getText(property.getPackageName(), nameResId,
+                                    property.getServiceInfo().applicationInfo);
+                        } else {
+                            CharSequence language = subtype.getLocale();
+                            CharSequence mode = modeResId == 0 ? null
+                                    : pm.getText(property.getPackageName(), modeResId,
+                                            property.getServiceInfo().applicationInfo);
+                            // TODO: Use more friendly Title and UI
+                            title = label + "," + (mode == null ? "" : mode) + ","
+                                    + (language == null ? "" : language);
+                        }
+                        imMap.put(title, new Pair<InputMethodInfo, Integer>(property, j));
+                    }
+                } else {
+                    imMap.put(label,
+                            new Pair<InputMethodInfo, Integer>(property, NOT_A_SUBTYPE_ID));
+                    subtypeIds.add(0);
+                }
             }
 
             N = imMap.size();
             mItems = imMap.keySet().toArray(new CharSequence[N]);
-            mIms = imMap.values().toArray(new InputMethodInfo[N]);
-
+            mIms = new InputMethodInfo[N];
+            mSubtypeIds = new int[N];
             int checkedItem = 0;
             for (int i = 0; i < N; ++i) {
+                Pair<InputMethodInfo, Integer> value = imMap.get(mItems[i]);
+                mIms[i] = value.first;
+                mSubtypeIds[i] = value.second;
                 if (mIms[i].getId().equals(lastInputMethodId)) {
-                    checkedItem = i;
-                    break;
+                    int subtypeId = mSubtypeIds[i];
+                    if ((subtypeId == NOT_A_SUBTYPE_ID)
+                            || (lastInputMethodSubtypeId == NOT_A_SUBTYPE_ID && subtypeId == 0)
+                            || (subtypeId == lastInputMethodSubtypeId)) {
+                        checkedItem = i;
+                    }
                 }
             }
-    
+
             AlertDialog.OnClickListener adocl = new AlertDialog.OnClickListener() {
                 public void onClick(DialogInterface dialog, int which) {
                     hideInputMethodMenu();
@@ -1559,13 +1661,19 @@
                     new AlertDialog.OnClickListener() {
                         public void onClick(DialogInterface dialog, int which) {
                             synchronized (mMethodMap) {
-                                if (mIms == null || mIms.length <= which) {
+                                if (mIms == null || mIms.length <= which
+                                        || mSubtypeIds == null || mSubtypeIds.length <= which) {
                                     return;
                                 }
                                 InputMethodInfo im = mIms[which];
+                                int subtypeId = mSubtypeIds[which];
                                 hideInputMethodMenu();
                                 if (im != null) {
-                                    setInputMethodLocked(im.getId());
+                                    if ((subtypeId < 0)
+                                            || (subtypeId >= im.getSubtypes().size())) {
+                                        subtypeId = NOT_A_SUBTYPE_ID;
+                                    }
+                                    setInputMethodLocked(im.getId(), subtypeId);
                                 }
                             }
                         }
@@ -1679,6 +1787,7 @@
                 Settings.Secure.putString(mContext.getContentResolver(),
                         Settings.Secure.DEFAULT_INPUT_METHOD,
                         firstId != null ? firstId : "");
+                resetSelectedInputMethodSubtype();
             }
             // Previous state was enabled.
             return true;
@@ -1698,6 +1807,53 @@
         return false;
     }
 
+    private void resetSelectedInputMethodSubtype() {
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE, NOT_A_SUBTYPE_ID);
+    }
+
+    private boolean putSelectedInputMethodSubtype(InputMethodInfo imi, int subtypeId) {
+        ArrayList<InputMethodSubtype> subtypes = imi.getSubtypes();
+        if (subtypeId >= 0 && subtypeId < subtypes.size()) {
+            Settings.Secure.putInt(mContext.getContentResolver(),
+                    Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE,
+                    subtypes.get(subtypeId).hashCode());
+            return true;
+        } else {
+            resetSelectedInputMethodSubtype();
+            return false;
+        }
+    }
+
+    private int getSelectedInputMethodSubtypeId(String id) {
+        InputMethodInfo imi = mMethodMap.get(id);
+        if (imi == null) {
+            return NOT_A_SUBTYPE_ID;
+        }
+        ArrayList<InputMethodSubtype> subtypes = imi.getSubtypes();
+        int subtypeId;
+        try {
+            subtypeId = Settings.Secure.getInt(mContext.getContentResolver(),
+                    Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE);
+        } catch (SettingNotFoundException e) {
+            return NOT_A_SUBTYPE_ID;
+        }
+        for (int i = 0; i < subtypes.size(); ++i) {
+            InputMethodSubtype ims = subtypes.get(i);
+            if (subtypeId == ims.hashCode()) {
+                return i;
+            }
+        }
+        return NOT_A_SUBTYPE_ID;
+    }
+
+    /**
+     * @return Return the current subtype of this input method.
+     */
+    public InputMethodSubtype getCurrentInputMethodSubtype() {
+        return mCurrentSubtype;
+    }
+
     // ----------------------------------------------------------------------
 
     @Override
diff --git a/services/java/com/android/server/NativeDaemonConnector.java b/services/java/com/android/server/NativeDaemonConnector.java
index 03f8dae..cf87a9d 100644
--- a/services/java/com/android/server/NativeDaemonConnector.java
+++ b/services/java/com/android/server/NativeDaemonConnector.java
@@ -181,7 +181,8 @@
         }
     }
 
-    private void sendCommand(String command) {
+    private void sendCommand(String command)
+            throws NativeDaemonConnectorException  {
         sendCommand(command, null);
     }
 
@@ -191,11 +192,13 @@
      * @param command  The command to send to the daemon
      * @param argument The argument to send with the command (or null)
      */
-    private void sendCommand(String command, String argument) {
+    private void sendCommand(String command, String argument)
+            throws NativeDaemonConnectorException  {
         synchronized (this) {
             if (LOCAL_LOGD) Slog.d(TAG, String.format("SND -> {%s} {%s}", command, argument));
             if (mOutputStream == null) {
                 Slog.e(TAG, "No connection to daemon", new IllegalStateException());
+                throw new NativeDaemonConnectorException("No output stream!");
             } else {
                 StringBuilder builder = new StringBuilder(command);
                 if (argument != null) {
@@ -226,6 +229,7 @@
 
         while (!complete) {
             try {
+                // TODO - this should not block forever
                 String line = mResponseQueue.take();
                 if (LOCAL_LOGD) Slog.d(TAG, String.format("RSP <- {%s}", line));
                 String[] tokens = line.split(" ");
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index de459ab..05abd99 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -47,6 +47,7 @@
 
 import java.net.InetAddress;
 import java.net.UnknownHostException;
+import java.util.concurrent.CountDownLatch;
 
 /**
  * @hide
@@ -54,7 +55,7 @@
 class NetworkManagementService extends INetworkManagementService.Stub {
 
     private static final String TAG = "NetworkManagmentService";
-
+    private static final boolean DBG = true;
     private static final String NETD_TAG = "NetdConnector";
 
     class NetdResponseCode {
@@ -86,6 +87,9 @@
      */
     private NativeDaemonConnector mConnector;
 
+    private Thread mThread;
+    private final CountDownLatch mConnectedSignal = new CountDownLatch(1);
+
     private ArrayList<INetworkManagementEventObserver> mObservers;
 
     /**
@@ -93,9 +97,8 @@
      *
      * @param context  Binder context for this service
      */
-    public NetworkManagementService(Context context) {
+    private NetworkManagementService(Context context) {
         mContext = context;
-
         mObservers = new ArrayList<INetworkManagementEventObserver>();
 
         if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
@@ -104,8 +107,17 @@
 
         mConnector = new NativeDaemonConnector(
                 new NetdCallbackReceiver(), "netd", 10, NETD_TAG);
-        Thread thread = new Thread(mConnector, NETD_TAG);
-        thread.start();
+        mThread = new Thread(mConnector, NETD_TAG);
+    }
+
+    public static NetworkManagementService create(Context context) throws InterruptedException {
+        NetworkManagementService service = new NetworkManagementService(context);
+        if (DBG) Slog.d(TAG, "Creating NetworkManagementService");
+        service.mThread.start();
+        if (DBG) Slog.d(TAG, "Awaiting socket connection");
+        service.mConnectedSignal.await();
+        if (DBG) Slog.d(TAG, "Connected");
+        return service;
     }
 
     public void registerObserver(INetworkManagementEventObserver obs) {
@@ -157,6 +169,14 @@
         }
     }
 
+    /**
+     * Let us know the daemon is connected
+     */
+    protected void onConnected() {
+        if (DBG) Slog.d(TAG, "onConnected");
+        mConnectedSignal.countDown();
+    }
+
 
     //
     // Netd Callback handling
@@ -164,6 +184,7 @@
 
     class NetdCallbackReceiver implements INativeDaemonConnectorCallbacks {
         public void onDaemonConnected() {
+            NetworkManagementService.this.onConnected();
             new Thread() {
                 public void run() {
                 }
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index 023da46..9401ff8 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -25,6 +25,7 @@
 import android.content.BroadcastReceiver;
 import android.content.ContentQueryMap;
 import android.content.ContentResolver;
+import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -36,6 +37,7 @@
 import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener;
 import android.hardware.SensorManager;
+import android.os.BatteryManager;
 import android.os.BatteryStats;
 import android.os.Binder;
 import android.os.Environment;
@@ -109,6 +111,9 @@
     // Cached secure settings; see updateSettingsValues()
     private int mShortKeylightDelay = SHORT_KEYLIGHT_DELAY_DEFAULT;
 
+    // Default timeout for screen off, if not found in settings database = 15 seconds.
+    private static final int DEFAULT_SCREEN_OFF_TIMEOUT = 15000;
+
     // flags for setPowerState
     private static final int SCREEN_ON_BIT          = 0x00000001;
     private static final int SCREEN_BRIGHT_BIT      = 0x00000002;
@@ -411,24 +416,28 @@
     }
 
     private class SettingsObserver implements Observer {
-        private int getInt(String name) {
-            return mSettings.getValues(name).getAsInteger(Settings.System.VALUE);
+        private int getInt(String name, int defValue) {
+            ContentValues values = mSettings.getValues(name);
+            Integer iVal = values != null ? values.getAsInteger(Settings.System.VALUE) : null;
+            return iVal != null ? iVal : defValue;
         }
 
         public void update(Observable o, Object arg) {
             synchronized (mLocks) {
-                // STAY_ON_WHILE_PLUGGED_IN
-                mStayOnConditions = getInt(STAY_ON_WHILE_PLUGGED_IN);
+                // STAY_ON_WHILE_PLUGGED_IN, default to when plugged into AC
+                mStayOnConditions = getInt(STAY_ON_WHILE_PLUGGED_IN,
+                        BatteryManager.BATTERY_PLUGGED_AC);
                 updateWakeLockLocked();
 
-                // SCREEN_OFF_TIMEOUT
-                mScreenOffTimeoutSetting = getInt(SCREEN_OFF_TIMEOUT);
+                // SCREEN_OFF_TIMEOUT, default to 15 seconds
+                mScreenOffTimeoutSetting = getInt(SCREEN_OFF_TIMEOUT, DEFAULT_SCREEN_OFF_TIMEOUT);
 
                  // DIM_SCREEN
                 //mDimScreen = getInt(DIM_SCREEN) != 0;
 
-                // SCREEN_BRIGHTNESS_MODE
-                setScreenBrightnessMode(getInt(SCREEN_BRIGHTNESS_MODE));
+                // SCREEN_BRIGHTNESS_MODE, default to manual
+                setScreenBrightnessMode(getInt(SCREEN_BRIGHTNESS_MODE,
+                        Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL));
 
                 // recalculate everything
                 setScreenOffTimeoutsLocked();
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 4f05007..5ca386b 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -255,7 +255,8 @@
             try {
                 Slog.i(TAG, "NetworkManagement Service");
                 ServiceManager.addService(
-                        Context.NETWORKMANAGEMENT_SERVICE, new NetworkManagementService(context));
+                        Context.NETWORKMANAGEMENT_SERVICE,
+                        NetworkManagementService.create(context));
             } catch (Throwable e) {
                 Slog.e(TAG, "Failure starting NetworkManagement Service", e);
             }
diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java
index 3bf6ee4..39ce0b6 100755
--- a/services/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/java/com/android/server/location/GpsLocationProvider.java
@@ -1171,7 +1171,10 @@
     private void reportAGpsStatus(int type, int status) {
         switch (status) {
             case GPS_REQUEST_AGPS_DATA_CONN:
-                 int result = mConnMgr.startUsingNetworkFeature(
+                // Set mAGpsDataConnectionState before calling startUsingNetworkFeature
+                //  to avoid a race condition with handleUpdateNetworkState()
+                mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPENING;
+                int result = mConnMgr.startUsingNetworkFeature(
                         ConnectivityManager.TYPE_MOBILE, Phone.FEATURE_ENABLE_SUPL);
                 if (result == Phone.APN_ALREADY_ACTIVE) {
                     if (mAGpsApn != null) {
@@ -1179,11 +1182,13 @@
                         mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPEN;
                     } else {
                         Log.e(TAG, "mAGpsApn not set when receiving Phone.APN_ALREADY_ACTIVE");
+                        mAGpsDataConnectionState = AGPS_DATA_CONNECTION_CLOSED;
                         native_agps_data_conn_failed();
                     }
                 } else if (result == Phone.APN_REQUEST_STARTED) {
-                    mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPENING;
+                    // Nothing to do here
                 } else {
+                    mAGpsDataConnectionState = AGPS_DATA_CONNECTION_CLOSED;
                     native_agps_data_conn_failed();
                 }
                 break;
diff --git a/services/java/com/android/server/sip/SipService.java b/services/java/com/android/server/sip/SipService.java
index a2ebc69..f1dcd5a 100644
--- a/services/java/com/android/server/sip/SipService.java
+++ b/services/java/com/android/server/sip/SipService.java
@@ -172,7 +172,7 @@
         SipSessionGroupExt group = mSipGroups.remove(localProfileUri);
         if (group != null) {
             notifyProfileRemoved(group.getLocalProfile());
-            group.closeToNotReceiveCalls();
+            group.close();
             if (isWifiOn() && !anyOpened()) releaseWifiLock();
         }
     }
@@ -449,9 +449,9 @@
             }
         }
 
-        public void closeToNotReceiveCalls() {
+        public void close() {
             mOpened = false;
-            mSipGroup.closeToNotReceiveCalls();
+            mSipGroup.close();
             mAutoRegistration.stop();
             if (DEBUG) Log.d(TAG, "   close: " + getUri() + ": "
                     + mIncomingCallBroadcastAction);
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
index 5d4dc58..b71cf13 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
@@ -16,6 +16,16 @@
 
 package com.android.internal.telephony.cdma;
 
+import com.android.internal.telephony.CommandException;
+import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.DataConnectionTracker;
+import com.android.internal.telephony.EventLogTags;
+import com.android.internal.telephony.IccCard;
+import com.android.internal.telephony.MccTable;
+import com.android.internal.telephony.ServiceStateTracker;
+import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.telephony.TelephonyProperties;
+
 import android.app.AlarmManager;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -38,19 +48,8 @@
 import android.text.TextUtils;
 import android.util.EventLog;
 import android.util.Log;
-import android.util.Config;
 import android.util.TimeUtils;
 
-import com.android.internal.telephony.CommandException;
-import com.android.internal.telephony.CommandsInterface;
-import com.android.internal.telephony.DataConnectionTracker;
-import com.android.internal.telephony.EventLogTags;
-import com.android.internal.telephony.IccCard;
-import com.android.internal.telephony.MccTable;
-import com.android.internal.telephony.ServiceStateTracker;
-import com.android.internal.telephony.TelephonyIntents;
-import com.android.internal.telephony.TelephonyProperties;
-
 import java.util.Arrays;
 import java.util.Calendar;
 import java.util.Date;
@@ -987,7 +986,7 @@
         mNeedFixZone = false;
 
         if (zone != null) {
-            if (getAutoTime()) {
+            if (getAutoTimeZone()) {
                 setAndBroadcastNetworkSetTimeZone(zone.getID());
             }
             saveNitzTimeZone(zone.getID());
@@ -1439,7 +1438,7 @@
             }
 
             if (zone != null) {
-                if (getAutoTime()) {
+                if (getAutoTimeZone()) {
                     setAndBroadcastNetworkSetTimeZone(zone.getID());
                 }
                 saveNitzTimeZone(zone.getID());
@@ -1529,6 +1528,14 @@
         }
     }
 
+    private boolean getAutoTimeZone() {
+        try {
+            return Settings.System.getInt(cr, Settings.System.AUTO_TIME_ZONE) > 0;
+        } catch (SettingNotFoundException snfe) {
+            return true;
+        }
+    }
+
     private void saveNitzTimeZone(String zoneId) {
         mSavedTimeZone = zoneId;
     }
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
index b639eea..83ad552 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
@@ -16,6 +16,17 @@
 
 package com.android.internal.telephony.gsm;
 
+import com.android.internal.telephony.CommandException;
+import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.DataConnectionTracker;
+import com.android.internal.telephony.EventLogTags;
+import com.android.internal.telephony.IccCard;
+import com.android.internal.telephony.MccTable;
+import com.android.internal.telephony.RILConstants;
+import com.android.internal.telephony.ServiceStateTracker;
+import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.telephony.TelephonyProperties;
+
 import android.app.AlarmManager;
 import android.app.Notification;
 import android.app.NotificationManager;
@@ -47,17 +58,6 @@
 import android.util.Log;
 import android.util.TimeUtils;
 
-import com.android.internal.telephony.CommandException;
-import com.android.internal.telephony.CommandsInterface;
-import com.android.internal.telephony.DataConnectionTracker;
-import com.android.internal.telephony.EventLogTags;
-import com.android.internal.telephony.IccCard;
-import com.android.internal.telephony.MccTable;
-import com.android.internal.telephony.RILConstants;
-import com.android.internal.telephony.ServiceStateTracker;
-import com.android.internal.telephony.TelephonyIntents;
-import com.android.internal.telephony.TelephonyProperties;
-
 import java.util.Arrays;
 import java.util.Calendar;
 import java.util.Date;
@@ -995,7 +995,7 @@
                     mNeedFixZone = false;
 
                     if (zone != null) {
-                        if (getAutoTime()) {
+                        if (getAutoTimeZone()) {
                             setAndBroadcastNetworkSetTimeZone(zone.getID());
                         }
                         saveNitzTimeZone(zone.getID());
@@ -1476,7 +1476,7 @@
             }
 
             if (zone != null) {
-                if (getAutoTime()) {
+                if (getAutoTimeZone()) {
                     setAndBroadcastNetworkSetTimeZone(zone.getID());
                 }
                 saveNitzTimeZone(zone.getID());
@@ -1546,6 +1546,15 @@
         }
     }
 
+    private boolean getAutoTimeZone() {
+        try {
+            return Settings.System.getInt(phone.getContext().getContentResolver(),
+                    Settings.System.AUTO_TIME_ZONE) > 0;
+        } catch (SettingNotFoundException snfe) {
+            return true;
+        }
+    }
+
     private void saveNitzTimeZone(String zoneId) {
         mSavedTimeZone = zoneId;
     }
diff --git a/voip/java/android/net/rtp/AudioCodec.java b/voip/java/android/net/rtp/AudioCodec.java
index 89e6aa9..4851a46 100644
--- a/voip/java/android/net/rtp/AudioCodec.java
+++ b/voip/java/android/net/rtp/AudioCodec.java
@@ -16,41 +16,133 @@
 
 package android.net.rtp;
 
-/** @hide */
+import java.util.Arrays;
+
+/**
+ * This class defines a collection of audio codecs to be used with
+ * {@link AudioStream}s. Their parameters are designed to be exchanged using
+ * Session Description Protocol (SDP). Most of the values listed here can be
+ * found in RFC 3551, while others are described in separated standards.
+ *
+ * <p>Few simple configurations are defined as public static instances for the
+ * convenience of direct uses. More complicated ones could be obtained using
+ * {@link #getCodec(int, String, String)}. For example, one can use the
+ * following snippet to create a mode-1-only AMR codec.</p>
+ * <pre>
+ * AudioCodec codec = AudioCodec.getCodec(100, "AMR/8000", "mode-set=1");
+ * </pre>
+ *
+ * @see AudioStream
+ * @hide
+ */
 public class AudioCodec {
-    public static final AudioCodec ULAW = new AudioCodec("PCMU", 8000, 160, 0);
-    public static final AudioCodec ALAW = new AudioCodec("PCMA", 8000, 160, 8);
+    /**
+     * The RTP payload type of the encoding.
+     */
+    public final int type;
 
     /**
-     * Returns system supported codecs.
+     * The encoding parameters to be used in the corresponding SDP attribute.
      */
-    public static AudioCodec[] getSystemSupportedCodecs() {
-        return new AudioCodec[] {AudioCodec.ULAW, AudioCodec.ALAW};
+    public final String rtpmap;
+
+    /**
+     * The format parameters to be used in the corresponding SDP attribute.
+     */
+    public final String fmtp;
+
+    /**
+     * G.711 u-law audio codec.
+     */
+    public static final AudioCodec PCMU = new AudioCodec(0, "PCMU/8000", null);
+
+    /**
+     * G.711 a-law audio codec.
+     */
+    public static final AudioCodec PCMA = new AudioCodec(8, "PCMA/8000", null);
+
+    /**
+     * GSM Full-Rate audio codec, also known as GSM-FR, GSM 06.10, GSM, or
+     * simply FR.
+     */
+    public static final AudioCodec GSM = new AudioCodec(3, "GSM/8000", null);
+
+    /**
+     * GSM Enhanced Full-Rate audio codec, also known as GSM-EFR, GSM 06.60, or
+     * simply EFR.
+     */
+    public static final AudioCodec GSM_EFR = new AudioCodec(96, "GSM-EFR/8000", null);
+
+    /**
+     * Adaptive Multi-Rate narrowband audio codec, also known as AMR or AMR-NB.
+     * Currently CRC, robust sorting, and interleaving are not supported. See
+     * more details about these features in RFC 4867.
+     */
+    public static final AudioCodec AMR = new AudioCodec(97, "AMR/8000", null);
+
+    // TODO: add rest of the codecs when the native part is done.
+    private static final AudioCodec[] sCodecs = {PCMU, PCMA};
+
+    private AudioCodec(int type, String rtpmap, String fmtp) {
+        this.type = type;
+        this.rtpmap = rtpmap;
+        this.fmtp = fmtp;
     }
 
     /**
-     * Returns the codec instance if it is supported by the system.
+     * Returns system supported audio codecs.
+     */
+    public static AudioCodec[] getCodecs() {
+        return Arrays.copyOf(sCodecs, sCodecs.length);
+    }
+
+    /**
+     * Creates an AudioCodec according to the given configuration.
      *
-     * @param name name of the codec
-     * @return the matched codec or null if the codec name is not supported by
-     *      the system
+     * @param type The payload type of the encoding defined in RTP/AVP.
+     * @param rtpmap The encoding parameters specified in the corresponding SDP
+     *     attribute, or null if it is not available.
+     * @param fmtp The format parameters specified in the corresponding SDP
+     *     attribute, or null if it is not available.
+     * @return The configured AudioCodec or {@code null} if it is not supported.
      */
-    public static AudioCodec getSystemSupportedCodec(String name) {
-        for (AudioCodec codec : getSystemSupportedCodecs()) {
-            if (codec.name.equals(name)) return codec;
+    public static AudioCodec getCodec(int type, String rtpmap, String fmtp) {
+        if (type < 0 || type > 127) {
+            return null;
         }
-        return null;
-    }
 
-    public final String name;
-    public final int sampleRate;
-    public final int sampleCount;
-    public final int defaultType;
+        AudioCodec hint = null;
+        if (rtpmap != null) {
+            String clue = rtpmap.trim().toUpperCase();
+            for (AudioCodec codec : sCodecs) {
+                if (clue.startsWith(codec.rtpmap)) {
+                    String channels = clue.substring(codec.rtpmap.length());
+                    if (channels.length() == 0 || channels.equals("/1")) {
+                        hint = codec;
+                    }
+                    break;
+                }
+            }
+        } else if (type < 96) {
+            for (AudioCodec codec : sCodecs) {
+                if (type == codec.type) {
+                    hint = codec;
+                    rtpmap = codec.rtpmap;
+                    break;
+                }
+            }
+        }
 
-    private AudioCodec(String name, int sampleRate, int sampleCount, int defaultType) {
-        this.name = name;
-        this.sampleRate = sampleRate;
-        this.sampleCount = sampleCount;
-        this.defaultType = defaultType;
+        if (hint == null) {
+            return null;
+        }
+        if (hint == AMR && fmtp != null) {
+            String clue = fmtp.toLowerCase();
+            if (clue.contains("crc=1") || clue.contains("robust-sorting=1") ||
+                    clue.contains("interleaving=")) {
+                return null;
+            }
+        }
+        return new AudioCodec(type, rtpmap, fmtp);
     }
 }
diff --git a/voip/java/android/net/rtp/AudioGroup.java b/voip/java/android/net/rtp/AudioGroup.java
index 37cc121..43a3827 100644
--- a/voip/java/android/net/rtp/AudioGroup.java
+++ b/voip/java/android/net/rtp/AudioGroup.java
@@ -20,13 +20,63 @@
 import java.util.Map;
 
 /**
+ * An AudioGroup acts as a router connected to the speaker, the microphone, and
+ * {@link AudioStream}s. Its pipeline has four steps. First, for each
+ * AudioStream not in {@link RtpStream#MODE_SEND_ONLY}, decodes its incoming
+ * packets and stores in its buffer. Then, if the microphone is enabled,
+ * processes the recorded audio and stores in its buffer. Third, if the speaker
+ * is enabled, mixes and playbacks buffers of all AudioStreams. Finally, for
+ * each AudioStream not in {@link RtpStream#MODE_RECEIVE_ONLY}, mixes all other
+ * buffers and sends back the encoded packets. An AudioGroup does nothing if
+ * there is no AudioStream in it.
+ *
+ * <p>Few things must be noticed before using these classes. The performance is
+ * highly related to the system load and the network bandwidth. Usually a
+ * simpler {@link AudioCodec} costs fewer CPU cycles but requires more network
+ * bandwidth, and vise versa. Using two AudioStreams at the same time not only
+ * doubles the load but also the bandwidth. The condition varies from one device
+ * to another, and developers must choose the right combination in order to get
+ * the best result.
+ *
+ * <p>It is sometimes useful to keep multiple AudioGroups at the same time. For
+ * example, a Voice over IP (VoIP) application might want to put a conference
+ * call on hold in order to make a new call but still allow people in the
+ * previous call to talk to each other. This can be done easily using two
+ * AudioGroups, but there are some limitations. Since the speaker and the
+ * microphone are shared globally, only one AudioGroup is allowed to run in
+ * modes other than {@link #MODE_ON_HOLD}. In addition, before adding an
+ * AudioStream into an AudioGroup, one should always put all other AudioGroups
+ * into {@link #MODE_ON_HOLD}. That will make sure the audio driver correctly
+ * initialized.
+ * @hide
  */
-/** @hide */
 public class AudioGroup {
+    /**
+     * This mode is similar to {@link #MODE_NORMAL} except the speaker and
+     * the microphone are disabled.
+     */
     public static final int MODE_ON_HOLD = 0;
+
+    /**
+     * This mode is similar to {@link #MODE_NORMAL} except the microphone is
+     * muted.
+     */
     public static final int MODE_MUTED = 1;
+
+    /**
+     * This mode indicates that the speaker, the microphone, and all
+     * {@link AudioStream}s in the group are enabled. First, the packets
+     * received from the streams are decoded and mixed with the audio recorded
+     * from the microphone. Then, the results are played back to the speaker,
+     * encoded and sent back to each stream.
+     */
     public static final int MODE_NORMAL = 2;
-    public static final int MODE_EC_ENABLED = 3;
+
+    /**
+     * This mode is similar to {@link #MODE_NORMAL} except the echo suppression
+     * is enabled. It should be only used when the speaker phone is on.
+     */
+    public static final int MODE_ECHO_SUPPRESSION = 3;
 
     private final Map<AudioStream, Integer> mStreams;
     private int mMode = MODE_ON_HOLD;
@@ -36,23 +86,42 @@
         System.loadLibrary("rtp_jni");
     }
 
+    /**
+     * Creates an empty AudioGroup.
+     */
     public AudioGroup() {
         mStreams = new HashMap<AudioStream, Integer>();
     }
 
+    /**
+     * Returns the current mode.
+     */
     public int getMode() {
         return mMode;
     }
 
+    /**
+     * Changes the current mode. It must be one of {@link #MODE_ON_HOLD},
+     * {@link #MODE_MUTED}, {@link #MODE_NORMAL}, and
+     * {@link #MODE_ECHO_SUPPRESSION}.
+     *
+     * @param mode The mode to change to.
+     * @throws IllegalArgumentException if the mode is invalid.
+     */
     public synchronized native void setMode(int mode);
 
-    synchronized void add(AudioStream stream, AudioCodec codec, int codecType, int dtmfType) {
+    private native void add(int mode, int socket, String remoteAddress,
+            int remotePort, String codecSpec, int dtmfType);
+
+    synchronized void add(AudioStream stream, AudioCodec codec, int dtmfType) {
         if (!mStreams.containsKey(stream)) {
             try {
                 int socket = stream.dup();
+                String codecSpec = String.format("%d %s %s", codec.type,
+                        codec.rtpmap, codec.fmtp);
                 add(stream.getMode(), socket,
-                        stream.getRemoteAddress().getHostAddress(), stream.getRemotePort(),
-                        codec.name, codec.sampleRate, codec.sampleCount, codecType, dtmfType);
+                        stream.getRemoteAddress().getHostAddress(),
+                        stream.getRemotePort(), codecSpec, dtmfType);
                 mStreams.put(stream, socket);
             } catch (NullPointerException e) {
                 throw new IllegalStateException(e);
@@ -60,8 +129,7 @@
         }
     }
 
-    private native void add(int mode, int socket, String remoteAddress, int remotePort,
-            String codecName, int sampleRate, int sampleCount, int codecType, int dtmfType);
+    private native void remove(int socket);
 
     synchronized void remove(AudioStream stream) {
         Integer socket = mStreams.remove(stream);
@@ -70,8 +138,6 @@
         }
     }
 
-    private native void remove(int socket);
-
     /**
      * Sends a DTMF digit to every {@link AudioStream} in this group. Currently
      * only event {@code 0} to {@code 15} are supported.
@@ -80,13 +146,16 @@
      */
     public native synchronized void sendDtmf(int event);
 
-    public synchronized void reset() {
+    /**
+     * Removes every {@link AudioStream} in this group.
+     */
+    public synchronized void clear() {
         remove(-1);
     }
 
     @Override
     protected void finalize() throws Throwable {
-        reset();
+        clear();
         super.finalize();
     }
 }
diff --git a/voip/java/android/net/rtp/AudioStream.java b/voip/java/android/net/rtp/AudioStream.java
index a955fd2..e5197ce 100644
--- a/voip/java/android/net/rtp/AudioStream.java
+++ b/voip/java/android/net/rtp/AudioStream.java
@@ -20,12 +20,27 @@
 import java.net.SocketException;
 
 /**
- * AudioStream represents a RTP stream carrying audio payloads.
+ * An AudioStream is a {@link RtpStream} which carrys audio payloads over
+ * Real-time Transport Protocol (RTP). Two different classes are developed in
+ * order to support various usages such as audio conferencing. An AudioStream
+ * represents a remote endpoint which consists of a network mapping and a
+ * configured {@link AudioCodec}. On the other side, An {@link AudioGroup}
+ * represents a local endpoint which mixes all the AudioStreams and optionally
+ * interacts with the speaker and the microphone at the same time. The simplest
+ * usage includes one for each endpoints. For other combinations, users should
+ * be aware of the limitations described in {@link AudioGroup}.
+ *
+ * <p>An AudioStream becomes busy when it joins an AudioGroup. In this case most
+ * of the setter methods are disabled. This is designed to ease the task of
+ * managing native resources. One can always make an AudioStream leave its
+ * AudioGroup by calling {@link #join(AudioGroup)} with {@code null} and put it
+ * back after the modification is done.
+ *
+ * @see AudioGroup
+ * @hide
  */
-/** @hide */
 public class AudioStream extends RtpStream {
     private AudioCodec mCodec;
-    private int mCodecType = -1;
     private int mDtmfType = -1;
     private AudioGroup mGroup;
 
@@ -42,7 +57,8 @@
     }
 
     /**
-     * Returns {@code true} if the stream already joined an {@link AudioGroup}.
+     * Returns {@code true} if the stream has already joined an
+     * {@link AudioGroup}.
      */
     @Override
     public final boolean isBusy() {
@@ -52,7 +68,7 @@
     /**
      * Returns the joined {@link AudioGroup}.
      */
-    public AudioGroup getAudioGroup() {
+    public AudioGroup getGroup() {
         return mGroup;
     }
 
@@ -74,35 +90,45 @@
             mGroup = null;
         }
         if (group != null) {
-            group.add(this, mCodec, mCodecType, mDtmfType);
+            group.add(this, mCodec, mDtmfType);
             mGroup = group;
         }
     }
 
     /**
-     * Sets the {@link AudioCodec} and its RTP payload type. According to RFC
-     * 3551, the type must be in the range of 0 and 127, where 96 and above are
-     * dynamic types. For codecs with static mappings (non-negative
-     * {@link AudioCodec#defaultType}), assigning a different non-dynamic type
-     * is disallowed.
+     * Returns the {@link AudioCodec}, or {@code null} if it is not set.
+     *
+     * @see #setCodec(AudioCodec)
+     */
+    public AudioCodec getCodec() {
+        return mCodec;
+    }
+
+    /**
+     * Sets the {@link AudioCodec}.
      *
      * @param codec The AudioCodec to be used.
-     * @param type The RTP payload type.
-     * @throws IllegalArgumentException if the type is invalid or used by DTMF.
+     * @throws IllegalArgumentException if its type is used by DTMF.
      * @throws IllegalStateException if the stream is busy.
      */
-    public void setCodec(AudioCodec codec, int type) {
+    public void setCodec(AudioCodec codec) {
         if (isBusy()) {
             throw new IllegalStateException("Busy");
         }
-        if (type < 0 || type > 127 || (type != codec.defaultType && type < 96)) {
-            throw new IllegalArgumentException("Invalid type");
-        }
-        if (type == mDtmfType) {
+        if (codec.type == mDtmfType) {
             throw new IllegalArgumentException("The type is used by DTMF");
         }
         mCodec = codec;
-        mCodecType = type;
+    }
+
+    /**
+     * Returns the RTP payload type for dual-tone multi-frequency (DTMF) digits,
+     * or {@code -1} if it is not enabled.
+     *
+     * @see #setDtmfType(int)
+     */
+    public int getDtmfType() {
+        return mDtmfType;
     }
 
     /**
@@ -111,7 +137,7 @@
      * certain tasks, such as second-stage dialing. According to RFC 2833, the
      * RTP payload type for DTMF is assigned dynamically, so it must be in the
      * range of 96 and 127. One can use {@code -1} to disable DTMF and free up
-     * the previous assigned value. This method cannot be called when the stream
+     * the previous assigned type. This method cannot be called when the stream
      * already joined an {@link AudioGroup}.
      *
      * @param type The RTP payload type to be used or {@code -1} to disable it.
@@ -127,7 +153,7 @@
             if (type < 96 || type > 127) {
                 throw new IllegalArgumentException("Invalid type");
             }
-            if (type == mCodecType) {
+            if (type == mCodec.type) {
                 throw new IllegalArgumentException("The type is used by codec");
             }
         }
diff --git a/voip/java/android/net/rtp/RtpStream.java b/voip/java/android/net/rtp/RtpStream.java
index ef5ca17..23fb258 100644
--- a/voip/java/android/net/rtp/RtpStream.java
+++ b/voip/java/android/net/rtp/RtpStream.java
@@ -22,13 +22,25 @@
 import java.net.SocketException;
 
 /**
- * RtpStream represents a base class of media streams running over
- * Real-time Transport Protocol (RTP).
+ * RtpStream represents the base class of streams which send and receive network
+ * packets with media payloads over Real-time Transport Protocol (RTP).
+ * @hide
  */
-/** @hide */
 public class RtpStream {
+    /**
+     * This mode indicates that the stream sends and receives packets at the
+     * same time. This is the initial mode for new streams.
+     */
     public static final int MODE_NORMAL = 0;
+
+    /**
+     * This mode indicates that the stream only sends packets.
+     */
     public static final int MODE_SEND_ONLY = 1;
+
+    /**
+     * This mode indicates that the stream only receives packets.
+     */
     public static final int MODE_RECEIVE_ONLY = 2;
 
     private final InetAddress mLocalAddress;
@@ -89,15 +101,16 @@
     }
 
     /**
-     * Returns {@code true} if the stream is busy. This method is intended to be
-     * overridden by subclasses.
+     * Returns {@code true} if the stream is busy. In this case most of the
+     * setter methods are disabled. This method is intended to be overridden
+     * by subclasses.
      */
     public boolean isBusy() {
         return false;
     }
 
     /**
-     * Returns the current mode. The initial mode is {@link #MODE_NORMAL}.
+     * Returns the current mode.
      */
     public int getMode() {
         return mMode;
@@ -123,7 +136,8 @@
     }
 
     /**
-     * Associates with a remote host.
+     * Associates with a remote host. This defines the destination of the
+     * outgoing packets.
      *
      * @param address The network address of the remote host.
      * @param port The network port of the remote host.
diff --git a/voip/java/android/net/sip/SimpleSessionDescription.java b/voip/java/android/net/sip/SimpleSessionDescription.java
new file mode 100644
index 0000000..29166dc
--- /dev/null
+++ b/voip/java/android/net/sip/SimpleSessionDescription.java
@@ -0,0 +1,612 @@
+/*
+ * 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.net.sip;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * An object used to manipulate messages of Session Description Protocol (SDP).
+ * It is mainly designed for the uses of Session Initiation Protocol (SIP).
+ * Therefore, it only handles connection addresses ("c="), bandwidth limits,
+ * ("b="), encryption keys ("k="), and attribute fields ("a="). Currently this
+ * implementation does not support multicast sessions.
+ *
+ * <p>Here is an example code to create a session description.</p>
+ * <pre>
+ * SimpleSessionDescription description = new SimpleSessionDescription(
+ *     System.currentTimeMillis(), "1.2.3.4");
+ * Media media = description.newMedia("audio", 56789, 1, "RTP/AVP");
+ * media.setRtpPayload(0, "PCMU/8000", null);
+ * media.setRtpPayload(8, "PCMA/8000", null);
+ * media.setRtpPayload(127, "telephone-event/8000", "0-15");
+ * media.setAttribute("sendrecv", "");
+ * </pre>
+ * <p>Invoking <code>description.encode()</code> will produce a result like the
+ * one below.</p>
+ * <pre>
+ * v=0
+ * o=- 1284970442706 1284970442709 IN IP4 1.2.3.4
+ * s=-
+ * c=IN IP4 1.2.3.4
+ * t=0 0
+ * m=audio 56789 RTP/AVP 0 8 127
+ * a=rtpmap:0 PCMU/8000
+ * a=rtpmap:8 PCMA/8000
+ * a=rtpmap:127 telephone-event/8000
+ * a=fmtp:127 0-15
+ * a=sendrecv
+ * </pre>
+ * @hide
+ */
+public class SimpleSessionDescription {
+    private final Fields mFields = new Fields("voscbtka");
+    private final ArrayList<Media> mMedia = new ArrayList<Media>();
+
+    /**
+     * Creates a minimal session description from the given session ID and
+     * unicast address. The address is used in the origin field ("o=") and the
+     * connection field ("c="). See {@link SimpleSessionDescription} for an
+     * example of its usage.
+     */
+    public SimpleSessionDescription(long sessionId, String address) {
+        address = (address.indexOf(':') < 0 ? "IN IP4 " : "IN IP6 ") + address;
+        mFields.parse("v=0");
+        mFields.parse(String.format("o=- %d %d %s", sessionId,
+                System.currentTimeMillis(), address));
+        mFields.parse("s=-");
+        mFields.parse("t=0 0");
+        mFields.parse("c=" + address);
+    }
+
+    /**
+     * Creates a session description from the given message.
+     *
+     * @throws IllegalArgumentException if message is invalid.
+     */
+    public SimpleSessionDescription(String message) {
+        String[] lines = message.trim().replaceAll(" +", " ").split("[\r\n]+");
+        Fields fields = mFields;
+
+        for (String line : lines) {
+            try {
+                if (line.charAt(1) != '=') {
+                    throw new IllegalArgumentException();
+                }
+                if (line.charAt(0) == 'm') {
+                    String[] parts = line.substring(2).split(" ", 4);
+                    String[] ports = parts[1].split("/", 2);
+                    Media media = newMedia(parts[0], Integer.parseInt(ports[0]),
+                            (ports.length < 2) ? 1 : Integer.parseInt(ports[1]),
+                            parts[2]);
+                    for (String format : parts[3].split(" ")) {
+                        media.setFormat(format, null);
+                    }
+                    fields = media;
+                } else {
+                    fields.parse(line);
+                }
+            } catch (Exception e) {
+                throw new IllegalArgumentException("Invalid SDP: " + line);
+            }
+        }
+    }
+
+    /**
+     * Creates a new media description in this session description.
+     *
+     * @param type The media type, e.g. {@code "audio"}.
+     * @param port The first transport port used by this media.
+     * @param portCount The number of contiguous ports used by this media.
+     * @param protocol The transport protocol, e.g. {@code "RTP/AVP"}.
+     */
+    public Media newMedia(String type, int port, int portCount,
+            String protocol) {
+        Media media = new Media(type, port, portCount, protocol);
+        mMedia.add(media);
+        return media;
+    }
+
+    /**
+     * Returns all the media descriptions in this session description.
+     */
+    public Media[] getMedia() {
+        return mMedia.toArray(new Media[mMedia.size()]);
+    }
+
+    /**
+     * Encodes the session description and all its media descriptions in a
+     * string. Note that the result might be incomplete if a required field
+     * has never been added before.
+     */
+    public String encode() {
+        StringBuilder buffer = new StringBuilder();
+        mFields.write(buffer);
+        for (Media media : mMedia) {
+            media.write(buffer);
+        }
+        return buffer.toString();
+    }
+
+    /**
+     * Returns the connection address or {@code null} if it is not present.
+     */
+    public String getAddress() {
+        return mFields.getAddress();
+    }
+
+    /**
+     * Sets the connection address. The field will be removed if the address
+     * is {@code null}.
+     */
+    public void setAddress(String address) {
+        mFields.setAddress(address);
+    }
+
+    /**
+     * Returns the encryption method or {@code null} if it is not present.
+     */
+    public String getEncryptionMethod() {
+        return mFields.getEncryptionMethod();
+    }
+
+    /**
+     * Returns the encryption key or {@code null} if it is not present.
+     */
+    public String getEncryptionKey() {
+        return mFields.getEncryptionKey();
+    }
+
+    /**
+     * Sets the encryption method and the encryption key. The field will be
+     * removed if the method is {@code null}.
+     */
+    public void setEncryption(String method, String key) {
+        mFields.setEncryption(method, key);
+    }
+
+    /**
+     * Returns the types of the bandwidth limits.
+     */
+    public String[] getBandwidthTypes() {
+        return mFields.getBandwidthTypes();
+    }
+
+    /**
+     * Returns the bandwidth limit of the given type or {@code -1} if it is not
+     * present.
+     */
+    public int getBandwidth(String type) {
+        return mFields.getBandwidth(type);
+    }
+
+    /**
+     * Sets the bandwith limit for the given type. The field will be removed if
+     * the value is negative.
+     */
+    public void setBandwidth(String type, int value) {
+        mFields.setBandwidth(type, value);
+    }
+
+    /**
+     * Returns the names of all the attributes.
+     */
+    public String[] getAttributeNames() {
+        return mFields.getAttributeNames();
+    }
+
+    /**
+     * Returns the attribute of the given name or {@code null} if it is not
+     * present.
+     */
+    public String getAttribute(String name) {
+        return mFields.getAttribute(name);
+    }
+
+    /**
+     * Sets the attribute for the given name. The field will be removed if
+     * the value is {@code null}. To set a binary attribute, use an empty
+     * string as the value.
+     */
+    public void setAttribute(String name, String value) {
+        mFields.setAttribute(name, value);
+    }
+
+    /**
+     * This class represents a media description of a session description. It
+     * can only be created by {@link SimpleSessionDescription#newMedia}. Since
+     * the syntax is more restricted for RTP based protocols, two sets of access
+     * methods are implemented. See {@link SimpleSessionDescription} for an
+     * example of its usage.
+     */
+    public static class Media extends Fields {
+        private final String mType;
+        private final int mPort;
+        private final int mPortCount;
+        private final String mProtocol;
+        private ArrayList<String> mFormats = new ArrayList<String>();
+
+        private Media(String type, int port, int portCount, String protocol) {
+            super("icbka");
+            mType = type;
+            mPort = port;
+            mPortCount = portCount;
+            mProtocol = protocol;
+        }
+
+        /**
+         * Returns the media type.
+         */
+        public String getType() {
+            return mType;
+        }
+
+        /**
+         * Returns the first transport port used by this media.
+         */
+        public int getPort() {
+            return mPort;
+        }
+
+        /**
+         * Returns the number of contiguous ports used by this media.
+         */
+        public int getPortCount() {
+            return mPortCount;
+        }
+
+        /**
+         * Returns the transport protocol.
+         */
+        public String getProtocol() {
+            return mProtocol;
+        }
+
+        /**
+         * Returns the media formats.
+         */
+        public String[] getFormats() {
+            return mFormats.toArray(new String[mFormats.size()]);
+        }
+
+        /**
+         * Returns the {@code fmtp} attribute of the given format or
+         * {@code null} if it is not present.
+         */
+        public String getFmtp(String format) {
+            return super.get("a=fmtp:" + format, ' ');
+        }
+
+        /**
+         * Sets a format and its {@code fmtp} attribute. If the attribute is
+         * {@code null}, the corresponding field will be removed.
+         */
+        public void setFormat(String format, String fmtp) {
+            mFormats.remove(format);
+            mFormats.add(format);
+            super.set("a=rtpmap:" + format, ' ', null);
+            super.set("a=fmtp:" + format, ' ', fmtp);
+        }
+
+        /**
+         * Removes a format and its {@code fmtp} attribute.
+         */
+        public void removeFormat(String format) {
+            mFormats.remove(format);
+            super.set("a=rtpmap:" + format, ' ', null);
+            super.set("a=fmtp:" + format, ' ', null);
+        }
+
+        /**
+         * Returns the RTP payload types.
+         */
+        public int[] getRtpPayloadTypes() {
+            int[] types = new int[mFormats.size()];
+            int length = 0;
+            for (String format : mFormats) {
+                try {
+                    types[length] = Integer.parseInt(format);
+                    ++length;
+                } catch (NumberFormatException e) { }
+            }
+            return Arrays.copyOf(types, length);
+        }
+
+        /**
+         * Returns the {@code rtpmap} attribute of the given RTP payload type
+         * or {@code null} if it is not present.
+         */
+        public String getRtpmap(int type) {
+            return super.get("a=rtpmap:" + type, ' ');
+        }
+
+        /**
+         * Returns the {@code fmtp} attribute of the given RTP payload type or
+         * {@code null} if it is not present.
+         */
+        public String getFmtp(int type) {
+            return super.get("a=fmtp:" + type, ' ');
+        }
+
+        /**
+         * Sets a RTP payload type and its {@code rtpmap} and {@code fmtp}
+         * attributes. If any of the attributes is {@code null}, the
+         * corresponding field will be removed. See
+         * {@link SimpleSessionDescription} for an example of its usage.
+         */
+        public void setRtpPayload(int type, String rtpmap, String fmtp) {
+            String format = String.valueOf(type);
+            mFormats.remove(format);
+            mFormats.add(format);
+            super.set("a=rtpmap:" + format, ' ', rtpmap);
+            super.set("a=fmtp:" + format, ' ', fmtp);
+        }
+
+        /**
+         * Removes a RTP payload and its {@code rtpmap} and {@code fmtp}
+         * attributes.
+         */
+        public void removeRtpPayload(int type) {
+            removeFormat(String.valueOf(type));
+        }
+
+        private void write(StringBuilder buffer) {
+            buffer.append("m=").append(mType).append(' ').append(mPort);
+            if (mPortCount != 1) {
+                buffer.append('/').append(mPortCount);
+            }
+            buffer.append(' ').append(mProtocol);
+            for (String format : mFormats) {
+                buffer.append(' ').append(format);
+            }
+            buffer.append("\r\n");
+            super.write(buffer);
+        }
+    }
+
+    /**
+     * This class acts as a set of fields, and the size of the set is expected
+     * to be small. Therefore, it uses a simple list instead of maps. Each field
+     * has three parts: a key, a delimiter, and a value. Delimiters are special
+     * because they are not included in binary attributes. As a result, the
+     * private methods, which are the building blocks of this class, all take
+     * the delimiter as an argument.
+     */
+    private static class Fields {
+        private final String mOrder;
+        private final ArrayList<String> mLines = new ArrayList<String>();
+
+        Fields(String order) {
+            mOrder = order;
+        }
+
+        /**
+         * Returns the connection address or {@code null} if it is not present.
+         */
+        public String getAddress() {
+            String address = get("c", '=');
+            if (address == null) {
+                return null;
+            }
+            String[] parts = address.split(" ");
+            if (parts.length != 3) {
+                return null;
+            }
+            int slash = parts[2].indexOf('/');
+            return (slash < 0) ? parts[2] : parts[2].substring(0, slash);
+        }
+
+        /**
+         * Sets the connection address. The field will be removed if the address
+         * is {@code null}.
+         */
+        public void setAddress(String address) {
+            if (address != null) {
+                address = (address.indexOf(':') < 0 ? "IN IP4 " : "IN IP6 ") +
+                        address;
+            }
+            set("c", '=', address);
+        }
+
+        /**
+         * Returns the encryption method or {@code null} if it is not present.
+         */
+        public String getEncryptionMethod() {
+            String encryption = get("k", '=');
+            if (encryption == null) {
+                return null;
+            }
+            int colon = encryption.indexOf(':');
+            return (colon == -1) ? encryption : encryption.substring(0, colon);
+        }
+
+        /**
+         * Returns the encryption key or {@code null} if it is not present.
+         */
+        public String getEncryptionKey() {
+            String encryption = get("k", '=');
+            if (encryption == null) {
+                return null;
+            }
+            int colon = encryption.indexOf(':');
+            return (colon == -1) ? null : encryption.substring(0, colon + 1);
+        }
+
+        /**
+         * Sets the encryption method and the encryption key. The field will be
+         * removed if the method is {@code null}.
+         */
+        public void setEncryption(String method, String key) {
+            set("k", '=', (method == null || key == null) ?
+                    method : method + ':' + key);
+        }
+
+        /**
+         * Returns the types of the bandwidth limits.
+         */
+        public String[] getBandwidthTypes() {
+            return cut("b=", ':');
+        }
+
+        /**
+         * Returns the bandwidth limit of the given type or {@code -1} if it is
+         * not present.
+         */
+        public int getBandwidth(String type) {
+            String value = get("b=" + type, ':');
+            if (value != null) {
+                try {
+                    return Integer.parseInt(value);
+                } catch (NumberFormatException e) { }
+                setBandwidth(type, -1);
+            }
+            return -1;
+        }
+
+        /**
+         * Sets the bandwith limit for the given type. The field will be removed
+         * if the value is negative.
+         */
+        public void setBandwidth(String type, int value) {
+            set("b=" + type, ':', (value < 0) ? null : String.valueOf(value));
+        }
+
+        /**
+         * Returns the names of all the attributes.
+         */
+        public String[] getAttributeNames() {
+            return cut("a=", ':');
+        }
+
+        /**
+         * Returns the attribute of the given name or {@code null} if it is not
+         * present.
+         */
+        public String getAttribute(String name) {
+            return get("a=" + name, ':');
+        }
+
+        /**
+         * Sets the attribute for the given name. The field will be removed if
+         * the value is {@code null}. To set a binary attribute, use an empty
+         * string as the value.
+         */
+        public void setAttribute(String name, String value) {
+            set("a=" + name, ':', value);
+        }
+
+        private void write(StringBuilder buffer) {
+            for (int i = 0; i < mOrder.length(); ++i) {
+                char type = mOrder.charAt(i);
+                for (String line : mLines) {
+                    if (line.charAt(0) == type) {
+                        buffer.append(line).append("\r\n");
+                    }
+                }
+            }
+        }
+
+        /**
+         * Invokes {@link #set} after splitting the line into three parts.
+         */
+        private void parse(String line) {
+            char type = line.charAt(0);
+            if (mOrder.indexOf(type) == -1) {
+                return;
+            }
+            char delimiter = '=';
+            if (line.startsWith("a=rtpmap:") || line.startsWith("a=fmtp:")) {
+                delimiter = ' ';
+            } else if (type == 'b' || type == 'a') {
+                delimiter = ':';
+            }
+            int i = line.indexOf(delimiter);
+            if (i == -1) {
+                set(line, delimiter, "");
+            } else {
+                set(line.substring(0, i), delimiter, line.substring(i + 1));
+            }
+        }
+
+        /**
+         * Finds the key with the given prefix and returns its suffix.
+         */
+        private String[] cut(String prefix, char delimiter) {
+            String[] names = new String[mLines.size()];
+            int length = 0;
+            for (String line : mLines) {
+                if (line.startsWith(prefix)) {
+                    int i = line.indexOf(delimiter);
+                    if (i == -1) {
+                        i = line.length();
+                    }
+                    names[length] = line.substring(prefix.length(), i);
+                    ++length;
+                }
+            }
+            return Arrays.copyOf(names, length);
+        }
+
+        /**
+         * Returns the index of the key.
+         */
+        private int find(String key, char delimiter) {
+            int length = key.length();
+            for (int i = mLines.size() - 1; i >= 0; --i) {
+                String line = mLines.get(i);
+                if (line.startsWith(key) && (line.length() == length ||
+                        line.charAt(length) == delimiter)) {
+                    return i;
+                }
+            }
+            return -1;
+        }
+
+        /**
+         * Sets the key with the value or removes the key if the value is
+         * {@code null}.
+         */
+        private void set(String key, char delimiter, String value) {
+            int index = find(key, delimiter);
+            if (value != null) {
+                if (value.length() != 0) {
+                    key = key + delimiter + value;
+                }
+                if (index == -1) {
+                    mLines.add(key);
+                } else {
+                    mLines.set(index, key);
+                }
+            } else if (index != -1) {
+                mLines.remove(index);
+            }
+        }
+
+        /**
+         * Returns the value of the key.
+         */
+        private String get(String key, char delimiter) {
+            int index = find(key, delimiter);
+            if (index == -1) {
+                return null;
+            }
+            String line = mLines.get(index);
+            int length = key.length();
+            return (line.length() == length) ? "" : line.substring(length + 1);
+        }
+    }
+}
diff --git a/voip/java/android/net/sip/SipAudioCallImpl.java b/voip/java/android/net/sip/SipAudioCallImpl.java
index 8cd41db..5eecc05 100644
--- a/voip/java/android/net/sip/SipAudioCallImpl.java
+++ b/voip/java/android/net/sip/SipAudioCallImpl.java
@@ -16,8 +16,6 @@
 
 package android.net.sip;
 
-import gov.nist.javax.sdp.fields.SDPKeywords;
-
 import android.content.Context;
 import android.media.AudioManager;
 import android.media.Ringtone;
@@ -28,6 +26,7 @@
 import android.net.rtp.AudioGroup;
 import android.net.rtp.AudioStream;
 import android.net.rtp.RtpStream;
+import android.net.sip.SimpleSessionDescription.Media;
 import android.net.wifi.WifiManager;
 import android.os.Message;
 import android.os.RemoteException;
@@ -38,15 +37,13 @@
 import java.io.IOException;
 import java.net.InetAddress;
 import java.net.UnknownHostException;
-import java.text.ParseException;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import javax.sdp.SdpException;
 
 /**
- * Class that handles an audio call over SIP. 
+ * Class that handles an audio call over SIP.
  */
 /** @hide */
 public class SipAudioCallImpl extends SipSessionAdapter
@@ -54,20 +51,19 @@
     private static final String TAG = SipAudioCallImpl.class.getSimpleName();
     private static final boolean RELEASE_SOCKET = true;
     private static final boolean DONT_RELEASE_SOCKET = false;
-    private static final String AUDIO = "audio";
-    private static final int DTMF = 101;
     private static final int SESSION_TIMEOUT = 5; // in seconds
 
     private Context mContext;
     private SipProfile mLocalProfile;
     private SipAudioCall.Listener mListener;
     private ISipSession mSipSession;
-    private SdpSessionDescription mPeerSd;
+
+    private long mSessionId = System.currentTimeMillis();
+    private String mPeerSd;
 
     private AudioStream mAudioStream;
     private AudioGroup mAudioGroup;
-    private SdpSessionDescription.AudioCodec mCodec;
-    private long mSessionId = -1L; // SDP session ID
+
     private boolean mInCall = false;
     private boolean mMuted = false;
     private boolean mHold = false;
@@ -146,7 +142,7 @@
 
         mInCall = false;
         mHold = false;
-        mSessionId = -1L;
+        mSessionId = System.currentTimeMillis();
         mErrorCode = SipErrorCode.NO_ERROR;
         mErrorMessage = null;
 
@@ -226,8 +222,8 @@
 
             // session changing request
             try {
-                mPeerSd = new SdpSessionDescription(sessionDescription);
-                answerCall(SESSION_TIMEOUT);
+                String answer = createAnswer(sessionDescription).encode();
+                mSipSession.answerCall(answer, SESSION_TIMEOUT);
             } catch (Throwable e) {
                 Log.e(TAG, "onRinging()", e);
                 session.endCall();
@@ -242,12 +238,8 @@
             String sessionDescription) {
         stopRingbackTone();
         stopRinging();
-        try {
-            mPeerSd = new SdpSessionDescription(sessionDescription);
-            Log.d(TAG, "sip call established: " + mPeerSd);
-        } catch (SdpException e) {
-            Log.e(TAG, "createSessionDescription()", e);
-        }
+        mPeerSd = sessionDescription;
+        Log.v(TAG, "onCallEstablished()" + mPeerSd);
 
         Listener listener = mListener;
         if (listener != null) {
@@ -332,10 +324,10 @@
     public synchronized void attachCall(ISipSession session,
             String sessionDescription) throws SipException {
         mSipSession = session;
+        mPeerSd = sessionDescription;
+        Log.v(TAG, "attachCall()" + mPeerSd);
         try {
-            mPeerSd = new SdpSessionDescription(sessionDescription);
             session.setListener(this);
-
             if (getState() == SipSessionState.INCOMING_CALL) startRinging();
         } catch (Throwable e) {
             Log.e(TAG, "attachCall()", e);
@@ -351,8 +343,8 @@
                 throw new SipException(
                         "Failed to create SipSession; network available?");
             }
-            mSipSession.makeCall(peerProfile, createOfferSessionDescription(),
-                    timeout);
+            mAudioStream = new AudioStream(InetAddress.getByName(getLocalIp()));
+            mSipSession.makeCall(peerProfile, createOffer().encode(), timeout);
         } catch (Throwable e) {
             if (e instanceof SipException) {
                 throw (SipException) e;
@@ -365,7 +357,7 @@
     public synchronized void endCall() throws SipException {
         try {
             stopRinging();
-            stopCall(true);
+            stopCall(RELEASE_SOCKET);
             mInCall = false;
 
             // perform the above local ops first and then network op
@@ -375,123 +367,131 @@
         }
     }
 
-    public synchronized void holdCall(int timeout) throws SipException {
-        if (mHold) return;
-        try {
-            mSipSession.changeCall(createHoldSessionDescription(), timeout);
-            mHold = true;
-        } catch (Throwable e) {
-            throwSipException(e);
-        }
-
-        AudioGroup audioGroup = getAudioGroup();
-        if (audioGroup != null) audioGroup.setMode(AudioGroup.MODE_ON_HOLD);
-    }
-
     public synchronized void answerCall(int timeout) throws SipException {
         try {
             stopRinging();
-            mSipSession.answerCall(createAnswerSessionDescription(), timeout);
+            mAudioStream = new AudioStream(InetAddress.getByName(getLocalIp()));
+            mSipSession.answerCall(createAnswer(mPeerSd).encode(), timeout);
         } catch (Throwable e) {
             Log.e(TAG, "answerCall()", e);
             throwSipException(e);
         }
     }
 
-    public synchronized void continueCall(int timeout) throws SipException {
-        if (!mHold) return;
+    public synchronized void holdCall(int timeout) throws SipException {
+        if (mHold) return;
         try {
-            mHold = false;
-            mSipSession.changeCall(createContinueSessionDescription(), timeout);
+            mSipSession.changeCall(createHoldOffer().encode(), timeout);
         } catch (Throwable e) {
             throwSipException(e);
         }
+        mHold = true;
+        AudioGroup audioGroup = getAudioGroup();
+        if (audioGroup != null) audioGroup.setMode(AudioGroup.MODE_ON_HOLD);
+    }
 
+    public synchronized void continueCall(int timeout) throws SipException {
+        if (!mHold) return;
+        try {
+            mSipSession.changeCall(createContinueOffer().encode(), timeout);
+        } catch (Throwable e) {
+            throwSipException(e);
+        }
+        mHold = false;
         AudioGroup audioGroup = getAudioGroup();
         if (audioGroup != null) audioGroup.setMode(AudioGroup.MODE_NORMAL);
     }
 
-    private String createOfferSessionDescription() {
-        AudioCodec[] codecs = AudioCodec.getSystemSupportedCodecs();
-        return createSdpBuilder(true, convert(codecs)).build();
-    }
-
-    private String createAnswerSessionDescription() {
-        try {
-            // choose an acceptable media from mPeerSd to answer
-            SdpSessionDescription.AudioCodec codec = getCodec(mPeerSd);
-            SdpSessionDescription.Builder sdpBuilder =
-                    createSdpBuilder(false, codec);
-            if (mPeerSd.isSendOnly(AUDIO)) {
-                sdpBuilder.addMediaAttribute(AUDIO, "recvonly", (String) null);
-            } else if (mPeerSd.isReceiveOnly(AUDIO)) {
-                sdpBuilder.addMediaAttribute(AUDIO, "sendonly", (String) null);
-            }
-            return sdpBuilder.build();
-        } catch (SdpException e) {
-            throw new RuntimeException(e);
+    private SimpleSessionDescription createOffer() {
+        SimpleSessionDescription offer =
+                new SimpleSessionDescription(mSessionId, getLocalIp());
+        AudioCodec[] codecs = AudioCodec.getCodecs();
+        Media media = offer.newMedia(
+                "audio", mAudioStream.getLocalPort(), 1, "RTP/AVP");
+        for (AudioCodec codec : AudioCodec.getCodecs()) {
+            media.setRtpPayload(codec.type, codec.rtpmap, codec.fmtp);
         }
+        media.setRtpPayload(127, "telephone-event/8000", "0-15");
+        return offer;
     }
 
-    private String createHoldSessionDescription() {
-        try {
-            return createSdpBuilder(false, mCodec)
-                    .addMediaAttribute(AUDIO, "sendonly", (String) null)
-                    .build();
-        } catch (SdpException e) {
-            throw new RuntimeException(e);
-        }
-    }
+    private SimpleSessionDescription createAnswer(String offerSd) {
+        SimpleSessionDescription offer =
+                new SimpleSessionDescription(offerSd);
+        SimpleSessionDescription answer =
+                new SimpleSessionDescription(mSessionId, getLocalIp());
+        AudioCodec codec = null;
+        for (Media media : offer.getMedia()) {
+            if ((codec == null) && (media.getPort() > 0)
+                    && "audio".equals(media.getType())
+                    && "RTP/AVP".equals(media.getProtocol())) {
+                // Find the first audio codec we supported.
+                for (int type : media.getRtpPayloadTypes()) {
+                    codec = AudioCodec.getCodec(type, media.getRtpmap(type),
+                            media.getFmtp(type));
+                    if (codec != null) {
+                        break;
+                    }
+                }
+                if (codec != null) {
+                    Media reply = answer.newMedia(
+                            "audio", mAudioStream.getLocalPort(), 1, "RTP/AVP");
+                    reply.setRtpPayload(codec.type, codec.rtpmap, codec.fmtp);
 
-    private String createContinueSessionDescription() {
-        return createSdpBuilder(true, mCodec).build();
-    }
+                    // Check if DTMF is supported in the same media.
+                    for (int type : media.getRtpPayloadTypes()) {
+                        String rtpmap = media.getRtpmap(type);
+                        if ((type != codec.type) && (rtpmap != null)
+                                && rtpmap.startsWith("telephone-event")) {
+                            reply.setRtpPayload(
+                                    type, rtpmap, media.getFmtp(type));
+                        }
+                    }
 
-    private String getMediaDescription(SdpSessionDescription.AudioCodec codec) {
-        return String.format("%d %s/%d", codec.payloadType, codec.name,
-                codec.sampleRate);
-    }
-
-    private long getSessionId() {
-        if (mSessionId < 0) {
-            mSessionId = System.currentTimeMillis();
-        }
-        return mSessionId;
-    }
-
-    private SdpSessionDescription.Builder createSdpBuilder(
-            boolean addTelephoneEvent,
-            SdpSessionDescription.AudioCodec... codecs) {
-        String localIp = getLocalIp();
-        SdpSessionDescription.Builder sdpBuilder;
-        try {
-            long sessionVersion = System.currentTimeMillis();
-            sdpBuilder = new SdpSessionDescription.Builder("SIP Call")
-                    .setOrigin(mLocalProfile, getSessionId(), sessionVersion,
-                            SDPKeywords.IN, SDPKeywords.IPV4, localIp)
-                    .setConnectionInfo(SDPKeywords.IN, SDPKeywords.IPV4,
-                            localIp);
-            List<Integer> codecIds = new ArrayList<Integer>();
-            for (SdpSessionDescription.AudioCodec codec : codecs) {
-                codecIds.add(codec.payloadType);
+                    // Handle recvonly and sendonly.
+                    if (media.getAttribute("recvonly") != null) {
+                        answer.setAttribute("sendonly", "");
+                    } else if(media.getAttribute("sendonly") != null) {
+                        answer.setAttribute("recvonly", "");
+                    } else if(offer.getAttribute("recvonly") != null) {
+                        answer.setAttribute("sendonly", "");
+                    } else if(offer.getAttribute("sendonly") != null) {
+                        answer.setAttribute("recvonly", "");
+                    }
+                    continue;
+                }
             }
-            if (addTelephoneEvent) codecIds.add(DTMF);
-            sdpBuilder.addMedia(AUDIO, getLocalMediaPort(), 1, "RTP/AVP",
-                    codecIds.toArray(new Integer[codecIds.size()]));
-            for (SdpSessionDescription.AudioCodec codec : codecs) {
-                sdpBuilder.addMediaAttribute(AUDIO, "rtpmap",
-                        getMediaDescription(codec));
+            // Reject the media.
+            Media reply = answer.newMedia(
+                    media.getType(), 0, 1, media.getProtocol());
+            for (String format : media.getFormats()) {
+                reply.setFormat(format, null);
             }
-            if (addTelephoneEvent) {
-                sdpBuilder.addMediaAttribute(AUDIO, "rtpmap",
-                        DTMF + " telephone-event/8000");
-            }
-            // FIXME: deal with vbr codec
-            sdpBuilder.addMediaAttribute(AUDIO, "ptime", "20");
-        } catch (SdpException e) {
-            throw new RuntimeException(e);
         }
-        return sdpBuilder;
+        if (codec == null) {
+            throw new IllegalStateException("Reject SDP: no suitable codecs");
+        }
+        return answer;
+    }
+
+    private SimpleSessionDescription createHoldOffer() {
+        SimpleSessionDescription offer = createContinueOffer();
+        offer.setAttribute("sendonly", "");
+        return offer;
+    }
+
+    private SimpleSessionDescription createContinueOffer() {
+        SimpleSessionDescription offer =
+                new SimpleSessionDescription(mSessionId, getLocalIp());
+        Media media = offer.newMedia(
+                "audio", mAudioStream.getLocalPort(), 1, "RTP/AVP");
+        AudioCodec codec = mAudioStream.getCodec();
+        media.setRtpPayload(codec.type, codec.rtpmap, codec.fmtp);
+        int dtmfType = mAudioStream.getDtmfType();
+        if (dtmfType != -1) {
+            media.setRtpPayload(dtmfType, "telephone-event/8000", "0-15");
+        }
+        return offer;
     }
 
     public synchronized void toggleMute() {
@@ -532,49 +532,16 @@
 
     public synchronized AudioGroup getAudioGroup() {
         if (mAudioGroup != null) return mAudioGroup;
-        return ((mAudioStream == null) ? null : mAudioStream.getAudioGroup());
+        return ((mAudioStream == null) ? null : mAudioStream.getGroup());
     }
 
     public synchronized void setAudioGroup(AudioGroup group) {
-        if ((mAudioStream != null) && (mAudioStream.getAudioGroup() != null)) {
+        if ((mAudioStream != null) && (mAudioStream.getGroup() != null)) {
             mAudioStream.join(group);
         }
         mAudioGroup = group;
     }
 
-    private SdpSessionDescription.AudioCodec getCodec(SdpSessionDescription sd) {
-        HashMap<String, AudioCodec> acceptableCodecs =
-                new HashMap<String, AudioCodec>();
-        for (AudioCodec codec : AudioCodec.getSystemSupportedCodecs()) {
-            acceptableCodecs.put(codec.name, codec);
-        }
-        for (SdpSessionDescription.AudioCodec codec : sd.getAudioCodecs()) {
-            AudioCodec matchedCodec = acceptableCodecs.get(codec.name);
-            if (matchedCodec != null) return codec;
-        }
-        Log.w(TAG, "no common codec is found, use PCM/0");
-        return convert(AudioCodec.ULAW);
-    }
-
-    private AudioCodec convert(SdpSessionDescription.AudioCodec codec) {
-        AudioCodec c = AudioCodec.getSystemSupportedCodec(codec.name);
-        return ((c == null) ? AudioCodec.ULAW : c);
-    }
-
-    private SdpSessionDescription.AudioCodec convert(AudioCodec codec) {
-        return new SdpSessionDescription.AudioCodec(codec.defaultType,
-                codec.name, codec.sampleRate, codec.sampleCount);
-    }
-
-    private SdpSessionDescription.AudioCodec[] convert(AudioCodec[] codecs) {
-        SdpSessionDescription.AudioCodec[] copies =
-                new SdpSessionDescription.AudioCodec[codecs.length];
-        for (int i = 0, len = codecs.length; i < len; i++) {
-            copies[i] = convert(codecs[i]);
-        }
-        return copies;
-    }
-
     public void startAudio() {
         try {
             startAudioInternal();
@@ -588,41 +555,75 @@
     }
 
     private synchronized void startAudioInternal() throws UnknownHostException {
+        if (mPeerSd == null) {
+            Log.v(TAG, "startAudioInternal() mPeerSd = null");
+            throw new IllegalStateException("mPeerSd = null");
+        }
+
         stopCall(DONT_RELEASE_SOCKET);
         mInCall = true;
-        SdpSessionDescription peerSd = mPeerSd;
-        String peerMediaAddress = peerSd.getPeerMediaAddress(AUDIO);
-        // TODO: handle multiple media fields
-        int peerMediaPort = peerSd.getPeerMediaPort(AUDIO);
-        Log.i(TAG, "start audiocall " + peerMediaAddress + ":" + peerMediaPort);
 
-        int localPort = getLocalMediaPort();
-        int sampleRate = 8000;
-        int frameSize = sampleRate / 50; // 160
+        // Run exact the same logic in createAnswer() to setup mAudioStream.
+        SimpleSessionDescription offer =
+                new SimpleSessionDescription(mPeerSd);
+        AudioStream stream = mAudioStream;
+        AudioCodec codec = null;
+        for (Media media : offer.getMedia()) {
+            if ((codec == null) && (media.getPort() > 0)
+                    && "audio".equals(media.getType())
+                    && "RTP/AVP".equals(media.getProtocol())) {
+                // Find the first audio codec we supported.
+                for (int type : media.getRtpPayloadTypes()) {
+                    codec = AudioCodec.getCodec(
+                            type, media.getRtpmap(type), media.getFmtp(type));
+                    if (codec != null) {
+                        break;
+                    }
+                }
 
-        // TODO: get sample rate from sdp
-        mCodec = getCodec(peerSd);
+                if (codec != null) {
+                    // Associate with the remote host.
+                    String address = media.getAddress();
+                    if (address == null) {
+                        address = offer.getAddress();
+                    }
+                    stream.associate(InetAddress.getByName(address),
+                            media.getPort());
 
-        AudioStream audioStream = mAudioStream;
-        audioStream.associate(InetAddress.getByName(peerMediaAddress),
-                peerMediaPort);
-        audioStream.setCodec(convert(mCodec), mCodec.payloadType);
-        audioStream.setDtmfType(DTMF);
-        Log.d(TAG, "start media: localPort=" + localPort + ", peer="
-                + peerMediaAddress + ":" + peerMediaPort);
+                    stream.setDtmfType(-1);
+                    stream.setCodec(codec);
+                    // Check if DTMF is supported in the same media.
+                    for (int type : media.getRtpPayloadTypes()) {
+                        String rtpmap = media.getRtpmap(type);
+                        if ((type != codec.type) && (rtpmap != null)
+                                && rtpmap.startsWith("telephone-event")) {
+                            stream.setDtmfType(type);
+                        }
+                    }
 
-        audioStream.setMode(RtpStream.MODE_NORMAL);
+                    // Handle recvonly and sendonly.
+                    if (mHold) {
+                        stream.setMode(RtpStream.MODE_NORMAL);
+                    } else if (media.getAttribute("recvonly") != null) {
+                        stream.setMode(RtpStream.MODE_SEND_ONLY);
+                    } else if(media.getAttribute("sendonly") != null) {
+                        stream.setMode(RtpStream.MODE_RECEIVE_ONLY);
+                    } else if(offer.getAttribute("recvonly") != null) {
+                        stream.setMode(RtpStream.MODE_SEND_ONLY);
+                    } else if(offer.getAttribute("sendonly") != null) {
+                        stream.setMode(RtpStream.MODE_RECEIVE_ONLY);
+                    } else {
+                        stream.setMode(RtpStream.MODE_NORMAL);
+                    }
+                    break;
+                }
+            }
+        }
+        if (codec == null) {
+            throw new IllegalStateException("Reject SDP: no suitable codecs");
+        }
+
         if (!mHold) {
-            // FIXME: won't work if peer is not sending nor receiving
-            if (!peerSd.isSending(AUDIO)) {
-                Log.d(TAG, "   not receiving");
-                audioStream.setMode(RtpStream.MODE_SEND_ONLY);
-            }
-            if (!peerSd.isReceiving(AUDIO)) {
-                Log.d(TAG, "   not sending");
-                audioStream.setMode(RtpStream.MODE_RECEIVE_ONLY);
-            }
-
             /* The recorder volume will be very low if the device is in
              * IN_CALL mode. Therefore, we have to set the mode to NORMAL
              * in order to have the normal microphone level.
@@ -642,7 +643,7 @@
             // there's another AudioGroup out there that's active
         } else {
             if (audioGroup == null) audioGroup = new AudioGroup();
-            audioStream.join(audioGroup);
+            mAudioStream.join(audioGroup);
             if (mMuted) {
                 audioGroup.setMode(AudioGroup.MODE_MUTED);
             } else {
@@ -663,24 +664,11 @@
         }
     }
 
-    private int getLocalMediaPort() {
-        if (mAudioStream != null) return mAudioStream.getLocalPort();
-        try {
-            AudioStream s = mAudioStream =
-                    new AudioStream(InetAddress.getByName(getLocalIp()));
-            return s.getLocalPort();
-        } catch (IOException e) {
-            Log.w(TAG, "getLocalMediaPort(): " + e);
-            throw new RuntimeException(e);
-        }
-    }
-
     private String getLocalIp() {
         try {
             return mSipSession.getLocalIp();
         } catch (RemoteException e) {
-            // FIXME
-            return "127.0.0.1";
+            throw new IllegalStateException(e);
         }
     }
 
diff --git a/voip/jni/rtp/AudioCodec.cpp b/voip/jni/rtp/AudioCodec.cpp
index ddd07fc..4d8d36c 100644
--- a/voip/jni/rtp/AudioCodec.cpp
+++ b/voip/jni/rtp/AudioCodec.cpp
@@ -36,9 +36,9 @@
 class UlawCodec : public AudioCodec
 {
 public:
-    bool set(int sampleRate, int sampleCount) {
-        mSampleCount = sampleCount;
-        return sampleCount > 0;
+    int set(int sampleRate, const char *fmtp) {
+        mSampleCount = sampleRate / 50;
+        return mSampleCount;
     }
     int encode(void *payload, int16_t *samples);
     int decode(int16_t *samples, void *payload, int length);
@@ -89,9 +89,9 @@
 class AlawCodec : public AudioCodec
 {
 public:
-    bool set(int sampleRate, int sampleCount) {
-        mSampleCount = sampleCount;
-        return sampleCount > 0;
+    int set(int sampleRate, const char *fmtp) {
+        mSampleCount = sampleRate / 50;
+        return mSampleCount;
     }
     int encode(void *payload, int16_t *samples);
     int decode(int16_t *samples, void *payload, int length);
@@ -152,8 +152,10 @@
 {
     AudioCodecType *type = gAudioCodecTypes;
     while (type->name != NULL) {
-        if (strcmp(codecName, type->name) == 0) {
-            return type->create();
+        if (strcasecmp(codecName, type->name) == 0) {
+            AudioCodec *codec = type->create();
+            codec->name = type->name;
+            return codec;
         }
         ++type;
     }
diff --git a/voip/jni/rtp/AudioCodec.h b/voip/jni/rtp/AudioCodec.h
index 797494c..e389255 100644
--- a/voip/jni/rtp/AudioCodec.h
+++ b/voip/jni/rtp/AudioCodec.h
@@ -22,9 +22,11 @@
 class AudioCodec
 {
 public:
+    const char *name;
+    // Needed by destruction through base class pointers.
     virtual ~AudioCodec() {}
-    // Returns true if initialization succeeds.
-    virtual bool set(int sampleRate, int sampleCount) = 0;
+    // Returns sampleCount or non-positive value if unsupported.
+    virtual int set(int sampleRate, const char *fmtp) = 0;
     // Returns the length of payload in bytes.
     virtual int encode(void *payload, int16_t *samples) = 0;
     // Returns the number of decoded samples.
diff --git a/voip/jni/rtp/AudioGroup.cpp b/voip/jni/rtp/AudioGroup.cpp
index 3433dcf..7cf06137 100644
--- a/voip/jni/rtp/AudioGroup.cpp
+++ b/voip/jni/rtp/AudioGroup.cpp
@@ -77,7 +77,7 @@
     AudioStream();
     ~AudioStream();
     bool set(int mode, int socket, sockaddr_storage *remote,
-        const char *codecName, int sampleRate, int sampleCount,
+        AudioCodec *codec, int sampleRate, int sampleCount,
         int codecType, int dtmfType);
 
     void sendDtmf(int event);
@@ -104,6 +104,7 @@
     int mSampleRate;
     int mSampleCount;
     int mInterval;
+    int mLogThrottle;
 
     int16_t *mBuffer;
     int mBufferMask;
@@ -140,7 +141,7 @@
 }
 
 bool AudioStream::set(int mode, int socket, sockaddr_storage *remote,
-    const char *codecName, int sampleRate, int sampleCount,
+    AudioCodec *codec, int sampleRate, int sampleCount,
     int codecType, int dtmfType)
 {
     if (mode < 0 || mode > LAST_MODE) {
@@ -148,14 +149,6 @@
     }
     mMode = mode;
 
-    if (codecName) {
-        mRemote = *remote;
-        mCodec = newAudioCodec(codecName);
-        if (!mCodec || !mCodec->set(sampleRate, sampleCount)) {
-            return false;
-        }
-    }
-
     mCodecMagic = (0x8000 | codecType) << 16;
     mDtmfMagic = (dtmfType == -1) ? 0 : (0x8000 | dtmfType) << 16;
 
@@ -181,11 +174,15 @@
     mDtmfEvent = -1;
     mDtmfStart = 0;
 
-    // Only take over the socket when succeeded.
+    // Only take over these things when succeeded.
     mSocket = socket;
+    if (codec) {
+        mRemote = *remote;
+        mCodec = codec;
+    }
 
     LOGD("stream[%d] is configured as %s %dkHz %dms", mSocket,
-        (codecName ? codecName : "RAW"), mSampleRate, mInterval);
+        (codec ? codec->name : "RAW"), mSampleRate, mInterval);
     return true;
 }
 
@@ -282,7 +279,10 @@
         chain = chain->mNext;
     }
     if (!mixed) {
-        LOGD("stream[%d] no data", mSocket);
+        if ((mTick ^ mLogThrottle) >> 10) {
+            mLogThrottle = mTick;
+            LOGD("stream[%d] no data", mSocket);
+        }
         return;
     }
 
@@ -831,10 +831,9 @@
 
 void add(JNIEnv *env, jobject thiz, jint mode,
     jint socket, jstring jRemoteAddress, jint remotePort,
-    jstring jCodecName, jint sampleRate, jint sampleCount,
-    jint codecType, jint dtmfType)
+    jstring jCodecSpec, jint dtmfType)
 {
-    const char *codecName = NULL;
+    AudioCodec *codec = NULL;
     AudioStream *stream = NULL;
     AudioGroup *group = NULL;
 
@@ -842,33 +841,42 @@
     sockaddr_storage remote;
     if (parse(env, jRemoteAddress, remotePort, &remote) < 0) {
         // Exception already thrown.
-        goto error;
+        return;
     }
-    if (sampleRate < 0 || sampleCount < 0 || codecType < 0 || codecType > 127) {
-        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
-        goto error;
+    if (!jCodecSpec) {
+        jniThrowNullPointerException(env, "codecSpec");
+        return;
     }
-    if (!jCodecName) {
-        jniThrowNullPointerException(env, "codecName");
-        goto error;
-    }
-    codecName = env->GetStringUTFChars(jCodecName, NULL);
-    if (!codecName) {
+    const char *codecSpec = env->GetStringUTFChars(jCodecSpec, NULL);
+    if (!codecSpec) {
         // Exception already thrown.
+        return;
+    }
+
+    // Create audio codec.
+    int codecType = -1;
+    char codecName[16];
+    int sampleRate = -1;
+    sscanf(codecSpec, "%d %[^/]%*c%d", &codecType, codecName, &sampleRate);
+    codec = newAudioCodec(codecName);
+    int sampleCount = (codec ? codec->set(sampleRate, codecSpec) : -1);
+    env->ReleaseStringUTFChars(jCodecSpec, codecSpec);
+    if (sampleCount <= 0) {
+        jniThrowException(env, "java/lang/IllegalStateException",
+            "cannot initialize audio codec");
         goto error;
     }
 
     // Create audio stream.
     stream = new AudioStream;
-    if (!stream->set(mode, socket, &remote, codecName, sampleRate, sampleCount,
+    if (!stream->set(mode, socket, &remote, codec, sampleRate, sampleCount,
         codecType, dtmfType)) {
         jniThrowException(env, "java/lang/IllegalStateException",
             "cannot initialize audio stream");
-        env->ReleaseStringUTFChars(jCodecName, codecName);
         goto error;
     }
-    env->ReleaseStringUTFChars(jCodecName, codecName);
     socket = -1;
+    codec = NULL;
 
     // Create audio group.
     group = (AudioGroup *)env->GetIntField(thiz, gNative);
@@ -896,6 +904,7 @@
 error:
     delete group;
     delete stream;
+    delete codec;
     close(socket);
     env->SetIntField(thiz, gNative, NULL);
 }
@@ -930,7 +939,7 @@
 }
 
 JNINativeMethod gMethods[] = {
-    {"add", "(IILjava/lang/String;ILjava/lang/String;IIII)V", (void *)add},
+    {"add", "(IILjava/lang/String;ILjava/lang/String;I)V", (void *)add},
     {"remove", "(I)V", (void *)remove},
     {"setMode", "(I)V", (void *)setMode},
     {"sendDtmf", "(I)V", (void *)sendDtmf},