Merge "Adding support for all allowed textures. Cleaning up unused code Adding error messages"
diff --git a/api/current.xml b/api/current.xml
index db19baa3..80428bd 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -21741,6 +21741,17 @@
  visibility="public"
 >
 </method>
+<method name="removeAllUpdateListeners"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="removeUpdateListener"
  return="void"
  abstract="false"
@@ -72521,6 +72532,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"
@@ -89670,6 +89821,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"
@@ -90273,6 +90437,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"
@@ -94495,1079 +94672,6 @@
 >
 </method>
 </class>
-<class name="AudioEffect"
- extends="java.lang.Object"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="AudioEffect"
- type="android.media.AudioEffect"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="type" type="java.util.UUID">
-</parameter>
-<parameter name="uuid" type="java.util.UUID">
-</parameter>
-<parameter name="priority" type="int">
-</parameter>
-<parameter name="audioSession" type="int">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="RuntimeException" type="java.lang.RuntimeException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</constructor>
-<method name="byteArrayToInt"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="valueBuf" type="byte[]">
-</parameter>
-</method>
-<method name="byteArrayToInt"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="valueBuf" type="byte[]">
-</parameter>
-<parameter name="offset" type="int">
-</parameter>
-</method>
-<method name="byteArrayToShort"
- return="short"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="valueBuf" type="byte[]">
-</parameter>
-</method>
-<method name="byteArrayToShort"
- return="short"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="valueBuf" type="byte[]">
-</parameter>
-<parameter name="offset" type="int">
-</parameter>
-</method>
-<method name="checkState"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="methodName" type="java.lang.String">
-</parameter>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-</method>
-<method name="checkStatus"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="status" type="int">
-</parameter>
-</method>
-<method name="command"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="cmdCode" type="int">
-</parameter>
-<parameter name="command" type="byte[]">
-</parameter>
-<parameter name="reply" type="byte[]">
-</parameter>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-</method>
-<method name="concatArrays"
- return="byte[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="arrays" type="byte...">
-</parameter>
-</method>
-<method name="getDescriptor"
- return="android.media.AudioEffect.Descriptor"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-</method>
-<method name="getEnabled"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-</method>
-<method name="getId"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-</method>
-<method name="getParameter"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="param" type="byte[]">
-</parameter>
-<parameter name="value" type="byte[]">
-</parameter>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-</method>
-<method name="getParameter"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="param" type="int">
-</parameter>
-<parameter name="value" type="byte[]">
-</parameter>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-</method>
-<method name="getParameter"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="param" type="int">
-</parameter>
-<parameter name="value" type="int[]">
-</parameter>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-</method>
-<method name="getParameter"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="param" type="int">
-</parameter>
-<parameter name="value" type="short[]">
-</parameter>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-</method>
-<method name="getParameter"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="param" type="int[]">
-</parameter>
-<parameter name="value" type="int[]">
-</parameter>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-</method>
-<method name="getParameter"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="param" type="int[]">
-</parameter>
-<parameter name="value" type="short[]">
-</parameter>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-</method>
-<method name="getParameter"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="param" type="int[]">
-</parameter>
-<parameter name="value" type="byte[]">
-</parameter>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-</method>
-<method name="hasControl"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-</method>
-<method name="intToByteArray"
- return="byte[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="value" type="int">
-</parameter>
-</method>
-<method name="queryEffects"
- return="android.media.AudioEffect.Descriptor[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="release"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="setControlStatusListener"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="listener" type="android.media.AudioEffect.OnControlStatusChangeListener">
-</parameter>
-</method>
-<method name="setEnableStatusListener"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="listener" type="android.media.AudioEffect.OnEnableStatusChangeListener">
-</parameter>
-</method>
-<method name="setEnabled"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="enabled" type="boolean">
-</parameter>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-</method>
-<method name="setParameter"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="param" type="byte[]">
-</parameter>
-<parameter name="value" type="byte[]">
-</parameter>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-</method>
-<method name="setParameter"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="param" type="int">
-</parameter>
-<parameter name="value" type="int">
-</parameter>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-</method>
-<method name="setParameter"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="param" type="int">
-</parameter>
-<parameter name="value" type="short">
-</parameter>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-</method>
-<method name="setParameter"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="param" type="int">
-</parameter>
-<parameter name="value" type="byte[]">
-</parameter>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-</method>
-<method name="setParameter"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="param" type="int[]">
-</parameter>
-<parameter name="value" type="int[]">
-</parameter>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-</method>
-<method name="setParameter"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="param" type="int[]">
-</parameter>
-<parameter name="value" type="short[]">
-</parameter>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-</method>
-<method name="setParameter"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="param" type="int[]">
-</parameter>
-<parameter name="value" type="byte[]">
-</parameter>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-</method>
-<method name="setParameterListener"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="listener" type="android.media.AudioEffect.OnParameterChangeListener">
-</parameter>
-</method>
-<method name="shortToByteArray"
- return="byte[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="value" type="short">
-</parameter>
-</method>
-<field name="ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.media.action.CLOSE_AUDIO_EFFECT_CONTROL_SESSION&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.media.action.DISPLAY_AUDIO_EFFECT_CONTROL_PANEL&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.media.action.OPEN_AUDIO_EFFECT_CONTROL_SESSION&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ALREADY_EXISTS"
- type="int"
- transient="false"
- volatile="false"
- value="-2"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="CONTENT_TYPE_GAME"
- type="int"
- transient="false"
- volatile="false"
- value="2"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="CONTENT_TYPE_MOVIE"
- type="int"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="CONTENT_TYPE_MUSIC"
- type="int"
- transient="false"
- volatile="false"
- value="0"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="CONTENT_TYPE_VOICE"
- type="int"
- transient="false"
- volatile="false"
- value="3"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="EFFECT_AUXILIARY"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;Auxiliary&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="EFFECT_INSERT"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;Insert&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="EFFECT_TYPE_BASS_BOOST"
- type="java.util.UUID"
- transient="false"
- volatile="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="EFFECT_TYPE_ENV_REVERB"
- type="java.util.UUID"
- transient="false"
- volatile="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="EFFECT_TYPE_EQUALIZER"
- type="java.util.UUID"
- transient="false"
- volatile="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="EFFECT_TYPE_NULL"
- type="java.util.UUID"
- transient="false"
- volatile="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="EFFECT_TYPE_PRESET_REVERB"
- type="java.util.UUID"
- transient="false"
- volatile="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="EFFECT_TYPE_VIRTUALIZER"
- type="java.util.UUID"
- transient="false"
- volatile="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ERROR"
- type="int"
- transient="false"
- volatile="false"
- value="-1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ERROR_BAD_VALUE"
- type="int"
- transient="false"
- volatile="false"
- value="-4"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ERROR_DEAD_OBJECT"
- type="int"
- transient="false"
- volatile="false"
- value="-7"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ERROR_INVALID_OPERATION"
- type="int"
- transient="false"
- volatile="false"
- value="-5"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ERROR_NO_INIT"
- type="int"
- transient="false"
- volatile="false"
- value="-3"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ERROR_NO_MEMORY"
- type="int"
- transient="false"
- volatile="false"
- value="-6"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="EXTRA_AUDIO_SESSION"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.media.extra.AUDIO_SESSION&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="EXTRA_CONTENT_TYPE"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.media.extra.CONTENT_TYPE&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="EXTRA_PACKAGE_NAME"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.media.extra.PACKAGE_NAME&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="NATIVE_EVENT_CONTROL_STATUS"
- type="int"
- transient="false"
- volatile="false"
- value="0"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="NATIVE_EVENT_ENABLED_STATUS"
- type="int"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="NATIVE_EVENT_PARAMETER_CHANGED"
- type="int"
- transient="false"
- volatile="false"
- value="2"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="STATE_INITIALIZED"
- type="int"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="STATE_UNINITIALIZED"
- type="int"
- transient="false"
- volatile="false"
- value="0"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="SUCCESS"
- type="int"
- transient="false"
- volatile="false"
- value="0"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="mListenerLock"
- type="java.lang.Object"
- transient="false"
- volatile="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="mNativeEventHandler"
- type="android.media.AudioEffect.NativeEventHandler"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
-<class name="AudioEffect.Descriptor"
- extends="java.lang.Object"
- abstract="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="AudioEffect.Descriptor"
- type="android.media.AudioEffect.Descriptor"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<constructor name="AudioEffect.Descriptor"
- type="android.media.AudioEffect.Descriptor"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="type" type="java.lang.String">
-</parameter>
-<parameter name="uuid" type="java.lang.String">
-</parameter>
-<parameter name="connectMode" type="java.lang.String">
-</parameter>
-<parameter name="name" type="java.lang.String">
-</parameter>
-<parameter name="implementor" type="java.lang.String">
-</parameter>
-</constructor>
-<field name="mConnectMode"
- type="java.lang.String"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="mImplementor"
- type="java.lang.String"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="mName"
- type="java.lang.String"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="mType"
- type="java.util.UUID"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="mUuid"
- type="java.util.UUID"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
-<class name="AudioEffect.NativeEventHandler"
- extends="android.os.Handler"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="private"
->
-<constructor name="AudioEffect.NativeEventHandler"
- type="android.media.AudioEffect.NativeEventHandler"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="ae" type="android.media.AudioEffect">
-</parameter>
-<parameter name="looper" type="android.os.Looper">
-</parameter>
-</constructor>
-</class>
-<interface name="AudioEffect.OnControlStatusChangeListener"
- abstract="true"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="onControlStatusChange"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="effect" type="android.media.AudioEffect">
-</parameter>
-<parameter name="controlGranted" type="boolean">
-</parameter>
-</method>
-</interface>
-<interface name="AudioEffect.OnEnableStatusChangeListener"
- abstract="true"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="onEnableStatusChange"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="effect" type="android.media.AudioEffect">
-</parameter>
-<parameter name="enabled" type="boolean">
-</parameter>
-</method>
-</interface>
-<interface name="AudioEffect.OnParameterChangeListener"
- abstract="true"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="onParameterChange"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="effect" type="android.media.AudioEffect">
-</parameter>
-<parameter name="status" type="int">
-</parameter>
-<parameter name="param" type="byte[]">
-</parameter>
-<parameter name="value" type="byte[]">
-</parameter>
-</method>
-</interface>
 <class name="AudioFormat"
  extends="java.lang.Object"
  abstract="false"
@@ -98409,217 +97513,6 @@
 </parameter>
 </method>
 </interface>
-<class name="BassBoost"
- extends="android.media.AudioEffect"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="BassBoost"
- type="android.media.BassBoost"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="priority" type="int">
-</parameter>
-<parameter name="audioSession" type="int">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="RuntimeException" type="java.lang.RuntimeException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</constructor>
-<method name="getProperties"
- return="android.media.BassBoost.Settings"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getRoundedStrength"
- return="short"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getStrengthSupported"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="setParameterListener"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="listener" type="android.media.BassBoost.OnParameterChangeListener">
-</parameter>
-</method>
-<method name="setProperties"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="settings" type="android.media.BassBoost.Settings">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="setStrength"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="strength" type="short">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<field name="PARAM_STRENGTH"
- type="int"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PARAM_STRENGTH_SUPPORTED"
- type="int"
- transient="false"
- volatile="false"
- value="0"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
-<interface name="BassBoost.OnParameterChangeListener"
- abstract="true"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="onParameterChange"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="effect" type="android.media.BassBoost">
-</parameter>
-<parameter name="status" type="int">
-</parameter>
-<parameter name="param" type="int">
-</parameter>
-<parameter name="value" type="short">
-</parameter>
-</method>
-</interface>
-<class name="BassBoost.Settings"
- extends="java.lang.Object"
- abstract="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="BassBoost.Settings"
- type="android.media.BassBoost.Settings"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<constructor name="BassBoost.Settings"
- type="android.media.BassBoost.Settings"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="settings" type="java.lang.String">
-</parameter>
-</constructor>
-<field name="strength"
- type="short"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
 <class name="CamcorderProfile"
  extends="java.lang.Object"
  abstract="false"
@@ -99037,1178 +97930,6 @@
 >
 </field>
 </class>
-<class name="EnvironmentalReverb"
- extends="android.media.AudioEffect"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="EnvironmentalReverb"
- type="android.media.EnvironmentalReverb"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="priority" type="int">
-</parameter>
-<parameter name="audioSession" type="int">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="RuntimeException" type="java.lang.RuntimeException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</constructor>
-<method name="getDecayHFRatio"
- return="short"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getDecayTime"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getDensity"
- return="short"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getDiffusion"
- return="short"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getProperties"
- return="android.media.EnvironmentalReverb.Settings"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getReflectionsDelay"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getReflectionsLevel"
- return="short"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getReverbDelay"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getReverbLevel"
- return="short"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getRoomHFLevel"
- return="short"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getRoomLevel"
- return="short"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="setDecayHFRatio"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="decayHFRatio" type="short">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="setDecayTime"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="decayTime" type="int">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="setDensity"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="density" type="short">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="setDiffusion"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="diffusion" type="short">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="setParameterListener"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="listener" type="android.media.EnvironmentalReverb.OnParameterChangeListener">
-</parameter>
-</method>
-<method name="setProperties"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="settings" type="android.media.EnvironmentalReverb.Settings">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="setReflectionsDelay"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reflectionsDelay" type="int">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="setReflectionsLevel"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reflectionsLevel" type="short">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="setReverbDelay"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reverbDelay" type="int">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="setReverbLevel"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reverbLevel" type="short">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="setRoomHFLevel"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="roomHF" type="short">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="setRoomLevel"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="room" type="short">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<field name="PARAM_DECAY_HF_RATIO"
- type="int"
- transient="false"
- volatile="false"
- value="3"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PARAM_DECAY_TIME"
- type="int"
- transient="false"
- volatile="false"
- value="2"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PARAM_DENSITY"
- type="int"
- transient="false"
- volatile="false"
- value="9"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PARAM_DIFFUSION"
- type="int"
- transient="false"
- volatile="false"
- value="8"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PARAM_REFLECTIONS_DELAY"
- type="int"
- transient="false"
- volatile="false"
- value="5"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PARAM_REFLECTIONS_LEVEL"
- type="int"
- transient="false"
- volatile="false"
- value="4"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PARAM_REVERB_DELAY"
- type="int"
- transient="false"
- volatile="false"
- value="7"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PARAM_REVERB_LEVEL"
- type="int"
- transient="false"
- volatile="false"
- value="6"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PARAM_ROOM_HF_LEVEL"
- type="int"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PARAM_ROOM_LEVEL"
- type="int"
- transient="false"
- volatile="false"
- value="0"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
-<interface name="EnvironmentalReverb.OnParameterChangeListener"
- abstract="true"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="onParameterChange"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="effect" type="android.media.EnvironmentalReverb">
-</parameter>
-<parameter name="status" type="int">
-</parameter>
-<parameter name="param" type="int">
-</parameter>
-<parameter name="value" type="int">
-</parameter>
-</method>
-</interface>
-<class name="EnvironmentalReverb.Settings"
- extends="java.lang.Object"
- abstract="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="EnvironmentalReverb.Settings"
- type="android.media.EnvironmentalReverb.Settings"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<constructor name="EnvironmentalReverb.Settings"
- type="android.media.EnvironmentalReverb.Settings"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="settings" type="java.lang.String">
-</parameter>
-</constructor>
-<field name="decayHFRatio"
- type="short"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="decayTime"
- type="int"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="density"
- type="short"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="diffusion"
- type="short"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="reflectionsDelay"
- type="int"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="reflectionsLevel"
- type="short"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="reverbDelay"
- type="int"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="reverbLevel"
- type="short"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="roomHFLevel"
- type="short"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="roomLevel"
- type="short"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
-<class name="Equalizer"
- extends="android.media.AudioEffect"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="Equalizer"
- type="android.media.Equalizer"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="priority" type="int">
-</parameter>
-<parameter name="audioSession" type="int">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="RuntimeException" type="java.lang.RuntimeException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</constructor>
-<method name="getBand"
- return="short"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="frequency" type="int">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getBandFreqRange"
- return="int[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="band" type="short">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getBandLevel"
- return="short"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="band" type="short">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getBandLevelRange"
- return="short[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getCenterFreq"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="band" type="short">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getCurrentPreset"
- return="short"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getNumberOfBands"
- return="short"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getNumberOfPresets"
- return="short"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getPresetName"
- return="java.lang.String"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="preset" type="short">
-</parameter>
-</method>
-<method name="getProperties"
- return="android.media.Equalizer.Settings"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="setBandLevel"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="band" type="short">
-</parameter>
-<parameter name="level" type="short">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="setParameterListener"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="listener" type="android.media.Equalizer.OnParameterChangeListener">
-</parameter>
-</method>
-<method name="setProperties"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="settings" type="android.media.Equalizer.Settings">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="usePreset"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="preset" type="short">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<field name="PARAM_BAND_FREQ_RANGE"
- type="int"
- transient="false"
- volatile="false"
- value="4"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PARAM_BAND_LEVEL"
- type="int"
- transient="false"
- volatile="false"
- value="2"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PARAM_CENTER_FREQ"
- type="int"
- transient="false"
- volatile="false"
- value="3"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PARAM_CURRENT_PRESET"
- type="int"
- transient="false"
- volatile="false"
- value="6"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PARAM_GET_BAND"
- type="int"
- transient="false"
- volatile="false"
- value="5"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PARAM_GET_NUM_OF_PRESETS"
- type="int"
- transient="false"
- volatile="false"
- value="7"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PARAM_GET_PRESET_NAME"
- type="int"
- transient="false"
- volatile="false"
- value="8"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PARAM_LEVEL_RANGE"
- type="int"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PARAM_NUM_BANDS"
- type="int"
- transient="false"
- volatile="false"
- value="0"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PARAM_STRING_SIZE_MAX"
- type="int"
- transient="false"
- volatile="false"
- value="32"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
-<interface name="Equalizer.OnParameterChangeListener"
- abstract="true"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="onParameterChange"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="effect" type="android.media.Equalizer">
-</parameter>
-<parameter name="status" type="int">
-</parameter>
-<parameter name="param1" type="int">
-</parameter>
-<parameter name="param2" type="int">
-</parameter>
-<parameter name="value" type="int">
-</parameter>
-</method>
-</interface>
-<class name="Equalizer.Settings"
- extends="java.lang.Object"
- abstract="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="Equalizer.Settings"
- type="android.media.Equalizer.Settings"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<constructor name="Equalizer.Settings"
- type="android.media.Equalizer.Settings"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="settings" type="java.lang.String">
-</parameter>
-</constructor>
-<field name="bandLevels"
- type="short[]"
- transient="false"
- volatile="false"
- value="null"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="curPreset"
- type="short"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="numBands"
- type="short"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
 <class name="ExifInterface"
  extends="java.lang.Object"
  abstract="false"
@@ -102912,270 +100633,6 @@
 </parameter>
 </method>
 </interface>
-<class name="PresetReverb"
- extends="android.media.AudioEffect"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="PresetReverb"
- type="android.media.PresetReverb"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="priority" type="int">
-</parameter>
-<parameter name="audioSession" type="int">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="RuntimeException" type="java.lang.RuntimeException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</constructor>
-<method name="getPreset"
- return="short"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getProperties"
- return="android.media.PresetReverb.Settings"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="setParameterListener"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="listener" type="android.media.PresetReverb.OnParameterChangeListener">
-</parameter>
-</method>
-<method name="setPreset"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="preset" type="short">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="setProperties"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="settings" type="android.media.PresetReverb.Settings">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<field name="PARAM_PRESET"
- type="int"
- transient="false"
- volatile="false"
- value="0"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PRESET_LARGEHALL"
- type="short"
- transient="false"
- volatile="false"
- value="5"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PRESET_LARGEROOM"
- type="short"
- transient="false"
- volatile="false"
- value="3"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PRESET_MEDIUMHALL"
- type="short"
- transient="false"
- volatile="false"
- value="4"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PRESET_MEDIUMROOM"
- type="short"
- transient="false"
- volatile="false"
- value="2"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PRESET_NONE"
- type="short"
- transient="false"
- volatile="false"
- value="0"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PRESET_PLATE"
- type="short"
- transient="false"
- volatile="false"
- value="6"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PRESET_SMALLROOM"
- type="short"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
-<interface name="PresetReverb.OnParameterChangeListener"
- abstract="true"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="onParameterChange"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="effect" type="android.media.PresetReverb">
-</parameter>
-<parameter name="status" type="int">
-</parameter>
-<parameter name="param" type="int">
-</parameter>
-<parameter name="value" type="short">
-</parameter>
-</method>
-</interface>
-<class name="PresetReverb.Settings"
- extends="java.lang.Object"
- abstract="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="PresetReverb.Settings"
- type="android.media.PresetReverb.Settings"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<constructor name="PresetReverb.Settings"
- type="android.media.PresetReverb.Settings"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="settings" type="java.lang.String">
-</parameter>
-</constructor>
-<field name="preset"
- type="short"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
 <class name="Ringtone"
  extends="java.lang.Object"
  abstract="false"
@@ -105250,16 +102707,494 @@
 >
 </field>
 </class>
-<class name="Virtualizer"
- extends="android.media.AudioEffect"
+</package>
+<package name="android.media.audiofx"
+>
+<class name="AudioEffect"
+ extends="java.lang.Object"
  abstract="false"
  static="false"
  final="false"
  deprecated="not deprecated"
  visibility="public"
 >
-<constructor name="Virtualizer"
- type="android.media.Virtualizer"
+<method name="getDescriptor"
+ return="android.media.audiofx.AudioEffect.Descriptor"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+</method>
+<method name="getEnabled"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+</method>
+<method name="getId"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+</method>
+<method name="hasControl"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+</method>
+<method name="queryEffects"
+ return="android.media.audiofx.AudioEffect.Descriptor[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="release"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="setControlStatusListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.media.audiofx.AudioEffect.OnControlStatusChangeListener">
+</parameter>
+</method>
+<method name="setEnableStatusListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.media.audiofx.AudioEffect.OnEnableStatusChangeListener">
+</parameter>
+</method>
+<method name="setEnabled"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="enabled" type="boolean">
+</parameter>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+</method>
+<field name="ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.media.action.CLOSE_AUDIO_EFFECT_CONTROL_SESSION&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.media.action.DISPLAY_AUDIO_EFFECT_CONTROL_PANEL&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.media.action.OPEN_AUDIO_EFFECT_CONTROL_SESSION&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ALREADY_EXISTS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CONTENT_TYPE_GAME"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CONTENT_TYPE_MOVIE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CONTENT_TYPE_MUSIC"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CONTENT_TYPE_VOICE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EFFECT_AUXILIARY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;Auxiliary&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EFFECT_INSERT"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;Insert&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_BAD_VALUE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_DEAD_OBJECT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-7"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_INVALID_OPERATION"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-5"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_NO_INIT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_NO_MEMORY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-6"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EXTRA_AUDIO_SESSION"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.media.extra.AUDIO_SESSION&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EXTRA_CONTENT_TYPE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.media.extra.CONTENT_TYPE&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EXTRA_PACKAGE_NAME"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.media.extra.PACKAGE_NAME&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SUCCESS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="AudioEffect.Descriptor"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="AudioEffect.Descriptor"
+ type="android.media.audiofx.AudioEffect.Descriptor"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<constructor name="AudioEffect.Descriptor"
+ type="android.media.audiofx.AudioEffect.Descriptor"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="type" type="java.lang.String">
+</parameter>
+<parameter name="uuid" type="java.lang.String">
+</parameter>
+<parameter name="connectMode" type="java.lang.String">
+</parameter>
+<parameter name="name" type="java.lang.String">
+</parameter>
+<parameter name="implementor" type="java.lang.String">
+</parameter>
+</constructor>
+<field name="connectMode"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="implementor"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="name"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="type"
+ type="java.util.UUID"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="uuid"
+ type="java.util.UUID"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<interface name="AudioEffect.OnControlStatusChangeListener"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onControlStatusChange"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="effect" type="android.media.audiofx.AudioEffect">
+</parameter>
+<parameter name="controlGranted" type="boolean">
+</parameter>
+</method>
+</interface>
+<interface name="AudioEffect.OnEnableStatusChangeListener"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onEnableStatusChange"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="effect" type="android.media.audiofx.AudioEffect">
+</parameter>
+<parameter name="enabled" type="boolean">
+</parameter>
+</method>
+</interface>
+<class name="BassBoost"
+ extends="android.media.audiofx.AudioEffect"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="BassBoost"
+ type="android.media.audiofx.BassBoost"
  static="false"
  final="false"
  deprecated="not deprecated"
@@ -105279,7 +103214,7 @@
 </exception>
 </constructor>
 <method name="getProperties"
- return="android.media.Virtualizer.Settings"
+ return="android.media.audiofx.BassBoost.Settings"
  abstract="false"
  native="false"
  synchronized="false"
@@ -105333,7 +103268,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="listener" type="android.media.Virtualizer.OnParameterChangeListener">
+<parameter name="listener" type="android.media.audiofx.BassBoost.OnParameterChangeListener">
 </parameter>
 </method>
 <method name="setProperties"
@@ -105346,7 +103281,1654 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="settings" type="android.media.Virtualizer.Settings">
+<parameter name="settings" type="android.media.audiofx.BassBoost.Settings">
+</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="setStrength"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="strength" type="short">
+</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<field name="PARAM_STRENGTH"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PARAM_STRENGTH_SUPPORTED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<interface name="BassBoost.OnParameterChangeListener"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onParameterChange"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="effect" type="android.media.audiofx.BassBoost">
+</parameter>
+<parameter name="status" type="int">
+</parameter>
+<parameter name="param" type="int">
+</parameter>
+<parameter name="value" type="short">
+</parameter>
+</method>
+</interface>
+<class name="BassBoost.Settings"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="BassBoost.Settings"
+ type="android.media.audiofx.BassBoost.Settings"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<constructor name="BassBoost.Settings"
+ type="android.media.audiofx.BassBoost.Settings"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="settings" type="java.lang.String">
+</parameter>
+</constructor>
+<field name="strength"
+ type="short"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="EnvironmentalReverb"
+ extends="android.media.audiofx.AudioEffect"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="EnvironmentalReverb"
+ type="android.media.audiofx.EnvironmentalReverb"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="priority" type="int">
+</parameter>
+<parameter name="audioSession" type="int">
+</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="RuntimeException" type="java.lang.RuntimeException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</constructor>
+<method name="getDecayHFRatio"
+ return="short"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="getDecayTime"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="getDensity"
+ return="short"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="getDiffusion"
+ return="short"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="getProperties"
+ return="android.media.audiofx.EnvironmentalReverb.Settings"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="getReflectionsDelay"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="getReflectionsLevel"
+ return="short"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="getReverbDelay"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="getReverbLevel"
+ return="short"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="getRoomHFLevel"
+ return="short"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="getRoomLevel"
+ return="short"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="setDecayHFRatio"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="decayHFRatio" type="short">
+</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="setDecayTime"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="decayTime" type="int">
+</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="setDensity"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="density" type="short">
+</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="setDiffusion"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="diffusion" type="short">
+</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="setParameterListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.media.audiofx.EnvironmentalReverb.OnParameterChangeListener">
+</parameter>
+</method>
+<method name="setProperties"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="settings" type="android.media.audiofx.EnvironmentalReverb.Settings">
+</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="setReflectionsDelay"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="reflectionsDelay" type="int">
+</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="setReflectionsLevel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="reflectionsLevel" type="short">
+</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="setReverbDelay"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="reverbDelay" type="int">
+</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="setReverbLevel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="reverbLevel" type="short">
+</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="setRoomHFLevel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="roomHF" type="short">
+</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="setRoomLevel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="room" type="short">
+</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<field name="PARAM_DECAY_HF_RATIO"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PARAM_DECAY_TIME"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PARAM_DENSITY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="9"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PARAM_DIFFUSION"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="8"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PARAM_REFLECTIONS_DELAY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="5"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PARAM_REFLECTIONS_LEVEL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PARAM_REVERB_DELAY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="7"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PARAM_REVERB_LEVEL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="6"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PARAM_ROOM_HF_LEVEL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PARAM_ROOM_LEVEL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<interface name="EnvironmentalReverb.OnParameterChangeListener"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onParameterChange"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="effect" type="android.media.audiofx.EnvironmentalReverb">
+</parameter>
+<parameter name="status" type="int">
+</parameter>
+<parameter name="param" type="int">
+</parameter>
+<parameter name="value" type="int">
+</parameter>
+</method>
+</interface>
+<class name="EnvironmentalReverb.Settings"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="EnvironmentalReverb.Settings"
+ type="android.media.audiofx.EnvironmentalReverb.Settings"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<constructor name="EnvironmentalReverb.Settings"
+ type="android.media.audiofx.EnvironmentalReverb.Settings"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="settings" type="java.lang.String">
+</parameter>
+</constructor>
+<field name="decayHFRatio"
+ type="short"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="decayTime"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="density"
+ type="short"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="diffusion"
+ type="short"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="reflectionsDelay"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="reflectionsLevel"
+ type="short"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="reverbDelay"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="reverbLevel"
+ type="short"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="roomHFLevel"
+ type="short"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="roomLevel"
+ type="short"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="Equalizer"
+ extends="android.media.audiofx.AudioEffect"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="Equalizer"
+ type="android.media.audiofx.Equalizer"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="priority" type="int">
+</parameter>
+<parameter name="audioSession" type="int">
+</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="RuntimeException" type="java.lang.RuntimeException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</constructor>
+<method name="getBand"
+ return="short"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="frequency" type="int">
+</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="getBandFreqRange"
+ return="int[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="band" type="short">
+</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="getBandLevel"
+ return="short"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="band" type="short">
+</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="getBandLevelRange"
+ return="short[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="getCenterFreq"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="band" type="short">
+</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="getCurrentPreset"
+ return="short"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="getNumberOfBands"
+ return="short"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="getNumberOfPresets"
+ return="short"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="getPresetName"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="preset" type="short">
+</parameter>
+</method>
+<method name="getProperties"
+ return="android.media.audiofx.Equalizer.Settings"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="setBandLevel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="band" type="short">
+</parameter>
+<parameter name="level" type="short">
+</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="setParameterListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.media.audiofx.Equalizer.OnParameterChangeListener">
+</parameter>
+</method>
+<method name="setProperties"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="settings" type="android.media.audiofx.Equalizer.Settings">
+</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="usePreset"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="preset" type="short">
+</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<field name="PARAM_BAND_FREQ_RANGE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PARAM_BAND_LEVEL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PARAM_CENTER_FREQ"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PARAM_CURRENT_PRESET"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="6"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PARAM_GET_BAND"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="5"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PARAM_GET_NUM_OF_PRESETS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="7"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PARAM_GET_PRESET_NAME"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="8"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PARAM_LEVEL_RANGE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PARAM_NUM_BANDS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PARAM_STRING_SIZE_MAX"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="32"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<interface name="Equalizer.OnParameterChangeListener"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onParameterChange"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="effect" type="android.media.audiofx.Equalizer">
+</parameter>
+<parameter name="status" type="int">
+</parameter>
+<parameter name="param1" type="int">
+</parameter>
+<parameter name="param2" type="int">
+</parameter>
+<parameter name="value" type="int">
+</parameter>
+</method>
+</interface>
+<class name="Equalizer.Settings"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="Equalizer.Settings"
+ type="android.media.audiofx.Equalizer.Settings"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<constructor name="Equalizer.Settings"
+ type="android.media.audiofx.Equalizer.Settings"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="settings" type="java.lang.String">
+</parameter>
+</constructor>
+<field name="bandLevels"
+ type="short[]"
+ transient="false"
+ volatile="false"
+ value="null"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="curPreset"
+ type="short"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="numBands"
+ type="short"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="PresetReverb"
+ extends="android.media.audiofx.AudioEffect"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="PresetReverb"
+ type="android.media.audiofx.PresetReverb"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="priority" type="int">
+</parameter>
+<parameter name="audioSession" type="int">
+</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="RuntimeException" type="java.lang.RuntimeException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</constructor>
+<method name="getPreset"
+ return="short"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="getProperties"
+ return="android.media.audiofx.PresetReverb.Settings"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="setParameterListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.media.audiofx.PresetReverb.OnParameterChangeListener">
+</parameter>
+</method>
+<method name="setPreset"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="preset" type="short">
+</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="setProperties"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="settings" type="android.media.audiofx.PresetReverb.Settings">
+</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<field name="PARAM_PRESET"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PRESET_LARGEHALL"
+ type="short"
+ transient="false"
+ volatile="false"
+ value="5"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PRESET_LARGEROOM"
+ type="short"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PRESET_MEDIUMHALL"
+ type="short"
+ transient="false"
+ volatile="false"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PRESET_MEDIUMROOM"
+ type="short"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PRESET_NONE"
+ type="short"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PRESET_PLATE"
+ type="short"
+ transient="false"
+ volatile="false"
+ value="6"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PRESET_SMALLROOM"
+ type="short"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<interface name="PresetReverb.OnParameterChangeListener"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onParameterChange"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="effect" type="android.media.audiofx.PresetReverb">
+</parameter>
+<parameter name="status" type="int">
+</parameter>
+<parameter name="param" type="int">
+</parameter>
+<parameter name="value" type="short">
+</parameter>
+</method>
+</interface>
+<class name="PresetReverb.Settings"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="PresetReverb.Settings"
+ type="android.media.audiofx.PresetReverb.Settings"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<constructor name="PresetReverb.Settings"
+ type="android.media.audiofx.PresetReverb.Settings"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="settings" type="java.lang.String">
+</parameter>
+</constructor>
+<field name="preset"
+ type="short"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="Virtualizer"
+ extends="android.media.audiofx.AudioEffect"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="Virtualizer"
+ type="android.media.audiofx.Virtualizer"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="priority" type="int">
+</parameter>
+<parameter name="audioSession" type="int">
+</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="RuntimeException" type="java.lang.RuntimeException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</constructor>
+<method name="getProperties"
+ return="android.media.audiofx.Virtualizer.Settings"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="getRoundedStrength"
+ return="short"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="getStrengthSupported"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="setParameterListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.media.audiofx.Virtualizer.OnParameterChangeListener">
+</parameter>
+</method>
+<method name="setProperties"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="settings" type="android.media.audiofx.Virtualizer.Settings">
 </parameter>
 <exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
 </exception>
@@ -105414,7 +104996,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="effect" type="android.media.Virtualizer">
+<parameter name="effect" type="android.media.audiofx.Virtualizer">
 </parameter>
 <parameter name="status" type="int">
 </parameter>
@@ -105433,7 +105015,7 @@
  visibility="public"
 >
 <constructor name="Virtualizer.Settings"
- type="android.media.Virtualizer.Settings"
+ type="android.media.audiofx.Virtualizer.Settings"
  static="false"
  final="false"
  deprecated="not deprecated"
@@ -105441,7 +105023,7 @@
 >
 </constructor>
 <constructor name="Virtualizer.Settings"
- type="android.media.Virtualizer.Settings"
+ type="android.media.audiofx.Virtualizer.Settings"
  static="false"
  final="false"
  deprecated="not deprecated"
@@ -105470,7 +105052,7 @@
  visibility="public"
 >
 <constructor name="Visualizer"
- type="android.media.Visualizer"
+ type="android.media.audiofx.Visualizer"
  static="false"
  final="false"
  deprecated="not deprecated"
@@ -105608,7 +105190,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="listener" type="android.media.Visualizer.OnDataCaptureListener">
+<parameter name="listener" type="android.media.audiofx.Visualizer.OnDataCaptureListener">
 </parameter>
 <parameter name="rate" type="int">
 </parameter>
@@ -105771,7 +105353,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="visualizer" type="android.media.Visualizer">
+<parameter name="visualizer" type="android.media.audiofx.Visualizer">
 </parameter>
 <parameter name="fft" type="byte[]">
 </parameter>
@@ -105788,7 +105370,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="visualizer" type="android.media.Visualizer">
+<parameter name="visualizer" type="android.media.audiofx.Visualizer">
 </parameter>
 <parameter name="waveform" type="byte[]">
 </parameter>
@@ -106715,6 +106297,21 @@
 <parameter name="uri" type="android.net.Uri">
 </parameter>
 </constructor>
+<method name="addRequestHeader"
+ return="android.net.DownloadManager.Request"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="header" type="java.lang.String">
+</parameter>
+<parameter name="value" type="java.lang.String">
+</parameter>
+</method>
 <method name="setAllowedNetworkTypes"
  return="android.net.DownloadManager.Request"
  abstract="false"
@@ -106751,7 +106348,39 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="description" type="java.lang.String">
+<parameter name="description" type="java.lang.CharSequence">
+</parameter>
+</method>
+<method name="setDestinationInExternalFilesDir"
+ return="android.net.DownloadManager.Request"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="dirType" type="java.lang.String">
+</parameter>
+<parameter name="subPath" type="java.lang.String">
+</parameter>
+</method>
+<method name="setDestinationInExternalPublicDir"
+ return="android.net.DownloadManager.Request"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="dirType" type="java.lang.String">
+</parameter>
+<parameter name="subPath" type="java.lang.String">
 </parameter>
 </method>
 <method name="setDestinationUri"
@@ -106767,7 +106396,7 @@
 <parameter name="uri" type="android.net.Uri">
 </parameter>
 </method>
-<method name="setMediaType"
+<method name="setMimeType"
  return="android.net.DownloadManager.Request"
  abstract="false"
  native="false"
@@ -106777,22 +106406,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="mediaType" type="java.lang.String">
-</parameter>
-</method>
-<method name="setRequestHeader"
- return="android.net.DownloadManager.Request"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="header" type="java.lang.String">
-</parameter>
-<parameter name="value" type="java.lang.String">
+<parameter name="mimeType" type="java.lang.String">
 </parameter>
 </method>
 <method name="setShowRunningNotification"
@@ -106818,7 +106432,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="title" type="java.lang.String">
+<parameter name="title" type="java.lang.CharSequence">
 </parameter>
 </method>
 <method name="setVisibleInDownloadsUi"
@@ -106856,17 +106470,6 @@
  visibility="public"
 >
 </field>
-<field name="NETWORK_WIMAX"
- type="int"
- transient="false"
- volatile="false"
- value="4"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 </class>
 <class name="LocalServerSocket"
  extends="java.lang.Object"
@@ -156639,6 +156242,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"
@@ -206818,17 +206432,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"
@@ -213625,6 +213228,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"
@@ -213949,7 +213565,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"
@@ -214011,108 +213627,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"
@@ -214333,6 +213847,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"
@@ -214747,6 +214272,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/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index f901bfb..4dee350 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -19,6 +19,7 @@
 package com.android.commands.am;
 
 import android.app.ActivityManagerNative;
+import android.app.IActivityController;
 import android.app.IActivityManager;
 import android.app.IInstrumentationWatcher;
 import android.app.Instrumentation;
@@ -28,12 +29,18 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.util.AndroidException;
 import android.view.IWindowManager;
 
+import java.io.BufferedReader;
+import java.io.DataInputStream;
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStreamReader;
 import java.io.PrintStream;
 import java.net.URISyntaxException;
 import java.util.Iterator;
@@ -99,6 +106,8 @@
             runProfile();
         } else if (op.equals("dumpheap")) {
             runDumpHeap();
+        } else if (op.equals("monitor")) {
+            runMonitor();
         } else {
             throw new IllegalArgumentException("Unknown command: " + op);
         }
@@ -472,6 +481,210 @@
         }
     }
 
+    class MyActivityController extends IActivityController.Stub {
+        static final int STATE_NORMAL = 0;
+        static final int STATE_CRASHED = 1;
+        static final int STATE_EARLY_ANR = 2;
+        static final int STATE_ANR = 3;
+
+        int mState;
+
+        static final int RESULT_DEFAULT = 0;
+
+        static final int RESULT_CRASH_DIALOG = 0;
+        static final int RESULT_CRASH_KILL = 1;
+
+        static final int RESULT_EARLY_ANR_CONTINUE = 0;
+        static final int RESULT_EARLY_ANR_KILL = 1;
+
+        static final int RESULT_ANR_DIALOG = 0;
+        static final int RESULT_ANR_KILL = 1;
+        static final int RESULT_ANR_WAIT = 1;
+
+        int mResult;
+
+        @Override
+        public boolean activityResuming(String pkg) throws RemoteException {
+            synchronized (this) {
+                System.out.println("** Activity resuming: " + pkg);
+            }
+            return true;
+        }
+
+        @Override
+        public boolean activityStarting(Intent intent, String pkg) throws RemoteException {
+            synchronized (this) {
+                System.out.println("** Activity starting: " + pkg);
+            }
+            return true;
+        }
+
+        @Override
+        public boolean appCrashed(String processName, int pid, String shortMsg, String longMsg,
+                long timeMillis, String stackTrace) throws RemoteException {
+            synchronized (this) {
+                System.out.println("** ERROR: PROCESS CRASHED");
+                System.out.println("processName: " + processName);
+                System.out.println("processPid: " + pid);
+                System.out.println("shortMsg: " + shortMsg);
+                System.out.println("longMsg: " + longMsg);
+                System.out.println("timeMillis: " + timeMillis);
+                System.out.println("stack:");
+                System.out.print(stackTrace);
+                System.out.println("#");
+                int result = waitControllerLocked(STATE_CRASHED);
+                return result == RESULT_CRASH_KILL ? false : true;
+            }
+        }
+
+        @Override
+        public int appEarlyNotResponding(String processName, int pid, String annotation)
+                throws RemoteException {
+            synchronized (this) {
+                System.out.println("** ERROR: EARLY PROCESS NOT RESPONDING");
+                System.out.println("processName: " + processName);
+                System.out.println("processPid: " + pid);
+                System.out.println("annotation: " + annotation);
+                int result = waitControllerLocked(STATE_EARLY_ANR);
+                if (result == RESULT_EARLY_ANR_KILL) return -1;
+                return 0;
+            }
+        }
+
+        @Override
+        public int appNotResponding(String processName, int pid, String processStats)
+                throws RemoteException {
+            synchronized (this) {
+                System.out.println("** ERROR: PROCESS NOT RESPONDING");
+                System.out.println("processName: " + processName);
+                System.out.println("processPid: " + pid);
+                System.out.println("processStats:");
+                System.out.print(processStats);
+                System.out.println("#");
+                int result = waitControllerLocked(STATE_ANR);
+                if (result == RESULT_ANR_KILL) return -1;
+                if (result == RESULT_ANR_WAIT) return 1;
+                return 0;
+            }
+        }
+
+        int waitControllerLocked(int state) {
+            mState = state;
+            System.out.println("");
+            printMessageForState();
+
+            while (mState != STATE_NORMAL) {
+                try {
+                    wait();
+                } catch (InterruptedException e) {
+                }
+            }
+
+            return mResult;
+        }
+
+        void resumeController(int result) {
+            synchronized (this) {
+                mState = STATE_NORMAL;
+                mResult = result;
+                notifyAll();
+            }
+        }
+
+        void printMessageForState() {
+            switch (mState) {
+                case STATE_NORMAL:
+                    System.out.println("Monitoring activity manager...  available commands:");
+                    break;
+                case STATE_CRASHED:
+                    System.out.println("Waiting after crash...  available commands:");
+                    System.out.println("(c)ontinue: show crash dialog");
+                    System.out.println("(k)ill: immediately kill app");
+                    break;
+                case STATE_EARLY_ANR:
+                    System.out.println("Waiting after early ANR...  available commands:");
+                    System.out.println("(c)ontinue: standard ANR processing");
+                    System.out.println("(k)ill: immediately kill app");
+                    break;
+                case STATE_ANR:
+                    System.out.println("Waiting after ANR...  available commands:");
+                    System.out.println("(c)ontinue: show ANR dialog");
+                    System.out.println("(k)ill: immediately kill app");
+                    System.out.println("(w)ait: wait some more");
+                    break;
+            }
+            System.out.println("(q)uit: finish monitoring");
+        }
+
+        void run() throws RemoteException {
+            try {
+                printMessageForState();
+
+                mAm.setActivityController(this);
+                mState = STATE_NORMAL;
+
+                InputStreamReader converter = new InputStreamReader(System.in);
+                BufferedReader in = new BufferedReader(converter);
+                String line;
+
+                while ((line = in.readLine()) != null) {
+                    boolean addNewline = true;
+                    if (line.length() <= 0) {
+                        addNewline = false;
+                    } else if ("q".equals(line) || "quit".equals(line)) {
+                        resumeController(RESULT_DEFAULT);
+                        break;
+                    } else if (mState == STATE_CRASHED) {
+                        if ("c".equals(line) || "continue".equals(line)) {
+                            resumeController(RESULT_CRASH_DIALOG);
+                        } else if ("k".equals(line) || "kill".equals(line)) {
+                            resumeController(RESULT_CRASH_KILL);
+                        } else {
+                            System.out.println("Invalid command: " + line);
+                        }
+                    } else if (mState == STATE_ANR) {
+                        if ("c".equals(line) || "continue".equals(line)) {
+                            resumeController(RESULT_ANR_DIALOG);
+                        } else if ("k".equals(line) || "kill".equals(line)) {
+                            resumeController(RESULT_ANR_KILL);
+                        } else if ("w".equals(line) || "wait".equals(line)) {
+                            resumeController(RESULT_ANR_WAIT);
+                        } else {
+                            System.out.println("Invalid command: " + line);
+                        }
+                    } else if (mState == STATE_EARLY_ANR) {
+                        if ("c".equals(line) || "continue".equals(line)) {
+                            resumeController(RESULT_EARLY_ANR_CONTINUE);
+                        } else if ("k".equals(line) || "kill".equals(line)) {
+                            resumeController(RESULT_EARLY_ANR_KILL);
+                        } else {
+                            System.out.println("Invalid command: " + line);
+                        }
+                    } else {
+                        System.out.println("Invalid command: " + line);
+                    }
+
+                    synchronized (this) {
+                        if (addNewline) {
+                            System.out.println("");
+                        }
+                        printMessageForState();
+                    }
+                }
+
+            } catch (IOException e) {
+                e.printStackTrace();
+            } finally {
+                mAm.setActivityController(null);
+            }
+        }
+    }
+
+    private void runMonitor() throws Exception {
+        MyActivityController controller = new MyActivityController();
+        controller.run();
+    }
+
     private class IntentReceiver extends IIntentReceiver.Stub {
         private boolean mFinished = false;
 
@@ -649,6 +862,8 @@
                 "    dump heap: am dumpheap [flags] <PROCESS> <FILE>\n" +
                 "        -n: dump native heap instead of managed heap\n" +
                 "\n" +
+                "    start monitoring: am monitor\n" +
+                "\n" +
                 "    <INTENT> specifications include these flags:\n" +
                 "        [-a <ACTION>] [-d <DATA_URI>] [-t <MIME_TYPE>]\n" +
                 "        [-c <CATEGORY> [-c <CATEGORY>] ...]\n" +
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index 8ab94ad..eb4b733 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+//#define LOG_NDEBUG 0
+#define LOG_TAG "stagefright"
+#include <media/stagefright/foundation/ADebug.h>
+
 #include <sys/time.h>
 
 #include <stdlib.h>
@@ -30,7 +34,6 @@
 #include <media/stagefright/AudioPlayer.h>
 #include <media/stagefright/DataSource.h>
 #include <media/stagefright/JPEGSource.h>
-#include <media/stagefright/MediaDebug.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MediaExtractor.h>
@@ -43,6 +46,8 @@
 #include <media/stagefright/foundation/hexdump.h>
 #include <media/stagefright/MPEG4Writer.h>
 
+#include <fcntl.h>
+
 using namespace android;
 
 static long gNumRepetitions;
@@ -120,7 +125,7 @@
 
             bool shouldSeek = false;
             if (err == INFO_FORMAT_CHANGED) {
-                CHECK_EQ(buffer, NULL);
+                CHECK(buffer == NULL);
 
                 printf("format changed.\n");
                 continue;
@@ -206,7 +211,7 @@
             options.clearSeekTo();
 
             if (err != OK) {
-                CHECK_EQ(buffer, NULL);
+                CHECK(buffer == NULL);
 
                 if (err == INFO_FORMAT_CHANGED) {
                     printf("format changed.\n");
@@ -267,14 +272,98 @@
     }
 }
 
-static void writeSourceToMP4(const sp<MediaSource> &source) {
+////////////////////////////////////////////////////////////////////////////////
+
+struct DetectSyncSource : public MediaSource {
+    DetectSyncSource(const sp<MediaSource> &source);
+
+    virtual status_t start(MetaData *params = NULL);
+    virtual status_t stop();
+    virtual sp<MetaData> getFormat();
+
+    virtual status_t read(
+            MediaBuffer **buffer, const ReadOptions *options);
+
+private:
+    sp<MediaSource> mSource;
+    bool mIsAVC;
+
+    DISALLOW_EVIL_CONSTRUCTORS(DetectSyncSource);
+};
+
+DetectSyncSource::DetectSyncSource(const sp<MediaSource> &source)
+    : mSource(source),
+      mIsAVC(false) {
+    const char *mime;
+    CHECK(mSource->getFormat()->findCString(kKeyMIMEType, &mime));
+
+    mIsAVC = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC);
+}
+
+status_t DetectSyncSource::start(MetaData *params) {
+    return mSource->start(params);
+}
+
+status_t DetectSyncSource::stop() {
+    return mSource->stop();
+}
+
+sp<MetaData> DetectSyncSource::getFormat() {
+    return mSource->getFormat();
+}
+
+static bool isIDRFrame(MediaBuffer *buffer) {
+    const uint8_t *data =
+        (const uint8_t *)buffer->data() + buffer->range_offset();
+    size_t size = buffer->range_length();
+    for (size_t i = 0; i + 3 < size; ++i) {
+        if (!memcmp("\x00\x00\x01", &data[i], 3)) {
+            uint8_t nalType = data[i + 3] & 0x1f;
+            if (nalType == 5) {
+                return true;
+            }
+        }
+    }
+
+    return false;
+}
+
+status_t DetectSyncSource::read(
+        MediaBuffer **buffer, const ReadOptions *options) {
+    status_t err = mSource->read(buffer, options);
+
+    if (err != OK) {
+        return err;
+    }
+
+    if (mIsAVC && isIDRFrame(*buffer)) {
+        (*buffer)->meta_data()->setInt32(kKeyIsSyncFrame, true);
+    } else {
+        (*buffer)->meta_data()->setInt32(kKeyIsSyncFrame, false);
+    }
+
+    return OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+static void writeSourceToMP4(
+        sp<MediaSource> &source, bool syncInfoPresent) {
+    if (!syncInfoPresent) {
+        source = new DetectSyncSource(source);
+    }
+
     sp<MPEG4Writer> writer =
         new MPEG4Writer(gWriteMP4Filename.string());
 
-    CHECK_EQ(writer->addSource(source), OK);
+    // at most one minute.
+    writer->setMaxFileDuration(60000000ll);
+
+    CHECK_EQ(writer->addSource(source), (status_t)OK);
 
     sp<MetaData> params = new MetaData;
-    CHECK_EQ(writer->start(), OK);
+    params->setInt32(kKeyNotRealTime, true);
+    CHECK_EQ(writer->start(params.get()), (status_t)OK);
 
     while (!writer->reachedEOS()) {
         usleep(100000);
@@ -283,7 +372,7 @@
 }
 
 static void performSeekTest(const sp<MediaSource> &source) {
-    CHECK_EQ(OK, source->start());
+    CHECK_EQ((status_t)OK, source->start());
 
     int64_t durationUs;
     CHECK(source->getFormat()->findInt64(kKeyDuration, &durationUs));
@@ -335,7 +424,7 @@
         }
     }
 
-    CHECK_EQ(OK, source->stop());
+    CHECK_EQ((status_t)OK, source->stop());
 }
 
 static void usage(const char *me) {
@@ -481,10 +570,10 @@
         for (int k = 0; k < argc; ++k) {
             const char *filename = argv[k];
 
-            CHECK_EQ(retriever->setDataSource(filename), OK);
+            CHECK_EQ(retriever->setDataSource(filename), (status_t)OK);
             CHECK_EQ(retriever->setMode(
                         METADATA_MODE_FRAME_CAPTURE_AND_METADATA_RETRIEVAL),
-                     OK);
+                     (status_t)OK);
 
             sp<IMemory> mem = retriever->captureFrame();
 
@@ -530,7 +619,7 @@
             Vector<CodecCapabilities> results;
             CHECK_EQ(QueryCodecs(omx, kMimeTypes[k],
                                  true, // queryDecoders
-                                 &results), OK);
+                                 &results), (status_t)OK);
 
             for (size_t i = 0; i < results.size(); ++i) {
                 printf("  decoder '%s' supports ",
@@ -579,6 +668,8 @@
     status_t err = client.connect();
 
     for (int k = 0; k < argc; ++k) {
+        bool syncInfoPresent = true;
+
         const char *filename = argv[k];
 
         sp<DataSource> dataSource = DataSource::CreateFromURI(filename);
@@ -625,6 +716,8 @@
                 }
 
                 extractor = rtspController.get();
+
+                syncInfoPresent = false;
             } else {
                 extractor = MediaExtractor::Create(dataSource);
                 if (extractor == NULL) {
@@ -674,7 +767,7 @@
         }
 
         if (gWriteMP4) {
-            writeSourceToMP4(mediaSource);
+            writeSourceToMP4(mediaSource, syncInfoPresent);
         } else if (seekTest) {
             performSeekTest(mediaSource);
         } else {
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index 1e2bbcc..02b2dce 100755
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -614,6 +614,17 @@
     }
 
     /**
+     * Removes all listeners from the set listening to frame updates for this animation.
+     */
+    public void removeAllUpdateListeners() {
+        if (mUpdateListeners == null) {
+            return;
+        }
+        mUpdateListeners.clear();
+        mUpdateListeners = null;
+    }
+
+    /**
      * Removes a listener from the set listening to frame updates for this animation.
      *
      * @param listener the listener to be removed from the current set of update listeners
@@ -685,7 +696,15 @@
      */
     private void start(boolean playBackwards) {
         mPlayingBackwards = playBackwards;
-        if ((mStartDelay == 0) && (Thread.currentThread() == Looper.getMainLooper().getThread())) {
+        Looper looper = Looper.getMainLooper();
+        final boolean isUiThread;
+        if (looper != null) {
+            isUiThread = Thread.currentThread() == looper.getThread();
+        } else {
+            // ignore check if we don't have a Looper (this isn't an Activity)
+            isUiThread = true;
+        }
+        if ((mStartDelay == 0) && isUiThread) {
             if (mListeners != null) {
                 ArrayList<AnimatorListener> tmpListeners =
                         (ArrayList<AnimatorListener>) mListeners.clone();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 3f74904..2ff88da 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -61,6 +61,7 @@
 import android.util.DisplayMetrics;
 import android.util.EventLog;
 import android.util.Log;
+import android.util.LogPrinter;
 import android.util.Slog;
 import android.view.Display;
 import android.view.HardwareRenderer;
@@ -121,6 +122,7 @@
     private static final android.graphics.Bitmap.Config THUMBNAIL_FORMAT = Bitmap.Config.RGB_565;
     private static final boolean DEBUG = false;
     static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV;
+    static final boolean DEBUG_MESSAGES = false;
     static final boolean DEBUG_BROADCAST = false;
     private static final boolean DEBUG_RESULTS = false;
     private static final boolean DEBUG_BACKUP = false;
@@ -913,7 +915,7 @@
         public static final int DUMP_HEAP               = 135;
         public static final int DUMP_ACTIVITY           = 136;
         String codeToString(int code) {
-            if (localLOGV) {
+            if (DEBUG_MESSAGES) {
                 switch (code) {
                     case LAUNCH_ACTIVITY: return "LAUNCH_ACTIVITY";
                     case PAUSE_ACTIVITY: return "PAUSE_ACTIVITY";
@@ -957,6 +959,7 @@
             return "(unknown)";
         }
         public void handleMessage(Message msg) {
+            if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + msg.what);
             switch (msg.what) {
                 case LAUNCH_ACTIVITY: {
                     ActivityClientRecord r = (ActivityClientRecord)msg.obj;
@@ -1084,6 +1087,7 @@
                     handleDumpActivity((DumpComponentInfo)msg.obj);
                     break;
             }
+            if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + msg.what);
         }
 
         void maybeSnapshot() {
@@ -1550,7 +1554,7 @@
 
     private final void queueOrSendMessage(int what, Object obj, int arg1, int arg2) {
         synchronized (this) {
-            if (localLOGV) Slog.v(
+            if (DEBUG_MESSAGES) Slog.v(
                 TAG, "SCHEDULE " + what + " " + mH.codeToString(what)
                 + ": " + arg1 + " / " + obj);
             Message msg = Message.obtain();
@@ -3699,6 +3703,11 @@
         ActivityThread thread = new ActivityThread();
         thread.attach(false);
 
+        if (false) {
+            Looper.myLooper().setMessageLogging(new
+                    LogPrinter(Log.DEBUG, "ActivityThread"));
+        }
+
         Looper.loop();
 
         if (Process.supportsProcesses()) {
diff --git a/core/java/android/app/IActivityController.aidl b/core/java/android/app/IActivityController.aidl
index c76a517..aca8305 100644
--- a/core/java/android/app/IActivityController.aidl
+++ b/core/java/android/app/IActivityController.aidl
@@ -48,6 +48,11 @@
             long timeMillis, String stackTrace);
     
     /**
+     * Early call as soon as an ANR is detected.
+     */
+    int appEarlyNotResponding(String processName, int pid, String annotation);
+
+    /**
      * An application process is not responding.  Return 0 to show the "app
      * not responding" dialog, 1 to continue waiting, or -1 to kill it
      * immediately.
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index 3df1790..4cb7026 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -2428,73 +2428,73 @@
      */
     /* package */ static ArrayList<DbStats> getDbStats() {
         ArrayList<DbStats> dbStatsList = new ArrayList<DbStats>();
-        // make a local copy of mActiveDatabases - so that this method is not competing
-        // for synchronization lock on mActiveDatabases
-        ArrayList<WeakReference<SQLiteDatabase>> tempList;
-        synchronized(mActiveDatabases) {
-            tempList = (ArrayList<WeakReference<SQLiteDatabase>>)mActiveDatabases.clone();
-        }
-        for (WeakReference<SQLiteDatabase> w : tempList) {
-            SQLiteDatabase db = w.get();
-            if (db == null || !db.isOpen()) {
-                continue;
-            }
-
-            synchronized (db) {
-                try {
-                    // get SQLITE_DBSTATUS_LOOKASIDE_USED for the db
-                    int lookasideUsed = db.native_getDbLookaside();
-
-                    // get the lastnode of the dbname
-                    String path = db.getPath();
-                    int indx = path.lastIndexOf("/");
-                    String lastnode = path.substring((indx != -1) ? ++indx : 0);
-
-                    // get list of attached dbs and for each db, get its size and pagesize
-                    ArrayList<Pair<String, String>> attachedDbs = db.getAttachedDbs();
-                    if (attachedDbs == null) {
-                        continue;
-                    }
-                    for (int i = 0; i < attachedDbs.size(); i++) {
-                        Pair<String, String> p = attachedDbs.get(i);
-                        long pageCount = DatabaseUtils.longForQuery(db, "PRAGMA " + p.first
-                                + ".page_count;", null);
-
-                        // first entry in the attached db list is always the main database
-                        // don't worry about prefixing the dbname with "main"
-                        String dbName;
-                        if (i == 0) {
-                            dbName = lastnode;
-                        } else {
-                            // lookaside is only relevant for the main db
-                            lookasideUsed = 0;
-                            dbName = "  (attached) " + p.first;
-                            // if the attached db has a path, attach the lastnode from the path to above
-                            if (p.second.trim().length() > 0) {
-                                int idx = p.second.lastIndexOf("/");
-                                dbName += " : " + p.second.substring((idx != -1) ? ++idx : 0);
-                            }
-                        }
-                        if (pageCount > 0) {
-                            dbStatsList.add(new DbStats(dbName, pageCount, db.getPageSize(),
-                                    lookasideUsed, db.getCacheHitNum(), db.getCacheMissNum(),
-                                    db.getCachesize()));
-                        }
-                    }
-                    // if there are pooled connections, return the cache stats for them also.
-                    if (db.mConnectionPool != null) {
-                        for (SQLiteDatabase pDb : db.mConnectionPool.getConnectionList()) {
-                            dbStatsList.add(new DbStats("(pooled # " + pDb.mConnectionNum + ") "
-                                    + lastnode, 0, 0, 0, pDb.getCacheHitNum(),
-                                    pDb.getCacheMissNum(), pDb.getCachesize()));
-                        }
-                    }
-                } catch (SQLiteException e) {
-                    // ignore. we don't care about exceptions when we are taking adb
-                    // bugreport!
-                }
-            }
-        }
+//        // make a local copy of mActiveDatabases - so that this method is not competing
+//        // for synchronization lock on mActiveDatabases
+//        ArrayList<WeakReference<SQLiteDatabase>> tempList;
+//        synchronized(mActiveDatabases) {
+//            tempList = (ArrayList<WeakReference<SQLiteDatabase>>)mActiveDatabases.clone();
+//        }
+//        for (WeakReference<SQLiteDatabase> w : tempList) {
+//            SQLiteDatabase db = w.get();
+//            if (db == null || !db.isOpen()) {
+//                continue;
+//            }
+//
+//            synchronized (db) {
+//                try {
+//                    // get SQLITE_DBSTATUS_LOOKASIDE_USED for the db
+//                    int lookasideUsed = db.native_getDbLookaside();
+//
+//                    // get the lastnode of the dbname
+//                    String path = db.getPath();
+//                    int indx = path.lastIndexOf("/");
+//                    String lastnode = path.substring((indx != -1) ? ++indx : 0);
+//
+//                    // get list of attached dbs and for each db, get its size and pagesize
+//                    ArrayList<Pair<String, String>> attachedDbs = db.getAttachedDbs();
+//                    if (attachedDbs == null) {
+//                        continue;
+//                    }
+//                    for (int i = 0; i < attachedDbs.size(); i++) {
+//                        Pair<String, String> p = attachedDbs.get(i);
+//                        long pageCount = DatabaseUtils.longForQuery(db, "PRAGMA " + p.first
+//                                + ".page_count;", null);
+//
+//                        // first entry in the attached db list is always the main database
+//                        // don't worry about prefixing the dbname with "main"
+//                        String dbName;
+//                        if (i == 0) {
+//                            dbName = lastnode;
+//                        } else {
+//                            // lookaside is only relevant for the main db
+//                            lookasideUsed = 0;
+//                            dbName = "  (attached) " + p.first;
+//                            // if the attached db has a path, attach the lastnode from the path to above
+//                            if (p.second.trim().length() > 0) {
+//                                int idx = p.second.lastIndexOf("/");
+//                                dbName += " : " + p.second.substring((idx != -1) ? ++idx : 0);
+//                            }
+//                        }
+//                        if (pageCount > 0) {
+//                            dbStatsList.add(new DbStats(dbName, pageCount, db.getPageSize(),
+//                                    lookasideUsed, db.getCacheHitNum(), db.getCacheMissNum(),
+//                                    db.getCachesize()));
+//                        }
+//                    }
+//                    // if there are pooled connections, return the cache stats for them also.
+//                    if (db.mConnectionPool != null) {
+//                        for (SQLiteDatabase pDb : db.mConnectionPool.getConnectionList()) {
+//                            dbStatsList.add(new DbStats("(pooled # " + pDb.mConnectionNum + ") "
+//                                    + lastnode, 0, 0, 0, pDb.getCacheHitNum(),
+//                                    pDb.getCacheMissNum(), pDb.getCachesize()));
+//                        }
+//                    }
+//                } catch (SQLiteException e) {
+//                    // ignore. we don't care about exceptions when we are taking adb
+//                    // bugreport!
+//                }
+//            }
+//        }
         return dbStatsList;
     }
 
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/net/DownloadManager.java b/core/java/android/net/DownloadManager.java
index fc5ebb3..12c05cc 100644
--- a/core/java/android/net/DownloadManager.java
+++ b/core/java/android/net/DownloadManager.java
@@ -19,19 +19,21 @@
 import android.content.ContentResolver;
 import android.content.ContentUris;
 import android.content.ContentValues;
+import android.content.Context;
 import android.database.Cursor;
 import android.database.CursorWrapper;
+import android.os.Environment;
 import android.os.ParcelFileDescriptor;
 import android.provider.BaseColumns;
 import android.provider.Downloads;
+import android.util.Pair;
 
+import java.io.File;
 import java.io.FileNotFoundException;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
-import java.util.Map;
 import java.util.Set;
 
 /**
@@ -245,6 +247,7 @@
         Downloads.COLUMN_LAST_MODIFICATION,
         Downloads.COLUMN_DESTINATION,
         Downloads.Impl.COLUMN_FILE_NAME_HINT,
+        Downloads.Impl._DATA,
     };
 
     private static final Set<String> LONG_COLUMNS = new HashSet<String>(
@@ -252,8 +255,12 @@
                           COLUMN_BYTES_DOWNLOADED_SO_FAR, COLUMN_LAST_MODIFIED_TIMESTAMP));
 
     /**
-     * This class contains all the information necessary to request a new download.  The URI is the
+     * This class contains all the information necessary to request a new download. The URI is the
      * only required parameter.
+     *
+     * Note that the default download destination is a shared volume where the system might delete
+     * your file if it needs to reclaim space for system use. If this is a problem, use a location
+     * on external storage (see {@link #setDestinationUri(Uri)}.
      */
     public static class Request {
         /**
@@ -268,19 +275,13 @@
          */
         public static final int NETWORK_WIFI = 1 << 1;
 
-        /**
-         * Bit flag for {@link #setAllowedNetworkTypes} corresponding to
-         * {@link ConnectivityManager#TYPE_WIMAX}.
-         */
-        public static final int NETWORK_WIMAX = 1 << 2;
-
         private Uri mUri;
         private Uri mDestinationUri;
-        private Map<String, String> mRequestHeaders = new HashMap<String, String>();
-        private String mTitle;
-        private String mDescription;
+        private List<Pair<String, String>> mRequestHeaders = new ArrayList<Pair<String, String>>();
+        private CharSequence mTitle;
+        private CharSequence mDescription;
         private boolean mShowNotification = true;
-        private String mMediaType;
+        private String mMimeType;
         private boolean mRoamingAllowed = true;
         private int mAllowedNetworkTypes = ~0; // default to all network types allowed
         private boolean mIsVisibleInDownloadsUi = true;
@@ -300,12 +301,12 @@
         }
 
         /**
-         * Set the local destination for the downloaded data. Must be a file URI to a path on
+         * Set the local destination for the downloaded file. Must be a file URI to a path on
          * external storage, and the calling application must have the WRITE_EXTERNAL_STORAGE
          * permission.
          *
-         *  By default, downloads are saved to a generated file in the download cache and may be
-         * deleted by the download manager at any time.
+         * By default, downloads are saved to a generated filename in the shared download cache and
+         * may be deleted by the system at any time to reclaim space.
          *
          * @return this object
          */
@@ -315,13 +316,62 @@
         }
 
         /**
-         * Set an HTTP header to be included with the download request.
+         * Set the local destination for the downloaded file to a path within the application's
+         * external files directory (as returned by {@link Context#getExternalFilesDir(String)}.
+         *
+         * @param context the {@link Context} to use in determining the external files directory
+         * @param dirType the directory type to pass to {@link Context#getExternalFilesDir(String)}
+         * @param subPath the path within the external directory, including the destination filename
+         * @return this object
+         */
+        public Request setDestinationInExternalFilesDir(Context context, String dirType,
+                String subPath) {
+            setDestinationFromBase(context.getExternalFilesDir(dirType), subPath);
+            return this;
+        }
+
+        /**
+         * Set the local destination for the downloaded file to a path within the public external
+         * storage directory (as returned by
+         * {@link Environment#getExternalStoragePublicDirectory(String)}.
+         *
+         * @param dirType the directory type to pass to
+         *        {@link Environment#getExternalStoragePublicDirectory(String)}
+         * @param subPath the path within the external directory, including the destination filename
+         * @return this object
+         */
+        public Request setDestinationInExternalPublicDir(String dirType, String subPath) {
+            setDestinationFromBase(Environment.getExternalStoragePublicDirectory(dirType), subPath);
+            return this;
+        }
+
+        private void setDestinationFromBase(File base, String subPath) {
+            if (subPath == null) {
+                throw new NullPointerException("subPath cannot be null");
+            }
+            mDestinationUri = Uri.withAppendedPath(Uri.fromFile(base), subPath);
+        }
+
+        /**
+         * Add an HTTP header to be included with the download request.  The header will be added to
+         * the end of the list.
          * @param header HTTP header name
          * @param value header value
          * @return this object
+         * @see <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2">HTTP/1.1
+         *      Message Headers</a>
          */
-        public Request setRequestHeader(String header, String value) {
-            mRequestHeaders.put(header, value);
+        public Request addRequestHeader(String header, String value) {
+            if (header == null) {
+                throw new NullPointerException("header cannot be null");
+            }
+            if (header.contains(":")) {
+                throw new IllegalArgumentException("header may not contain ':'");
+            }
+            if (value == null) {
+                value = "";
+            }
+            mRequestHeaders.add(Pair.create(header, value));
             return this;
         }
 
@@ -329,7 +379,7 @@
          * Set the title of this download, to be displayed in notifications (if enabled)
          * @return this object
          */
-        public Request setTitle(String title) {
+        public Request setTitle(CharSequence title) {
             mTitle = title;
             return this;
         }
@@ -338,19 +388,20 @@
          * Set a description of this download, to be displayed in notifications (if enabled)
          * @return this object
          */
-        public Request setDescription(String description) {
+        public Request setDescription(CharSequence description) {
             mDescription = description;
             return this;
         }
 
         /**
-         * Set the Internet Media Type of this download.  This will override the media type declared
+         * Set the MIME content type of this download.  This will override the content type declared
          * in the server's response.
-         * @see <a href="http://www.ietf.org/rfc/rfc1590.txt">RFC 1590, defining Media Types</a>
+         * @see <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7">HTTP/1.1
+         *      Media Types</a>
          * @return this object
          */
-        public Request setMediaType(String mediaType) {
-            mMediaType = mediaType;
+        public Request setMimeType(String mimeType) {
+            mMimeType = mimeType;
             return this;
         }
 
@@ -428,7 +479,7 @@
 
             putIfNonNull(values, Downloads.COLUMN_TITLE, mTitle);
             putIfNonNull(values, Downloads.COLUMN_DESCRIPTION, mDescription);
-            putIfNonNull(values, Downloads.COLUMN_MIME_TYPE, mMediaType);
+            putIfNonNull(values, Downloads.COLUMN_MIME_TYPE, mMimeType);
 
             values.put(Downloads.COLUMN_VISIBILITY,
                     mShowNotification ? Downloads.VISIBILITY_VISIBLE
@@ -443,16 +494,16 @@
 
         private void encodeHttpHeaders(ContentValues values) {
             int index = 0;
-            for (Map.Entry<String, String> entry : mRequestHeaders.entrySet()) {
-                String headerString = entry.getKey() + ": " + entry.getValue();
+            for (Pair<String, String> header : mRequestHeaders) {
+                String headerString = header.first + ": " + header.second;
                 values.put(Downloads.Impl.RequestHeaders.INSERT_KEY_PREFIX + index, headerString);
                 index++;
             }
         }
 
-        private void putIfNonNull(ContentValues contentValues, String key, String value) {
+        private void putIfNonNull(ContentValues contentValues, String key, Object value) {
             if (value != null) {
-                contentValues.put(key, value);
+                contentValues.put(key, value.toString());
             }
         }
     }
@@ -831,6 +882,11 @@
                 return getUnderlyingString(Downloads.Impl.COLUMN_FILE_NAME_HINT);
             }
 
+            if (destinationType == Downloads.Impl.DESTINATION_EXTERNAL) {
+                // return stored destination for legacy external download
+                return Uri.fromFile(new File(getUnderlyingString(Downloads.Impl._DATA))).toString();
+            }
+
             // return content URI for cache download
             long downloadId = getUnderlyingLong(Downloads.Impl._ID);
             return ContentUris.withAppendedId(mBaseUri, downloadId).toString();
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index ba8014f2..d49c8be 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -301,7 +301,11 @@
          */
         public static abstract class Proc {
 
-            public static class ExcessiveWake {
+            public static class ExcessivePower {
+                public static final int TYPE_WAKE = 1;
+                public static final int TYPE_CPU = 2;
+
+                public int type;
                 public long overTime;
                 public long usedTime;
             }
@@ -343,9 +347,9 @@
              */
             public abstract long getTimeAtCpuSpeedStep(int speedStep, int which);
 
-            public abstract int countExcessiveWakes();
+            public abstract int countExcessivePowers();
 
-            public abstract ExcessiveWake getExcessiveWake(int i);
+            public abstract ExcessivePower getExcessivePower(int i);
         }
 
         /**
@@ -1593,7 +1597,7 @@
                     systemTime = ps.getSystemTime(which);
                     starts = ps.getStarts(which);
                     numExcessive = which == STATS_SINCE_CHARGED
-                            ? ps.countExcessiveWakes() : 0;
+                            ? ps.countExcessivePowers() : 0;
 
                     if (userTime != 0 || systemTime != 0 || starts != 0
                             || numExcessive != 0) {
@@ -1609,9 +1613,17 @@
                         }
                         pw.println(sb.toString());
                         for (int e=0; e<numExcessive; e++) {
-                            Uid.Proc.ExcessiveWake ew = ps.getExcessiveWake(e);
+                            Uid.Proc.ExcessivePower ew = ps.getExcessivePower(e);
                             if (ew != null) {
-                                pw.print(prefix); pw.print("      * Killed for wake lock use: ");
+                                pw.print(prefix); pw.print("      * Killed for ");
+                                        if (ew.type == Uid.Proc.ExcessivePower.TYPE_WAKE) {
+                                            pw.print("wake lock");
+                                        } else if (ew.type == Uid.Proc.ExcessivePower.TYPE_CPU) {
+                                            pw.print("cpu");
+                                        } else {
+                                            pw.print("unknown");
+                                        }
+                                        pw.print(" use: ");
                                         TimeUtils.formatDuration(ew.usedTime, pw);
                                         pw.print(" over ");
                                         TimeUtils.formatDuration(ew.overTime, pw);
diff --git a/core/java/android/preference/PreferenceFragment.java b/core/java/android/preference/PreferenceFragment.java
index 479497a..7629c31 100644
--- a/core/java/android/preference/PreferenceFragment.java
+++ b/core/java/android/preference/PreferenceFragment.java
@@ -144,6 +144,7 @@
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         mPreferenceManager = new PreferenceManager(getActivity(), FIRST_REQUEST_CODE);
+        mPreferenceManager.setFragment(this);
         mPreferenceManager.setOnPreferenceTreeClickListener(this);
     }
 
diff --git a/core/java/android/preference/PreferenceManager.java b/core/java/android/preference/PreferenceManager.java
index fa83897..42150ed 100644
--- a/core/java/android/preference/PreferenceManager.java
+++ b/core/java/android/preference/PreferenceManager.java
@@ -16,12 +16,7 @@
 
 package android.preference;
 
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-
 import android.app.Activity;
-import android.app.Dialog;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
@@ -34,6 +29,10 @@
 import android.os.Bundle;
 import android.util.Log;
 
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+
 /**
  * Used to help create {@link Preference} hierarchies
  * from activities or XML.
@@ -61,6 +60,11 @@
     private Activity mActivity;
 
     /**
+     * Fragment that owns this instance.
+     */
+    private PreferenceFragment mFragment;
+
+    /**
      * The context to use. This should always be set.
      * 
      * @see #mActivity
@@ -158,7 +162,21 @@
         
         setSharedPreferencesName(getDefaultSharedPreferencesName(context));
     }
-    
+
+    /**
+     * Sets the owning preference fragment
+     */
+    void setFragment(PreferenceFragment fragment) {
+        mFragment = fragment;
+    }
+
+    /**
+     * Returns the owning preference fragment, if any.
+     */
+    PreferenceFragment getFragment() {
+        return mFragment;
+    }
+
     /**
      * Returns a list of {@link Activity} (indirectly) that match a given
      * {@link Intent}.
diff --git a/core/java/android/preference/RingtonePreference.java b/core/java/android/preference/RingtonePreference.java
index b46f180..cf14097 100644
--- a/core/java/android/preference/RingtonePreference.java
+++ b/core/java/android/preference/RingtonePreference.java
@@ -24,7 +24,6 @@
 import android.provider.Settings.System;
 import android.text.TextUtils;
 import android.util.AttributeSet;
-import android.util.Log;
 
 /**
  * A {@link Preference} that allows the user to choose a ringtone from those on the device. 
@@ -137,7 +136,12 @@
         // Launch the ringtone picker
         Intent intent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);
         onPrepareRingtonePickerIntent(intent);
-        getPreferenceManager().getActivity().startActivityForResult(intent, mRequestCode);
+        PreferenceFragment owningFragment = getPreferenceManager().getFragment();
+        if (owningFragment != null) {
+            owningFragment.startActivityForResult(intent, mRequestCode);
+        } else {
+            getPreferenceManager().getActivity().startActivityForResult(intent, mRequestCode);
+        }
     }
 
     /**
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/Settings.java b/core/java/android/provider/Settings.java
index 51dc6ae..e98fa26 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2356,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/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 3796994..fbb13af 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -19,6 +19,7 @@
 
 import android.graphics.Canvas;
 import android.os.SystemClock;
+import android.util.EventLog;
 import android.util.Log;
 
 import javax.microedition.khronos.egl.EGL10;
@@ -406,6 +407,11 @@
                 attachInfo.mDrawingTime = SystemClock.uptimeMillis();
                 attachInfo.mIgnoreDirtyState = true;
                 view.mPrivateFlags |= View.DRAWN;
+                
+                long startTime;
+                if (ViewDebug.DEBUG_PROFILE_DRAWING) {
+                    startTime = SystemClock.elapsedRealtime();
+                }
 
                 checkCurrent();
 
@@ -423,6 +429,10 @@
 
                 onPostDraw();
 
+                if (ViewDebug.DEBUG_PROFILE_DRAWING) {
+                    EventLog.writeEvent(60000, SystemClock.elapsedRealtime() - startTime);
+                }
+
                 attachInfo.mIgnoreDirtyState = false;
 
                 sEgl.eglSwapBuffers(sEglDisplay, mEglSurface);
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 2b92e87..12c49c4 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -1973,8 +1973,12 @@
                 if (hasNoCache) {
                     final int multipliedAlpha = (int) (255 * alpha);
                     if (!child.onSetAlpha(multipliedAlpha)) {
+                        int layerFlags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;
+                        if ((flags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {
+                            layerFlags |= Canvas.CLIP_TO_LAYER_SAVE_FLAG;
+                        }
                         canvas.saveLayerAlpha(sx, sy, sx + cr - cl, sy + cb - ct, multipliedAlpha,
-                                Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
+                                layerFlags);
                     } else {
                         child.mPrivateFlags |= ALPHA_SET;
                     }
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index 0321be0..6917bca 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -1711,6 +1711,9 @@
                 deliverPointerEvent(event);
             } finally {
                 event.recycle();
+                if (msg.arg1 != 0) {
+                    finishInputEvent();
+                }
                 if (LOCAL_LOGV || WATCH_POINTER) Log.i(TAG, "Done dispatching!");
             }
         } break;
@@ -1720,6 +1723,9 @@
                 deliverTrackballEvent(event);
             } finally {
                 event.recycle();
+                if (msg.arg1 != 0) {
+                    finishInputEvent();
+                }
             }
         } break;
         case DISPATCH_APP_VISIBILITY:
@@ -1843,15 +1849,24 @@
         }
     }
     
-    private void finishKeyEvent(KeyEvent event) {
-        if (LOCAL_LOGV) Log.v(TAG, "Telling window manager key is finished");
+    private void startInputEvent(Runnable finishedCallback) {
+        if (mFinishedCallback != null) {
+            Slog.w(TAG, "Received a new input event from the input queue but there is "
+                    + "already an unfinished input event in progress.");
+        }
+
+        mFinishedCallback = finishedCallback;
+    }
+
+    private void finishInputEvent() {
+        if (LOCAL_LOGV) Log.v(TAG, "Telling window manager input event is finished");
 
         if (mFinishedCallback != null) {
             mFinishedCallback.run();
             mFinishedCallback = null;
         } else {
-            Slog.w(TAG, "Attempted to tell the input queue that the current key event "
-                    + "is finished but there is no key event actually in progress.");
+            Slog.w(TAG, "Attempted to tell the input queue that the current input event "
+                    + "is finished but there is no input event actually in progress.");
         }
     }
     
@@ -2310,7 +2325,7 @@
         boolean handled = mView == null || mView.dispatchKeyEventPreIme(event);
         if (handled) {
             if (sendDone) {
-                finishKeyEvent(event);
+                finishInputEvent();
             }
             return;
         }
@@ -2340,7 +2355,7 @@
             if (!handled) {
                 deliverKeyEventToViewHierarchy(event, sendDone);
             } else if (sendDone) {
-                finishKeyEvent(event);
+                finishInputEvent();
             } else {
                 Log.w(TAG, "handleFinishedEvent(seq=" + seq
                         + " handled=" + handled + " ev=" + event
@@ -2413,7 +2428,7 @@
 
         } finally {
             if (sendDone) {
-                finishKeyEvent(event);
+                finishInputEvent();
             }
             // Let the exception fall through -- the looper will catch
             // it and take care of the bad app for us.
@@ -2606,20 +2621,13 @@
     
     private final InputHandler mInputHandler = new InputHandler() {
         public void handleKey(KeyEvent event, Runnable finishedCallback) {
-            if (mFinishedCallback != null) {
-                Slog.w(TAG, "Received a new key event from the input queue but there is "
-                        + "already an unfinished key event in progress.");
-            }
-
-            mFinishedCallback = finishedCallback;
-
+            startInputEvent(finishedCallback);
             dispatchKey(event, true);
         }
 
         public void handleMotion(MotionEvent event, Runnable finishedCallback) {
-            finishedCallback.run();
-            
-            dispatchMotion(event);
+            startInputEvent(finishedCallback);
+            dispatchMotion(event, true);
         }
     };
 
@@ -2651,26 +2659,43 @@
     }
     
     public void dispatchMotion(MotionEvent event) {
+        dispatchMotion(event, false);
+    }
+
+    private void dispatchMotion(MotionEvent event, boolean sendDone) {
         int source = event.getSource();
         if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
-            dispatchPointer(event);
+            dispatchPointer(event, sendDone);
         } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
-            dispatchTrackball(event);
+            dispatchTrackball(event, sendDone);
         } else {
             // TODO
             Log.v(TAG, "Dropping unsupported motion event (unimplemented): " + event);
+            if (sendDone) {
+                finishInputEvent();
+            }
         }
     }
 
     public void dispatchPointer(MotionEvent event) {
+        dispatchPointer(event, false);
+    }
+
+    private void dispatchPointer(MotionEvent event, boolean sendDone) {
         Message msg = obtainMessage(DISPATCH_POINTER);
         msg.obj = event;
+        msg.arg1 = sendDone ? 1 : 0;
         sendMessageAtTime(msg, event.getEventTime());
     }
 
     public void dispatchTrackball(MotionEvent event) {
+        dispatchTrackball(event, false);
+    }
+
+    private void dispatchTrackball(MotionEvent event, boolean sendDone) {
         Message msg = obtainMessage(DISPATCH_TRACKBALL);
         msg.obj = event;
+        msg.arg1 = sendDone ? 1 : 0;
         sendMessageAtTime(msg, event.getEventTime());
     }
     
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/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index 3801948..f7c869b 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -428,8 +428,12 @@
                 a = b;
                 b = tmp;
             }
+            // Clip the end points to be within the content bounds.
+            final int length = content.length();
             if (a < 0) a = 0;
-            if (b > content.length()) b = content.length();
+            if (b < 0) b = 0;
+            if (a > length) a = length;
+            if (b > length) b = length;
 
             ensureDefaultComposingSpans();
             if (mDefaultComposingSpans != null) {
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/voip/java/android/net/sip/SessionDescription.aidl b/core/java/android/view/inputmethod/InputMethodSubtype.aidl
similarity index 74%
rename from voip/java/android/net/sip/SessionDescription.aidl
rename to core/java/android/view/inputmethod/InputMethodSubtype.aidl
index a120d16..ed82b84 100644
--- a/voip/java/android/net/sip/SessionDescription.aidl
+++ b/core/java/android/view/inputmethod/InputMethodSubtype.aidl
@@ -1,11 +1,11 @@
 /*
- * Copyright (C) 2010, The Android Open Source Project
+ * 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
+ *      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,
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.net.sip;
+package android.view.inputmethod;
 
-parcelable SessionDescription;
+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 bb43690..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;
                     }
                 }
             };
@@ -2366,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;
@@ -2533,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/android/webruntime/WebRuntimeActivity.java b/core/java/android/webruntime/WebRuntimeActivity.java
deleted file mode 100644
index ec8c60c..0000000
--- a/core/java/android/webruntime/WebRuntimeActivity.java
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.webruntime;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.content.ComponentName;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.net.Uri;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.KeyEvent;
-import android.view.Menu;
-import android.view.View;
-import android.view.Window;
-import android.webkit.GeolocationPermissions;
-import android.webkit.WebChromeClient;
-import android.webkit.WebView;
-import android.webkit.WebViewClient;
-import android.widget.ImageView;
-import android.widget.Toast;
-
-import com.android.internal.R;
-
-import java.net.MalformedURLException;
-import java.net.URL;
-
-/**
- * The runtime used to display installed web applications.
- * @hide
- */
-public class WebRuntimeActivity extends Activity
-{
-    private final static String LOGTAG = "WebRuntimeActivity";
-
-    private WebView mWebView;
-    private URL mBaseUrl;
-    private ImageView mSplashScreen;
-
-    public static class SensitiveFeatures {
-        // All of the sensitive features
-        private boolean mGeolocation;
-        // On Android, the Browser doesn't prompt for database access, so we don't require an
-        // explicit permission here in the WebRuntimeActivity, and there's no Android system
-        // permission required for it either.
-        //private boolean mDatabase;
-
-        public boolean getGeolocation() {
-            return mGeolocation;
-        }
-        public void setGeolocation(boolean geolocation) {
-            mGeolocation = geolocation;
-        }
-    }
-
-    /** Called when the activity is first created. */
-    @Override
-    public void onCreate(Bundle savedInstanceState)
-    {
-        super.onCreate(savedInstanceState);
-
-        // Can't get meta data using getApplicationInfo() as it doesn't pass GET_META_DATA
-        PackageManager packageManager = getPackageManager();
-        ComponentName componentName = new ComponentName(this, getClass());
-        ActivityInfo activityInfo = null;
-        try {
-            activityInfo = packageManager.getActivityInfo(componentName, PackageManager.GET_META_DATA);
-        } catch (PackageManager.NameNotFoundException e) {
-            Log.d(LOGTAG, "Failed to find component");
-            return;
-        }
-        if (activityInfo == null) {
-            Log.d(LOGTAG, "Failed to get activity info");
-            return;
-        }
-
-        Bundle metaData = activityInfo.metaData;
-        if (metaData == null) {
-            Log.d(LOGTAG, "No meta data");
-            return;
-        }
-
-        String url = metaData.getString("android.webruntime.url");
-        if (url == null) {
-            Log.d(LOGTAG, "No URL");
-            return;
-        }
-
-        try {
-            mBaseUrl = new URL(url);
-        } catch (MalformedURLException e) {
-            Log.d(LOGTAG, "Invalid URL");
-        }
-
-        // All false by default, and reading non-existent bundle properties gives false too.
-        final SensitiveFeatures sensitiveFeatures = new SensitiveFeatures();
-        sensitiveFeatures.setGeolocation(metaData.getBoolean("android.webruntime.SensitiveFeaturesGeolocation"));
-
-        getWindow().requestFeature(Window.FEATURE_NO_TITLE);
-        setContentView(R.layout.web_runtime);
-        mWebView = (WebView) findViewById(R.id.webview);
-        mSplashScreen = (ImageView) findViewById(R.id.splashscreen);
-        mSplashScreen.setImageResource(
-                getResources().getIdentifier("splash_screen", "drawable", getPackageName()));
-        mWebView.getSettings().setJavaScriptEnabled(true);
-        mWebView.setWebViewClient(new WebViewClient() {
-            @Override
-            public boolean shouldOverrideUrlLoading(WebView view, String url) {
-                try {
-                    URL newOrigin = new URL(url);
-                    if (areSameOrigin(mBaseUrl, newOrigin)) {
-                        // If simple same origin test passes, load in the webview.
-                        return false;
-                    }
-                } catch(MalformedURLException e) {
-                    // Don't load anything if this wasn't a proper URL.
-                    return true;
-                }
-
-                // Otherwise this is a URL that is not same origin so pass it to the
-                // Browser to load.
-                startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
-                return true;
-            }
-
-            @Override
-            public void onPageFinished(WebView view, String url) {
-                if (mSplashScreen != null && mSplashScreen.getVisibility() == View.VISIBLE) {
-                    mSplashScreen.setVisibility(View.GONE);
-                    mSplashScreen = null;
-                }
-            }
-        });
-
-        // Use a custom WebChromeClient with geolocation permissions handling.
-        mWebView.setWebChromeClient(new WebChromeClient() {
-            public void onGeolocationPermissionsShowPrompt(
-                        String origin, GeolocationPermissions.Callback callback) {
-                // Allow this origin if it has Geolocation permissions, otherwise deny.
-                boolean allowed = false;
-                if (sensitiveFeatures.getGeolocation()) {
-                    try {
-                        URL originUrl = new URL(origin);
-                        allowed = areSameOrigin(mBaseUrl, originUrl);
-                    } catch(MalformedURLException e) {
-                    }
-                }
-                callback.invoke(origin, allowed, false);
-            }
-        });
-
-        // Set the DB location. Optional. Geolocation works without DBs.
-        mWebView.getSettings().setGeolocationDatabasePath(
-                getDir("geolocation", MODE_PRIVATE).getPath());
-
-        String title = metaData.getString("android.webruntime.title");
-        // We turned off the title bar to go full screen so display the
-        // webapp's title as a toast.
-        if (title != null) {
-            Toast.makeText(this, title, Toast.LENGTH_SHORT).show();
-        }
-
-        // Load the webapp's base URL.
-        mWebView.loadUrl(url);
-    }
-
-    @Override
-    public boolean onKeyDown(int keyCode, KeyEvent event) {
-        if (keyCode == KeyEvent.KEYCODE_BACK && mWebView.canGoBack()) {
-            mWebView.goBack();
-            return true;
-        }
-        return super.onKeyDown(keyCode, event);
-    }
-
-    @Override
-    public boolean onCreateOptionsMenu(Menu menu) {
-        menu.add(0, 0, 0, "Menu item 1");
-        menu.add(0, 1, 0, "Menu item 2");
-        return true;
-    }
-
-    private static boolean areSameOrigin(URL a, URL b) {
-        int aPort = a.getPort() == -1 ? a.getDefaultPort() : a.getPort();
-        int bPort = b.getPort() == -1 ? b.getDefaultPort() : b.getPort();
-        return a.getProtocol().equals(b.getProtocol()) && aPort == bPort && a.getHost().equals(b.getHost());
-    }
-}
diff --git a/core/java/android/widget/Button.java b/core/java/android/widget/Button.java
index 176233e..8d58a6d 100644
--- a/core/java/android/widget/Button.java
+++ b/core/java/android/widget/Button.java
@@ -25,13 +25,13 @@
 
 
 /**
- * <p>
- * <code>Button</code> represents a push-button widget. Push-buttons can be
- * pressed, or clicked, by the user to perform an action. A typical use of a
- * push-button in an activity would be the following:
+ * Represents a push-button widget. Push-buttons can be
+ * pressed, or clicked, by the user to perform an action.
+
+ * <p>A typical use of a push-button in an activity would be the following:
  * </p>
  *
- * <pre class="prettyprint">
+ * <pre>
  * public class MyActivity extends Activity {
  *     protected void onCreate(Bundle icicle) {
  *         super.onCreate(icicle);
@@ -45,16 +45,52 @@
  *             }
  *         });
  *     }
- * }
- * </pre>
+ * }</pre>
  *
- * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-formstuff.html">Form Stuff
- * tutorial</a>.</p>
+ * <p>However, instead of applying an {@link android.view.View.OnClickListener OnClickListener} to
+ * the button in your activity, you can assign a method to your button in the XML layout,
+ * using the {@link android.R.attr#onClick android:onClick} attribute. For example:</p>
+ *
+ * <pre>
+ * &lt;Button
+ *     android:layout_height="wrap_content"
+ *     android:layout_width="wrap_content"
+ *     android:text="@string/self_destruct"
+ *     android:onClick="selfDestruct" /&gt;</pre>
+ *
+ * <p>Now, when a user clicks the button, the Android system calls the activity's {@code
+ * selfDestruct(View)} method. In order for this to work, the method must be public and accept
+ * a {@link android.view.View} as its only parameter. For example:</p>
+ *
+ * <pre>
+ * public void selfDestruct(View view) {
+ *     // Kabloey
+ * }</pre>
+ *
+ * <p>The {@link android.view.View} passed into the method is a reference to the widget
+ * that was clicked.</p>
+ *
+ * <h3>Button style</h3>
+ *
+ * <p>Every Button is styled using the system's default button background, which is often different
+ * from one device to another and from one version of the platform to another. If you're not
+ * satisfied with the default button style and want to customize it to match the design of your
+ * application, then you can replace the button's background image with a <a
+ * href="{@docRoot}guide/topics/resources/drawable-resource.html#StateList">state list drawable</a>.
+ * A state list drawable is a drawable resource defined in XML that changes its image based on
+ * the current state of the button. Once you've defined a state list drawable in XML, you can apply
+ * it to your Button with the {@link android.R.attr#background android:background}
+ * attribute. For more information and an example, see <a
+ * href="{@docRoot}guide/topics/resources/drawable-resource.html#StateList">State List
+ * Drawable</a>.</p>
+ *
+ * <p>Also see the <a href="{@docRoot}resources/tutorials/views/hello-formstuff.html">Form Stuff
+ * tutorial</a> for an example implementation of a button.</p>
  *
  * <p><strong>XML attributes</strong></p>
- * <p> 
- * See {@link android.R.styleable#Button Button Attributes}, 
- * {@link android.R.styleable#TextView TextView Attributes},  
+ * <p>
+ * See {@link android.R.styleable#Button Button Attributes},
+ * {@link android.R.styleable#TextView TextView Attributes},
  * {@link android.R.styleable#View View Attributes}
  * </p>
  */
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 4e61ddf..66149ac 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -67,7 +67,7 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS' 
 
     // Current on-disk Parcel version
-    private static final int VERSION = 51;
+    private static final int VERSION = 52;
 
     // Maximum number of items we will record in the history.
     private static final int MAX_HISTORY_ITEMS = 2000;
@@ -85,7 +85,7 @@
 
     static final int MSG_UPDATE_WAKELOCKS = 1;
     static final int MSG_REPORT_POWER_CHANGE = 2;
-    static final long DELAY_UPDATE_WAKELOCKS = 15*1000;
+    static final long DELAY_UPDATE_WAKELOCKS = 5*1000;
 
     public interface BatteryCallback {
         public void batteryNeedsCpuUpdate();
@@ -1476,6 +1476,13 @@
         }
     }
 
+    public void reportExcessiveCpuLocked(int uid, String proc, long overTime, long usedTime) {
+        Uid u = mUidStats.get(uid);
+        if (u != null) {
+            u.reportExcessiveCpuLocked(proc, overTime, usedTime);
+        }
+    }
+
     int mSensorNesting;
 
     public void noteStartSensorLocked(int uid, int sensor) {
@@ -2977,7 +2984,7 @@
 
             SamplingCounter[] mSpeedBins;
 
-            ArrayList<ExcessiveWake> mExcessiveWake;
+            ArrayList<ExcessivePower> mExcessivePower;
 
             Proc() {
                 mUnpluggables.add(this);
@@ -3005,55 +3012,69 @@
                 }
             }
             
-            public int countExcessiveWakes() {
-                return mExcessiveWake != null ? mExcessiveWake.size() : 0;
+            public int countExcessivePowers() {
+                return mExcessivePower != null ? mExcessivePower.size() : 0;
             }
 
-            public ExcessiveWake getExcessiveWake(int i) {
-                if (mExcessiveWake != null) {
-                    return mExcessiveWake.get(i);
+            public ExcessivePower getExcessivePower(int i) {
+                if (mExcessivePower != null) {
+                    return mExcessivePower.get(i);
                 }
                 return null;
             }
 
             public void addExcessiveWake(long overTime, long usedTime) {
-                if (mExcessiveWake == null) {
-                    mExcessiveWake = new ArrayList<ExcessiveWake>();
+                if (mExcessivePower == null) {
+                    mExcessivePower = new ArrayList<ExcessivePower>();
                 }
-                ExcessiveWake ew = new ExcessiveWake();
+                ExcessivePower ew = new ExcessivePower();
+                ew.type = ExcessivePower.TYPE_WAKE;
                 ew.overTime = overTime;
                 ew.usedTime = usedTime;
-                mExcessiveWake.add(ew);
+                mExcessivePower.add(ew);
             }
 
-            void writeExcessiveWakeToParcelLocked(Parcel out) {
-                if (mExcessiveWake == null) {
+            public void addExcessiveCpu(long overTime, long usedTime) {
+                if (mExcessivePower == null) {
+                    mExcessivePower = new ArrayList<ExcessivePower>();
+                }
+                ExcessivePower ew = new ExcessivePower();
+                ew.type = ExcessivePower.TYPE_CPU;
+                ew.overTime = overTime;
+                ew.usedTime = usedTime;
+                mExcessivePower.add(ew);
+            }
+
+            void writeExcessivePowerToParcelLocked(Parcel out) {
+                if (mExcessivePower == null) {
                     out.writeInt(0);
                     return;
                 }
 
-                final int N = mExcessiveWake.size();
+                final int N = mExcessivePower.size();
                 out.writeInt(N);
                 for (int i=0; i<N; i++) {
-                    ExcessiveWake ew = mExcessiveWake.get(i);
+                    ExcessivePower ew = mExcessivePower.get(i);
+                    out.writeInt(ew.type);
                     out.writeLong(ew.overTime);
                     out.writeLong(ew.usedTime);
                 }
             }
 
-            void readExcessiveWakeFromParcelLocked(Parcel in) {
+            void readExcessivePowerFromParcelLocked(Parcel in) {
                 final int N = in.readInt();
                 if (N == 0) {
-                    mExcessiveWake = null;
+                    mExcessivePower = null;
                     return;
                 }
 
-                mExcessiveWake = new ArrayList<ExcessiveWake>();
+                mExcessivePower = new ArrayList<ExcessivePower>();
                 for (int i=0; i<N; i++) {
-                    ExcessiveWake ew = new ExcessiveWake();
+                    ExcessivePower ew = new ExcessivePower();
+                    ew.type = in.readInt();
                     ew.overTime = in.readLong();
                     ew.usedTime = in.readLong();
-                    mExcessiveWake.add(ew);
+                    mExcessivePower.add(ew);
                 }
             }
 
@@ -3082,7 +3103,7 @@
                     }
                 }
 
-                writeExcessiveWakeToParcelLocked(out);
+                writeExcessivePowerToParcelLocked(out);
             }
 
             void readFromParcelLocked(Parcel in) {
@@ -3112,7 +3133,7 @@
                     }
                 }
 
-                readExcessiveWakeFromParcelLocked(in);
+                readExcessivePowerFromParcelLocked(in);
             }
 
             public BatteryStatsImpl getBatteryStats() {
@@ -3746,6 +3767,13 @@
             }
         }
         
+        public void reportExcessiveCpuLocked(String proc, long overTime, long usedTime) {
+            Proc p = getProcessStatsLocked(proc);
+            if (p != null) {
+                p.addExcessiveCpu(overTime, usedTime);
+            }
+        }
+
         public void noteStartSensor(int sensor) {
             StopwatchTimer t = getSensorTimerLocked(sensor, true);
             if (t != null) {
@@ -4688,7 +4716,7 @@
                         p.mSpeedBins[i].readSummaryFromParcelLocked(in);
                     }
                 }
-                p.readExcessiveWakeFromParcelLocked(in);
+                p.readExcessivePowerFromParcelLocked(in);
             }
 
             NP = in.readInt();
@@ -4887,7 +4915,7 @@
                             out.writeInt(0);
                         }
                     }
-                    ps.writeExcessiveWakeToParcelLocked(out);
+                    ps.writeExcessivePowerToParcelLocked(out);
                 }
             }
 
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/java/com/android/internal/widget/DrawableHolder.java b/core/java/com/android/internal/widget/DrawableHolder.java
new file mode 100644
index 0000000..d53860c
--- /dev/null
+++ b/core/java/com/android/internal/widget/DrawableHolder.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget;
+
+import java.util.ArrayList;
+
+import android.animation.Animator;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.animation.Animator.AnimatorListener;
+import android.graphics.Canvas;
+import android.graphics.drawable.BitmapDrawable;
+import android.util.Log;
+import android.view.animation.DecelerateInterpolator;
+
+/**
+ * This class is a container for a Drawable with multiple animated properties.
+ *
+ */
+public class DrawableHolder implements AnimatorListener {
+    public static final DecelerateInterpolator EASE_OUT_INTERPOLATOR = new DecelerateInterpolator();
+    private static final String TAG = "DrawableHolder";
+    private static final boolean DBG = false;
+    private float mX = 0.0f;
+    private float mY = 0.0f;
+    private float mScaleX = 1.0f;
+    private float mScaleY = 1.0f;
+    private BitmapDrawable mDrawable;
+    private float mAlpha = 1f;
+    private ArrayList<ObjectAnimator<Float>> mAnimators = new ArrayList<ObjectAnimator<Float>>();
+    private ArrayList<ObjectAnimator<Float>> mNeedToStart = new ArrayList<ObjectAnimator<Float>>();
+
+    public DrawableHolder(BitmapDrawable drawable) {
+        this(drawable, 0.0f, 0.0f);
+    }
+
+    public DrawableHolder(BitmapDrawable drawable, float x, float y) {
+        mDrawable = drawable;
+        mX = x;
+        mY = y;
+        mDrawable.getPaint().setAntiAlias(true); // Force AA
+        mDrawable.setBounds(0, 0, mDrawable.getIntrinsicWidth(), mDrawable.getIntrinsicHeight());
+    }
+
+    /**
+     *
+     * Adds an animation that interpolates given property from its current value
+     * to the given value.
+     *
+     * @param duration the duration, in ms.
+     * @param delay the delay to start the animation, in ms.
+     * @param property the property to animate
+     * @param toValue the target value
+     * @param replace if true, replace the current animation with this one.
+     */
+    public ObjectAnimator<Float> addAnimTo(long duration, long delay,
+            String property, float toValue, boolean replace) {
+
+        if (replace) removeAnimationFor(property);
+
+        ObjectAnimator<Float> anim = new ObjectAnimator<Float>(duration, this, property, toValue);
+        anim.setStartDelay(delay);
+        anim.setInterpolator(EASE_OUT_INTERPOLATOR);
+        this.addAnimation(anim, replace);
+        if (DBG) Log.v(TAG, "animationCount = " + mAnimators.size());
+        return anim;
+    }
+
+    /**
+     * Stops all animations for the given property and removes it from the list.
+     *
+     * @param property
+     */
+    public void removeAnimationFor(String property) {
+        ArrayList<ObjectAnimator<Float>> removalList = new ArrayList<ObjectAnimator<Float>>();
+        for (ObjectAnimator<Float> currentAnim : mAnimators) {
+            if (property.equals(currentAnim.getPropertyName())) {
+                currentAnim.cancel();
+                removalList.add(currentAnim);
+            }
+        }
+        if (DBG) Log.v(TAG, "Remove list size: " + removalList.size());
+        mAnimators.removeAll(removalList);
+    }
+
+    /**
+     * Stops all animations and removes them from the list.
+     */
+    public void clearAnimations() {
+        for (ObjectAnimator<Float> currentAnim : mAnimators) {
+            currentAnim.cancel();
+        }
+        mAnimators.clear();
+    }
+
+    /**
+     * Adds the given animation to the list of animations for this object.
+     *
+     * @param anim
+     * @param overwrite
+     * @return
+     */
+    private DrawableHolder addAnimation(ObjectAnimator<Float> anim, boolean overwrite) {
+        if (anim != null)
+            mAnimators.add(anim);
+        mNeedToStart.add(anim);
+        return this;
+    }
+
+    /**
+     * Draw this object to the canvas using the properties defined in this class.
+     *
+     * @param canvas canvas to draw into
+     */
+    public void draw(Canvas canvas) {
+        final float threshold = 1.0f / 256.0f; // contribution less than 1 LSB of RGB byte
+        if (mAlpha <= threshold) // don't bother if it won't show up
+            return;
+        canvas.save(Canvas.MATRIX_SAVE_FLAG);
+        canvas.translate(mX, mY);
+        canvas.scale(mScaleX, mScaleY);
+        canvas.translate(-0.5f*getWidth(), -0.5f*getHeight());
+        mDrawable.setAlpha((int) Math.round(mAlpha * 255f));
+        mDrawable.draw(canvas);
+        canvas.restore();
+    }
+
+    /**
+     * Starts all animations added since the last call to this function.  Used to synchronize
+     * animations.
+     *
+     * @param listener an optional listener to add to the animations. Typically used to know when
+     * to invalidate the surface these are being drawn to.
+     */
+    public void startAnimations(ValueAnimator.AnimatorUpdateListener listener) {
+        for (int i = 0; i < mNeedToStart.size(); i++) {
+            ObjectAnimator<Float> anim = mNeedToStart.get(i);
+            anim.addUpdateListener(listener);
+            anim.addListener(this);
+            anim.start();
+        }
+        mNeedToStart.clear();
+    }
+
+
+    public DrawableHolder setX(float value) {
+        mX = value;
+        return this;
+    }
+
+    public DrawableHolder setY(float value) {
+        mY = value;
+        return this;
+    }
+
+    public DrawableHolder setScaleX(float value) {
+        mScaleX = value;
+        return this;
+    }
+
+    public DrawableHolder setScaleY(float value) {
+        mScaleY = value;
+        return this;
+    }
+
+    public DrawableHolder setAlpha(float alpha) {
+        mAlpha = alpha;
+        return this;
+    }
+
+    public float getX() {
+        return mX;
+    }
+
+    public float getY() {
+        return mY;
+    }
+
+    public float getScaleX() {
+        return mScaleX;
+    }
+
+    public float getScaleY() {
+        return mScaleY;
+    }
+
+    public float getAlpha() {
+        return mAlpha;
+    }
+
+    public BitmapDrawable getDrawable() {
+        return mDrawable;
+    }
+
+    public int getWidth() {
+        return mDrawable.getIntrinsicWidth();
+    }
+
+    public int getHeight() {
+        return mDrawable.getIntrinsicHeight();
+    }
+
+    public void onAnimationCancel(Animator animation) {
+
+    }
+
+    public void onAnimationEnd(Animator animation) {
+        mAnimators.remove(animation);
+    }
+
+    public void onAnimationRepeat(Animator animation) {
+
+    }
+
+    public void onAnimationStart(Animator animation) {
+
+    }
+}
diff --git a/core/java/com/android/internal/widget/WaveView.java b/core/java/com/android/internal/widget/WaveView.java
new file mode 100644
index 0000000..f4ee7ee
--- /dev/null
+++ b/core/java/com/android/internal/widget/WaveView.java
@@ -0,0 +1,573 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget;
+
+import java.util.ArrayList;
+
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.drawable.BitmapDrawable;
+import android.os.Vibrator;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+
+import com.android.internal.R;
+
+/**
+ * A special widget containing a center and outer ring. Moving the center ring to the outer ring
+ * causes an event that can be caught by implementing OnTriggerListener.
+ */
+public class WaveView extends View implements ValueAnimator.AnimatorUpdateListener {
+    private static final String TAG = "WaveView";
+    private static final boolean DBG = false;
+    private static final int WAVE_COUNT = 5; // default wave count
+    private static final long VIBRATE_SHORT = 20;  // msec
+    private static final long VIBRATE_LONG = 20;  // msec
+
+    // Lock state machine states
+    private static final int STATE_RESET_LOCK = 0;
+    private static final int STATE_READY = 1;
+    private static final int STATE_START_ATTEMPT = 2;
+    private static final int STATE_ATTEMPTING = 3;
+    private static final int STATE_UNLOCK_ATTEMPT = 4;
+    private static final int STATE_UNLOCK_SUCCESS = 5;
+
+    // Animation properties.
+    private static final long DURATION = 500; // duration of transitional animations
+    private static final long FINAL_DELAY = 1300; // delay for final animations
+    private static final long SHORT_DELAY = 100; // for starting one animation after another.
+    private static final long WAVE_DURATION = 2000; // amount of time for way to expand/decay
+    private static final long RESET_TIMEOUT = 3000; // elapsed time of inactivity before we reset
+    private static final long DELAY_INCREMENT = 15; // increment per wave while tracking motion
+    private static final long DELAY_INCREMENT2 = 12; // increment per wave while not tracking
+
+    private Vibrator mVibrator;
+    private OnTriggerListener mOnTriggerListener;
+    private ArrayList<DrawableHolder> mDrawables = new ArrayList<DrawableHolder>(3);
+    private ArrayList<DrawableHolder> mLightWaves = new ArrayList<DrawableHolder>(WAVE_COUNT);
+    private boolean mFingerDown = false;
+    private float mRingRadius = 182.0f; // Radius of bitmap ring. Used to snap halo to it
+    private int mSnapRadius = 136; // minimum threshold for drag unlock
+    private int mWaveDelay = 240; // time to delay
+    private int mWaveCount = WAVE_COUNT;  // number of waves
+    private long mWaveTimerDelay = mWaveDelay;
+    private int mCurrentWave = 0;
+    private float mLockCenterX; // center of widget as dictated by widget size
+    private float mLockCenterY;
+    private float mMouseX; // current mouse position as of last touch event
+    private float mMouseY;
+    private DrawableHolder mUnlockRing;
+    private DrawableHolder mUnlockDefault;
+    private DrawableHolder mUnlockHalo;
+    private int mLockState = STATE_RESET_LOCK;
+    private int mGrabbedState = OnTriggerListener.NO_HANDLE;
+
+    public WaveView(Context context) {
+        this(context, null);
+    }
+
+    public WaveView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+
+        // TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.WaveView);
+        // mOrientation = a.getInt(R.styleable.WaveView_orientation, HORIZONTAL);
+        // a.recycle();
+
+        initDrawables();
+    }
+
+    @Override
+    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+        mLockCenterX = 0.5f * w;
+        mLockCenterY = 0.5f * h;
+        super.onSizeChanged(w, h, oldw, oldh);
+    }
+
+    @Override
+    protected int getSuggestedMinimumWidth() {
+        // View should be large enough to contain the unlock ring + halo
+        return mUnlockRing.getWidth() + mUnlockHalo.getWidth();
+    }
+
+    @Override
+    protected int getSuggestedMinimumHeight() {
+        // View should be large enough to contain the unlock ring + halo
+        return mUnlockRing.getHeight() + mUnlockHalo.getHeight();
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
+        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
+        int widthSpecSize =  MeasureSpec.getSize(widthMeasureSpec);
+        int heightSpecSize =  MeasureSpec.getSize(heightMeasureSpec);
+        int width;
+        int height;
+
+        if (widthSpecMode == MeasureSpec.AT_MOST) {
+            width = Math.min(widthSpecSize, getSuggestedMinimumWidth());
+        } else if (widthSpecMode == MeasureSpec.EXACTLY) {
+            width = widthSpecSize;
+        } else {
+            width = getSuggestedMinimumWidth();
+        }
+
+        if (heightSpecMode == MeasureSpec.AT_MOST) {
+            height = Math.min(heightSpecSize, getSuggestedMinimumWidth());
+        } else if (heightSpecMode == MeasureSpec.EXACTLY) {
+            height = heightSpecSize;
+        } else {
+            height = getSuggestedMinimumHeight();
+        }
+
+        setMeasuredDimension(width, height);
+    }
+
+    private void initDrawables() {
+        mUnlockRing = new DrawableHolder(createDrawable(R.drawable.unlock_ring))
+            .setX(mLockCenterX).setY(mLockCenterY).setScaleX(0.1f).setScaleY(0.1f).setAlpha(0.0f);
+        mDrawables.add(mUnlockRing);
+
+        mUnlockDefault = new DrawableHolder(createDrawable(R.drawable.unlock_default))
+            .setX(mLockCenterX).setY(mLockCenterY).setScaleX(0.1f).setScaleY(0.1f).setAlpha(0.0f);
+        mDrawables.add(mUnlockDefault);
+
+        mUnlockHalo = new DrawableHolder(createDrawable(R.drawable.unlock_halo))
+            .setX(mLockCenterX).setY(mLockCenterY).setScaleX(0.1f).setScaleY(0.1f).setAlpha(0.0f);
+        mDrawables.add(mUnlockHalo);
+
+        BitmapDrawable wave = createDrawable(R.drawable.unlock_wave);
+        for (int i = 0; i < mWaveCount; i++) {
+            DrawableHolder holder = new DrawableHolder(wave);
+            mLightWaves.add(holder);
+            holder.setAlpha(0.0f);
+        }
+    }
+
+    private void waveUpdateFrame(float mouseX, float mouseY, boolean fingerDown) {
+        double distX = mouseX - mLockCenterX;
+        double distY = mouseY - mLockCenterY;
+        int dragDistance = (int) Math.ceil(Math.hypot(distX, distY));
+        double touchA = Math.atan2(distX, distY);
+        float ringX = (float) (mLockCenterX + mRingRadius * Math.sin(touchA));
+        float ringY = (float) (mLockCenterY + mRingRadius * Math.cos(touchA));
+
+        switch (mLockState) {
+            case STATE_RESET_LOCK:
+                if (DBG) Log.v(TAG, "State RESET_LOCK");
+                mWaveTimerDelay = mWaveDelay;
+                for (int i = 0; i < mLightWaves.size(); i++) {
+                    //TweenMax.to(mLightWave.get(i), .3, {alpha:0, ease:Quint.easeOut});
+                    DrawableHolder holder = mLightWaves.get(i);
+                    holder.addAnimTo(300, 0, "alpha", 0.0f, false);
+                }
+                for (int i = 0; i < mLightWaves.size(); i++) {
+                    mLightWaves.get(i).startAnimations(this);
+                }
+
+                //TweenMax.to(unlockRing, .5, { x: lockX, y: lockY, scaleX: .1, scaleY: .1,
+                // alpha: 0, overwrite: true, ease:Quint.easeOut   });
+                mUnlockRing.addAnimTo(DURATION, 0, "x", mLockCenterX, true);
+                mUnlockRing.addAnimTo(DURATION, 0, "y", mLockCenterY, true);
+                mUnlockRing.addAnimTo(DURATION, 0, "scaleX", 0.1f, true);
+                mUnlockRing.addAnimTo(DURATION, 0, "scaleY", 0.1f, true);
+                mUnlockRing.addAnimTo(DURATION, 0, "alpha", 0.0f, true);
+
+                //TweenMax.to(unlockDefault, 0, { x: lockX, y: lockY, scaleX: .1, scaleY: .1,
+                // alpha: 0  , overwrite: true                       });
+                mUnlockDefault.removeAnimationFor("x");
+                mUnlockDefault.removeAnimationFor("y");
+                mUnlockDefault.removeAnimationFor("scaleX");
+                mUnlockDefault.removeAnimationFor("scaleY");
+                mUnlockDefault.removeAnimationFor("alpha");
+                mUnlockDefault.setX(mLockCenterX).setY(mLockCenterY).setScaleX(0.1f).setScaleY(0.1f)
+                        .setAlpha(0.0f);
+
+                //TweenMax.to(unlockDefault, .5, { delay: .1, scaleX: 1, scaleY: 1,
+                // alpha: 1, overwrite: true, ease:Quint.easeOut   });
+                mUnlockDefault.addAnimTo(DURATION, SHORT_DELAY, "scaleX", 1.0f, true);
+                mUnlockDefault.addAnimTo(DURATION, SHORT_DELAY, "scaleY", 1.0f, true);
+                mUnlockDefault.addAnimTo(DURATION, SHORT_DELAY, "alpha", 1.0f, true);
+
+                //TweenMax.to(unlockHalo, 0, { x: lockX, y: lockY, scaleX:.1, scaleY: .1,
+                // alpha: 0  , overwrite: true                       });
+                mUnlockHalo.removeAnimationFor("x");
+                mUnlockHalo.removeAnimationFor("y");
+                mUnlockHalo.removeAnimationFor("scaleX");
+                mUnlockHalo.removeAnimationFor("scaleY");
+                mUnlockHalo.removeAnimationFor("alpha");
+                mUnlockHalo.setX(mLockCenterX).setY(mLockCenterY).setScaleX(0.1f).setScaleY(0.1f)
+                        .setAlpha(0.0f);
+
+                //TweenMax.to(unlockHalo, .5, { x: lockX, y: lockY, scaleX: 1, scaleY: 1,
+                // alpha: 1  , overwrite: true, ease:Quint.easeOut   });
+                mUnlockHalo.addAnimTo(DURATION, SHORT_DELAY, "x", mLockCenterX, true);
+                mUnlockHalo.addAnimTo(DURATION, SHORT_DELAY, "y", mLockCenterY, true);
+                mUnlockHalo.addAnimTo(DURATION, SHORT_DELAY, "scaleX", 1.0f, true);
+                mUnlockHalo.addAnimTo(DURATION, SHORT_DELAY, "scaleY", 1.0f, true);
+                mUnlockHalo.addAnimTo(DURATION, SHORT_DELAY, "alpha", 1.0f, true);
+
+                //lockTimer.stop();
+                removeCallbacks(mLockTimerActions);
+
+                mLockState = STATE_READY;
+                break;
+
+            case STATE_READY:
+                if (DBG) Log.v(TAG, "State READY");
+                break;
+
+            case STATE_START_ATTEMPT:
+                if (DBG) Log.v(TAG, "State START_ATTEMPT");
+                //TweenMax.to(unlockDefault, 0, {scaleX: .1, scaleY:.1, alpha: 0,
+                // x:lockX +182, y: lockY  , overwrite: true   });
+                mUnlockDefault.removeAnimationFor("x");
+                mUnlockDefault.removeAnimationFor("y");
+                mUnlockDefault.removeAnimationFor("scaleX");
+                mUnlockDefault.removeAnimationFor("scaleY");
+                mUnlockDefault.removeAnimationFor("alpha");
+                mUnlockDefault.setX(mLockCenterX + 182).setY(mLockCenterY).setScaleX(0.1f)
+                        .setScaleY(0.1f).setAlpha(0.0f);
+
+                //TweenMax.to(unlockDefault, 0.5, { delay: .1   , scaleX: 1, scaleY: 1,
+                // alpha: 1, ease:Quint.easeOut                        });
+                mUnlockDefault.addAnimTo(DURATION, SHORT_DELAY, "scaleX", 1.0f, false);
+                mUnlockDefault.addAnimTo(DURATION, SHORT_DELAY, "scaleY", 1.0f, false);
+                mUnlockDefault.addAnimTo(DURATION, SHORT_DELAY, "alpha", 1.0f, false);
+
+                //TweenMax.to(unlockRing, 0.5, {scaleX: 1, scaleY: 1,
+                // alpha: 1, ease:Quint.easeOut, overwrite: true   });
+                mUnlockRing.addAnimTo(DURATION, 0, "scaleX", 1.0f, true);
+                mUnlockRing.addAnimTo(DURATION, 0, "scaleY", 1.0f, true);
+                mUnlockRing.addAnimTo(DURATION, 0, "alpha", 1.0f, true);
+
+                postDelayed(mAddWaveAction, mWaveTimerDelay);
+
+                mLockState = STATE_ATTEMPTING;
+                break;
+
+            case STATE_ATTEMPTING:
+                if (DBG) Log.v(TAG, "State ATTEMPTING");
+                //TweenMax.to(unlockHalo, 0.4, { x:mouseX, y:mouseY, scaleX:1, scaleY:1,
+                // alpha: 1, ease:Quint.easeOut });
+                if (dragDistance > mSnapRadius) {
+                    if (fingerDown) {
+                        //TweenMax.to(unlockHalo, 0.4, {x:ringX, y:ringY, scaleX:1, scaleY:1,
+                        // alpha: 1 , ease:Quint.easeOut    , overwrite: true });
+                        mUnlockHalo.addAnimTo(0, 0, "x", ringX, true);
+                        mUnlockHalo.addAnimTo(0, 0, "y", ringY, true);
+                        mUnlockHalo.addAnimTo(0, 0, "scaleX", 1.0f, true);
+                        mUnlockHalo.addAnimTo(0, 0, "scaleY", 1.0f, true);
+                        mUnlockHalo.addAnimTo(0, 0, "alpha", 1.0f, true);
+                    }  else {
+                        mLockState = STATE_UNLOCK_ATTEMPT;
+                    }
+                } else {
+                    mUnlockHalo.addAnimTo(0, 0, "x", mouseX, true);
+                    mUnlockHalo.addAnimTo(0, 0, "y", mouseY, true);
+                    mUnlockHalo.addAnimTo(0, 0, "scaleX", 1.0f, true);
+                    mUnlockHalo.addAnimTo(0, 0, "scaleY", 1.0f, true);
+                    mUnlockHalo.addAnimTo(0, 0, "alpha", 1.0f, true);
+                }
+                break;
+
+            case STATE_UNLOCK_ATTEMPT:
+                if (DBG) Log.v(TAG, "State UNLOCK_ATTEMPT");
+                if (dragDistance > mSnapRadius) {
+                    for (int n = 0; n < mLightWaves.size(); n++) {
+                        //TweenMax.to(this["lightWave"+n], .5,{alpha:0, delay: (6+n-currentWave)*.1,
+                        // x:ringX, y:ringY, scaleX: .1, scaleY: .1, ease:Quint.easeOut});
+                        DrawableHolder wave = mLightWaves.get(n);
+                        long delay = 1000L*(6 + n - mCurrentWave)/10L;
+                        wave.addAnimTo(DURATION, delay, "x", ringX, true);
+                        wave.addAnimTo(DURATION, delay, "y", ringY, true);
+                        wave.addAnimTo(DURATION, delay, "scaleX", 0.1f, true);
+                        wave.addAnimTo(DURATION, delay, "scaleY", 0.1f, true);
+                        wave.addAnimTo(DURATION, delay, "alpha", 0.0f, true);
+                    }
+                    for (int i = 0; i < mLightWaves.size(); i++) {
+                        mLightWaves.get(i).startAnimations(this);
+                    }
+
+                    //TweenMax.to(unlockRing, .5, {x:ringX, y: ringY, scaleX: .1, scaleY: .1,
+                    // alpha: 0, ease: Quint.easeOut   });
+                    mUnlockRing.addAnimTo(DURATION, 0, "x", ringX, false);
+                    mUnlockRing.addAnimTo(DURATION, 0, "y", ringY, false);
+                    mUnlockRing.addAnimTo(DURATION, 0, "scaleX", 0.1f, false);
+                    mUnlockRing.addAnimTo(DURATION, 0, "scaleY", 0.1f, false);
+                    mUnlockRing.addAnimTo(DURATION, 0, "alpha", 0.0f, false);
+
+                    //TweenMax.to(unlockRing, .5, { delay: 1.3, alpha: 0  , ease: Quint.easeOut });
+                    mUnlockRing.addAnimTo(DURATION, FINAL_DELAY, "alpha", 0.0f, false);
+
+                    //TweenMax.to(unlockDefault, 0, { x:ringX, y: ringY, scaleX: .1, scaleY: .1,
+                    // alpha: 0  , overwrite: true });
+                    mUnlockDefault.removeAnimationFor("x");
+                    mUnlockDefault.removeAnimationFor("y");
+                    mUnlockDefault.removeAnimationFor("scaleX");
+                    mUnlockDefault.removeAnimationFor("scaleY");
+                    mUnlockDefault.removeAnimationFor("alpha");
+                    mUnlockDefault.setX(ringX).setY(ringY).setScaleX(0.1f).setScaleY(0.1f)
+                            .setAlpha(0.0f);
+
+                    //TweenMax.to(unlockDefault, .5, { x:ringX, y: ringY, scaleX: 1, scaleY: 1,
+                    // alpha: 1  , ease: Quint.easeOut  , overwrite: true });
+                    mUnlockDefault.addAnimTo(DURATION, 0, "x", ringX, true);
+                    mUnlockDefault.addAnimTo(DURATION, 0, "y", ringY, true);
+                    mUnlockDefault.addAnimTo(DURATION, 0, "scaleX", 1.0f, true);
+                    mUnlockDefault.addAnimTo(DURATION, 0, "scaleY", 1.0f, true);
+                    mUnlockDefault.addAnimTo(DURATION, 0, "alpha", 1.0f, true);
+
+                    //TweenMax.to(unlockDefault, .5, { delay: 1.3, scaleX: 3, scaleY: 3,
+                    // alpha: 1, ease: Quint.easeOut });
+                    mUnlockDefault.addAnimTo(DURATION, FINAL_DELAY, "scaleX", 3.0f, false);
+                    mUnlockDefault.addAnimTo(DURATION, FINAL_DELAY, "scaleY", 3.0f, false);
+                    mUnlockDefault.addAnimTo(DURATION, FINAL_DELAY, "alpha", 1.0f, false);
+
+                    //TweenMax.to(unlockHalo, .5, { x:ringX, y: ringY , ease: Back.easeOut    });
+                    mUnlockHalo.addAnimTo(DURATION, 0, "x", ringX, false);
+                    mUnlockHalo.addAnimTo(DURATION, 0, "y", ringY, false);
+
+                    //TweenMax.to(unlockHalo, .5, { delay: 1.3, scaleX: 3, scaleY: 3,
+                    // alpha: 1, ease: Quint.easeOut   });
+                    mUnlockHalo.addAnimTo(DURATION, FINAL_DELAY, "scaleX", 3.0f, false);
+                    mUnlockHalo.addAnimTo(DURATION, FINAL_DELAY, "scaleY", 3.0f, false);
+                    mUnlockHalo.addAnimTo(DURATION, FINAL_DELAY, "alpha", 1.0f, false);
+
+                    removeCallbacks(mLockTimerActions);
+
+                    postDelayed(mLockTimerActions, RESET_TIMEOUT);
+
+                    dispatchTriggerEvent(OnTriggerListener.CENTER_HANDLE);
+                    mLockState = STATE_UNLOCK_SUCCESS;
+                } else {
+                    mLockState = STATE_RESET_LOCK;
+                }
+                break;
+
+            case STATE_UNLOCK_SUCCESS:
+                if (DBG) Log.v(TAG, "State UNLOCK_SUCCESS");
+                removeCallbacks(mAddWaveAction);
+                break;
+
+            default:
+                if (DBG) Log.v(TAG, "Unknown state " + mLockState);
+                break;
+        }
+        mUnlockDefault.startAnimations(this);
+        mUnlockHalo.startAnimations(this);
+        mUnlockRing.startAnimations(this);
+    }
+
+    BitmapDrawable createDrawable(int resId) {
+        Resources res = getResources();
+        Bitmap bitmap = BitmapFactory.decodeResource(res, resId);
+        return new BitmapDrawable(res, bitmap);
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        waveUpdateFrame(mMouseX, mMouseY, mFingerDown);
+        for (int i = 0; i < mDrawables.size(); ++i) {
+            mDrawables.get(i).draw(canvas);
+        }
+        for (int i = 0; i < mLightWaves.size(); ++i) {
+            mLightWaves.get(i).draw(canvas);
+        }
+    }
+
+    private final Runnable mLockTimerActions = new Runnable() {
+        public void run() {
+            if (DBG) Log.v(TAG, "LockTimerActions");
+            // reset lock after inactivity
+            if (mLockState == STATE_ATTEMPTING) {
+                mLockState = STATE_RESET_LOCK;
+            }
+            // for prototype, reset after successful unlock
+            if (mLockState == STATE_UNLOCK_SUCCESS) {
+                mLockState = STATE_RESET_LOCK;
+            }
+            invalidate();
+        }
+    };
+
+    private final Runnable mAddWaveAction = new Runnable() {
+        public void run() {
+            double distX = mMouseX - mLockCenterX;
+            double distY = mMouseY - mLockCenterY;
+            int dragDistance = (int) Math.ceil(Math.hypot(distX, distY));
+            if (mLockState == STATE_ATTEMPTING && dragDistance < mSnapRadius
+                    && mWaveTimerDelay >= mWaveDelay) {
+                mWaveTimerDelay = Math.min(WAVE_DURATION, mWaveTimerDelay + DELAY_INCREMENT);
+
+                DrawableHolder wave = mLightWaves.get(mCurrentWave);
+                wave.setAlpha(0.0f);
+                wave.setScaleX(0.2f);
+                wave.setScaleY(0.2f);
+                wave.setX(mMouseX);
+                wave.setY(mMouseY);
+
+                //TweenMax.to(this["lightWave"+currentWave], 2, { x:lockX , y:lockY, alpha: 1.5,
+                // scaleX: 1, scaleY:1, ease:Cubic.easeOut});
+                wave.addAnimTo(WAVE_DURATION, 0, "x", mLockCenterX, true);
+                wave.addAnimTo(WAVE_DURATION, 0, "y", mLockCenterY, true);
+                wave.addAnimTo(WAVE_DURATION*2/3, 0, "alpha", 1.0f, true);
+                wave.addAnimTo(WAVE_DURATION, 0, "scaleX", 1.0f, true);
+                wave.addAnimTo(WAVE_DURATION, 0, "scaleY", 1.0f, true);
+
+                //TweenMax.to(this["lightWave"+currentWave], 1, { delay: 1.3
+                // , alpha: 0  , ease:Quint.easeOut});
+                wave.addAnimTo(1000, FINAL_DELAY, "alpha", 0.0f, false);
+                wave.startAnimations(WaveView.this);
+
+                mCurrentWave = (mCurrentWave+1) % mWaveCount;
+                if (DBG) Log.v(TAG, "WaveTimerDelay: start new wave in " + mWaveTimerDelay);
+                postDelayed(mAddWaveAction, mWaveTimerDelay);
+            } else {
+                mWaveTimerDelay += DELAY_INCREMENT2;
+            }
+        }
+    };
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        final int action = event.getAction();
+        mMouseX = event.getX();
+        mMouseY = event.getY();
+        boolean handled = false;
+        switch (action) {
+            case MotionEvent.ACTION_DOWN:
+                removeCallbacks(mLockTimerActions);
+                mFingerDown = true;
+                setGrabbedState(OnTriggerListener.CENTER_HANDLE);
+                {
+                    float x = mMouseX - mUnlockHalo.getX();
+                    float y = mMouseY - mUnlockHalo.getY();
+                    float dist = (float) Math.hypot(x, y);
+                    if (dist < mUnlockHalo.getWidth()*0.5f) {
+                        if (mLockState == STATE_READY) {
+                            mLockState = STATE_START_ATTEMPT;
+                        }
+                    }
+                }
+                handled = true;
+                break;
+
+            case MotionEvent.ACTION_MOVE:
+                handled = true;
+                break;
+
+            case MotionEvent.ACTION_UP:
+                mFingerDown = false;
+                postDelayed(mLockTimerActions, RESET_TIMEOUT);
+                setGrabbedState(OnTriggerListener.NO_HANDLE);
+                handled = true;
+                break;
+
+            case MotionEvent.ACTION_CANCEL:
+                mFingerDown = false;
+                handled = true;
+                break;
+        }
+        invalidate();
+        return handled ? true : super.onTouchEvent(event);
+    }
+
+    /**
+     * Triggers haptic feedback.
+     */
+    private synchronized void vibrate(long duration) {
+        if (mVibrator == null) {
+            mVibrator = (android.os.Vibrator)
+                    getContext().getSystemService(Context.VIBRATOR_SERVICE);
+        }
+        mVibrator.vibrate(duration);
+    }
+
+    /**
+     * Registers a callback to be invoked when the user triggers an event.
+     *
+     * @param listener the OnDialTriggerListener to attach to this view
+     */
+    public void setOnTriggerListener(OnTriggerListener listener) {
+        mOnTriggerListener = listener;
+    }
+
+    /**
+     * Dispatches a trigger event to listener. Ignored if a listener is not set.
+     * @param whichHandle the handle that triggered the event.
+     */
+    private void dispatchTriggerEvent(int whichHandle) {
+        vibrate(VIBRATE_LONG);
+        if (mOnTriggerListener != null) {
+            mOnTriggerListener.onTrigger(this, whichHandle);
+        }
+    }
+
+    /**
+     * Sets the current grabbed state, and dispatches a grabbed state change
+     * event to our listener.
+     */
+    private void setGrabbedState(int newState) {
+        if (newState != mGrabbedState) {
+            mGrabbedState = newState;
+            if (mOnTriggerListener != null) {
+                mOnTriggerListener.onGrabbedStateChange(this, mGrabbedState);
+            }
+        }
+    }
+
+    public interface OnTriggerListener {
+        /**
+         * Sent when the user releases the handle.
+         */
+        public static final int NO_HANDLE = 0;
+
+        /**
+         * Sent when the user grabs the center handle
+         */
+        public static final int CENTER_HANDLE = 10;
+
+        /**
+         * Called when the user drags the center ring beyond a threshold.
+         */
+        void onTrigger(View v, int whichHandle);
+
+        /**
+         * Called when the "grabbed state" changes (i.e. when the user either grabs or releases
+         * one of the handles.)
+         *
+         * @param v the view that was triggered
+         * @param grabbedState the new state: {@link #NO_HANDLE}, {@link #CENTER_HANDLE},
+         */
+        void onGrabbedStateChange(View v, int grabbedState);
+    }
+
+    public void onAnimationUpdate(ValueAnimator animation) {
+        invalidate();
+    }
+
+    public void reset() {
+        mLockState = STATE_RESET_LOCK;
+    }
+}
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/jni/android_net_wifi_Wifi.cpp b/core/jni/android_net_wifi_Wifi.cpp
index c5ccc43..defd0a0 100644
--- a/core/jni/android_net_wifi_Wifi.cpp
+++ b/core/jni/android_net_wifi_Wifi.cpp
@@ -30,6 +30,8 @@
 
 static jboolean sScanModeActive = false;
 
+//TODO: check general errors in addition to overflow on snprintf
+
 /*
  * The following remembers the jfieldID's of the fields
  * of the DhcpInfo Java object, so that we don't have
@@ -155,6 +157,36 @@
     return doIntCommand("ADD_NETWORK");
 }
 
+static jboolean android_net_wifi_wpsPbcCommand(JNIEnv* env, jobject clazz, jstring bssid)
+{
+    char cmdstr[50];
+    jboolean isCopy;
+
+    const char *bssidStr = env->GetStringUTFChars(bssid, &isCopy);
+    int numWritten = snprintf(cmdstr, sizeof(cmdstr), "WPS_PBC %s", bssidStr);
+    env->ReleaseStringUTFChars(bssid, bssidStr);
+
+    if ((numWritten == -1) || (numWritten >= sizeof(cmdstr))) {
+        return false;
+    }
+    return doBooleanCommand(cmdstr, "OK");
+}
+
+static jboolean android_net_wifi_wpsPinCommand(JNIEnv* env, jobject clazz, jstring bssid, int apPin)
+{
+    char cmdstr[50];
+    jboolean isCopy;
+
+    const char *bssidStr = env->GetStringUTFChars(bssid, &isCopy);
+    int numWritten = snprintf(cmdstr, sizeof(cmdstr), "WPS_REG %s %d", bssidStr, apPin);
+    env->ReleaseStringUTFChars(bssid, bssidStr);
+
+    if ((numWritten == -1) || (numWritten >= (int)sizeof(cmdstr))) {
+        return false;
+    }
+    return doBooleanCommand(cmdstr, "OK");
+}
+
 static jboolean android_net_wifi_setNetworkVariableCommand(JNIEnv* env,
                                                            jobject clazz,
                                                            jint netId,
@@ -603,7 +635,8 @@
     { "setScanResultHandlingCommand", "(I)Z", (void*) android_net_wifi_setScanResultHandlingCommand },
     { "addToBlacklistCommand", "(Ljava/lang/String;)Z", (void*) android_net_wifi_addToBlacklistCommand },
     { "clearBlacklistCommand", "()Z", (void*) android_net_wifi_clearBlacklistCommand },
-
+    { "startWpsPbcCommand", "(Ljava/lang/String;)Z", (void*) android_net_wifi_wpsPbcCommand },
+    { "startWpsPinCommand", "(Ljava/lang/String;I)Z", (void*) android_net_wifi_wpsPinCommand },
     { "doDhcpRequest", "(Landroid/net/DhcpInfo;)Z", (void*) android_net_wifi_doDhcpRequest },
     { "getDhcpError", "()Ljava/lang/String;", (void*) android_net_wifi_getDhcpError },
 };
diff --git a/core/res/res/drawable-xlarge/unlock_default.png b/core/res/res/drawable-xlarge/unlock_default.png
new file mode 100644
index 0000000..0a441c0
--- /dev/null
+++ b/core/res/res/drawable-xlarge/unlock_default.png
Binary files differ
diff --git a/core/res/res/drawable-xlarge/unlock_halo.png b/core/res/res/drawable-xlarge/unlock_halo.png
new file mode 100644
index 0000000..09b0526
--- /dev/null
+++ b/core/res/res/drawable-xlarge/unlock_halo.png
Binary files differ
diff --git a/core/res/res/drawable-xlarge/unlock_ring.png b/core/res/res/drawable-xlarge/unlock_ring.png
new file mode 100644
index 0000000..1ac6d54
--- /dev/null
+++ b/core/res/res/drawable-xlarge/unlock_ring.png
Binary files differ
diff --git a/core/res/res/drawable-xlarge/unlock_wave.png b/core/res/res/drawable-xlarge/unlock_wave.png
new file mode 100644
index 0000000..21bfa24
--- /dev/null
+++ b/core/res/res/drawable-xlarge/unlock_wave.png
Binary files differ
diff --git a/core/res/res/layout-xlarge/keyguard_screen_tab_unlock.xml b/core/res/res/layout-xlarge/keyguard_screen_tab_unlock.xml
index 4761800..b3645aa 100644
--- a/core/res/res/layout-xlarge/keyguard_screen_tab_unlock.xml
+++ b/core/res/res/layout-xlarge/keyguard_screen_tab_unlock.xml
@@ -57,13 +57,10 @@
             android:drawablePadding="4dip"
             />
 
-        <com.android.internal.widget.SlidingTab
-            android:id="@+id/tab_selector"
-            android:orientation="horizontal"
-            android:layout_width="match_parent"
+        <com.android.internal.widget.WaveView
+            android:id="@+id/wave_view"
+            android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_alignParentBottom="true"
-            android:layout_marginBottom="80dip"
             />
 
         <!-- "emergency calls only" shown when sim is missing or PUKd -->
diff --git a/core/res/res/layout-xlarge/keyguard_screen_tab_unlock_land.xml b/core/res/res/layout-xlarge/keyguard_screen_tab_unlock_land.xml
index bb398f6..6c99ccac 100644
--- a/core/res/res/layout-xlarge/keyguard_screen_tab_unlock_land.xml
+++ b/core/res/res/layout-xlarge/keyguard_screen_tab_unlock_land.xml
@@ -58,15 +58,14 @@
                 android:drawablePadding="4dip"
                 />
 
-        <com.android.internal.widget.SlidingTab
-            android:id="@+id/tab_selector"
-            android:orientation="vertical"
+        <com.android.internal.widget.WaveView
+            android:id="@+id/wave_view"
             android:layout_width="wrap_content"
             android:layout_height="match_parent"
             android:layout_marginRight="0dip"
             android:layout_weight="1.0"
             />
-            
+
         <!-- "emergency calls only" shown when sim is missing or PUKd -->
         <TextView
             android:id="@+id/emergencyCallText"
diff --git a/core/res/res/values-rm/donottranslate-cldr.xml b/core/res/res/values-rm/donottranslate-cldr.xml
index 4b9f9db..ccdb17c 100644
--- a/core/res/res/values-rm/donottranslate-cldr.xml
+++ b/core/res/res/values-rm/donottranslate-cldr.xml
@@ -53,14 +53,6 @@
     <string name="month_shortest_november">N</string>
     <string name="month_shortest_december">D</string>
 
-    <string name="sunday">dumengia</string>
-    <string name="monday">glindesdi</string>
-    <string name="tuesday">mardi</string>
-    <string name="wednesday">mesemna</string>
-    <string name="thursday">gievgia</string>
-    <string name="friday">venderdi</string>
-    <string name="saturday">sonda</string>
-
     <string name="day_of_week_long_sunday">dumengia</string>
     <string name="day_of_week_long_monday">glindesdi</string>
     <string name="day_of_week_long_tuesday">mardi</string>
@@ -85,14 +77,6 @@
     <string name="day_of_week_short_friday">ve</string>
     <string name="day_of_week_short_saturday">so</string>
 
-    <string name="day_of_week_shorter_sunday">D</string>
-    <string name="day_of_week_shorter_monday">G</string>
-    <string name="day_of_week_shorter_tuesday">M</string>
-    <string name="day_of_week_shorter_wednesday">M</string>
-    <string name="day_of_week_shorter_thursday">G</string>
-    <string name="day_of_week_shorter_friday">V</string>
-    <string name="day_of_week_shorter_saturday">S</string>
-
     <string name="day_of_week_shortest_sunday">D</string>
     <string name="day_of_week_shortest_monday">G</string>
     <string name="day_of_week_shortest_tuesday">M</string>
@@ -114,12 +98,7 @@
     <string name="numeric_date_format">dd.MM.yyyy</string>
     <string name="numeric_date_template">"%s.%s.%s"</string>
     <string name="month_day_year">%-e. %B %Y</string>
-    <string name="full_date_month_first">d. MMMM y</string>
-    <string name="full_date_day_first">d. MMMM y</string>
-    <string name="medium_date_month_first">d. MMM y</string>
-    <string name="medium_date_day_first">d. MMM y</string>
     <string name="time_of_day">%H:%M:%S</string>
-    <string name="status_bar_time_format">HH:mm</string>
     <string name="date_and_time">%H:%M:%S %d.%m.%Y</string>
     <string name="date_time">%1$s, %2$s</string>
     <string name="time_date">%3$s, %1$s</string>
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/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
index 975a4c2..1289a9e 100755
--- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
+++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
@@ -167,8 +167,7 @@
         return ipm;
     }
 
-    public boolean invokeInstallPackage(Uri packageURI, int flags,
-            GenericReceiver receiver) throws Exception {
+    public boolean invokeInstallPackage(Uri packageURI, int flags, GenericReceiver receiver) {
         PackageInstallObserver observer = new PackageInstallObserver();
         final boolean received = false;
         mContext.registerReceiver(receiver, receiver.filter);
@@ -180,11 +179,15 @@
                     getPm().installPackage(packageURI, observer, flags, null);
                     long waitTime = 0;
                     while((!observer.isDone()) && (waitTime < MAX_WAIT_TIME) ) {
-                        observer.wait(WAIT_TIME_INCR);
-                        waitTime += WAIT_TIME_INCR;
+                        try {
+                            observer.wait(WAIT_TIME_INCR);
+                            waitTime += WAIT_TIME_INCR;
+                        } catch (InterruptedException e) {
+                            Log.i(TAG, "Interrupted during sleep", e);
+                        }
                     }
                     if(!observer.isDone()) {
-                        throw new Exception("Timed out waiting for packageInstalled callback");
+                        fail("Timed out waiting for packageInstalled callback");
                     }
                     if (observer.returnCode != PackageManager.INSTALL_SUCCEEDED) {
                         Log.i(TAG, "Failed to install with error code = " + observer.returnCode);
@@ -193,11 +196,15 @@
                     // Verify we received the broadcast
                     waitTime = 0;
                     while((!receiver.isDone()) && (waitTime < MAX_WAIT_TIME) ) {
-                        receiver.wait(WAIT_TIME_INCR);
-                        waitTime += WAIT_TIME_INCR;
+                        try {
+                            receiver.wait(WAIT_TIME_INCR);
+                            waitTime += WAIT_TIME_INCR;
+                        } catch (InterruptedException e) {
+                            Log.i(TAG, "Interrupted during sleep", e);
+                        }
                     }
                     if(!receiver.isDone()) {
-                        throw new Exception("Timed out waiting for PACKAGE_ADDED notification");
+                        fail("Timed out waiting for PACKAGE_ADDED notification");
                     }
                     return receiver.received;
                 }
@@ -207,7 +214,7 @@
         }
     }
 
-    public void invokeInstallPackageFail(Uri packageURI, int flags, int result) throws Exception {
+    public void invokeInstallPackageFail(Uri packageURI, int flags, int result) {
         PackageInstallObserver observer = new PackageInstallObserver();
         try {
             // Wait on observer
@@ -215,11 +222,15 @@
                 getPm().installPackage(packageURI, observer, flags, null);
                 long waitTime = 0;
                 while((!observer.isDone()) && (waitTime < MAX_WAIT_TIME) ) {
-                    observer.wait(WAIT_TIME_INCR);
-                    waitTime += WAIT_TIME_INCR;
+                    try {
+                        observer.wait(WAIT_TIME_INCR);
+                        waitTime += WAIT_TIME_INCR;
+                    } catch (InterruptedException e) {
+                        Log.i(TAG, "Interrupted during sleep", e);
+                    }
                 }
                 if(!observer.isDone()) {
-                    throw new Exception("Timed out waiting for packageInstalled callback");
+                    fail("Timed out waiting for packageInstalled callback");
                 }
                 assertEquals(observer.returnCode, result);
             }
@@ -265,7 +276,7 @@
                 Environment.getExternalStorageDirectory().getPath());
         sdSize = (long)sdStats.getAvailableBlocks() *
                 (long)sdStats.getBlockSize();
-        // TODO check for thesholds here
+        // TODO check for thresholds here
         return pkgLen <= sdSize;
 
     }
@@ -368,12 +379,21 @@
                     assertFalse((info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0);
                     assertTrue(info.nativeLibraryDir.startsWith(dataDir.getPath()));
                 } else if (rLoc == INSTALL_LOC_SD){
-                    assertTrue((info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0);
-                    assertTrue(srcPath.startsWith(SECURE_CONTAINERS_PREFIX));
-                    assertTrue(publicSrcPath.startsWith(SECURE_CONTAINERS_PREFIX));
-                    assertTrue(info.nativeLibraryDir.startsWith(SECURE_CONTAINERS_PREFIX));
+                    assertTrue("Application flags (" + info.flags
+                            + ") should contain FLAG_EXTERNAL_STORAGE",
+                            (info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0);
+                    assertTrue("The APK path (" + srcPath + ") should start with "
+                            + SECURE_CONTAINERS_PREFIX, srcPath
+                            .startsWith(SECURE_CONTAINERS_PREFIX));
+                    assertTrue("The public APK path (" + publicSrcPath + ") should start with "
+                            + SECURE_CONTAINERS_PREFIX, publicSrcPath
+                            .startsWith(SECURE_CONTAINERS_PREFIX));
+                    assertTrue("The native library path (" + info.nativeLibraryDir
+                            + ") should start with " + SECURE_CONTAINERS_PREFIX,
+                            info.nativeLibraryDir.startsWith(SECURE_CONTAINERS_PREFIX));
                 } else {
                     // TODO handle error. Install should have failed.
+                    fail("Install should have failed");
                 }
             }
         } catch (NameNotFoundException e) {
@@ -544,8 +564,6 @@
                 // Verify installed information
                 assertInstall(pkg, flags, expInstallLocation);
             }
-        } catch (Exception e) {
-            failStr("Failed with exception : " + e);
         } finally {
             if (cleanUp) {
                 cleanUpInstall(ip);
diff --git a/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/DownloadManagerTestApp.java b/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/DownloadManagerTestApp.java
index 0293ded..f21e7ac 100644
--- a/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/DownloadManagerTestApp.java
+++ b/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/DownloadManagerTestApp.java
@@ -241,7 +241,7 @@
 
         Uri remoteUri = getExternalFileUri(filename);
         Request request = new Request(remoteUri);
-        request.setMediaType(getMimeMapping(DownloadFileType.APK));
+        request.setMimeType(getMimeMapping(DownloadFileType.APK));
 
         dlRequest = mDownloadManager.enqueue(request);
 
diff --git a/data/fonts/Ahem.ttf b/data/fonts/Ahem.ttf
deleted file mode 100644
index 17e6c60..0000000
--- a/data/fonts/Ahem.ttf
+++ /dev/null
Binary files differ
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index 6bcaaaf..1fd7bba 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -44,5 +44,5 @@
         <name>monaco</name>
     </font>
     <fallback ttf="DroidSansFallback" />
-    <fallback ttf="DroidSansJapanese" />
-</fonts>
\ No newline at end of file
+    <fallback ttf="MTLmr3m" />
+</fonts>
diff --git a/docs/html/guide/topics/resources/drawable-resource.jd b/docs/html/guide/topics/resources/drawable-resource.jd
index 035ddb9..9ebed56 100644
--- a/docs/html/guide/topics/resources/drawable-resource.jd
+++ b/docs/html/guide/topics/resources/drawable-resource.jd
@@ -434,7 +434,7 @@
 <h2 id="LayerList">Layer List</h2>
 
 <p>A {@link android.graphics.drawable.LayerDrawable} is a drawable object
-that manages an array of other drawables. Each drawable in the list is drawn in the order of the 
+that manages an array of other drawables. Each drawable in the list is drawn in the order of the
 list&mdash;the last drawable in the list is drawn on top.</p>
 
 <p>Each drawable is represented by an {@code &lt;item&gt;} element inside a single {@code
@@ -641,7 +641,7 @@
         android:state_checkable=["true" | "false"]
         android:state_checked=["true" | "false"]
         android:state_enabled=["true" | "false"]
-        android:state_window_focused=["true" | "false"] /> 
+        android:state_window_focused=["true" | "false"] />
 &lt;/selector>
 </pre>
 </dd>
@@ -729,12 +729,12 @@
 &lt;/selector>
 </pre>
 
-<p>This layout XML applies the drawable to a View:</p>
+<p>This layout XML applies the state list drawable to a Button:</p>
 <pre>
-&lt;ImageView
+&lt;Button
     android:layout_height="wrap_content"
     android:layout_width="wrap_content"
-    android:src="@drawable/button" />
+    android:background="@drawable/button" />
 </pre>
 </dd> <!-- end example -->
 
@@ -1670,13 +1670,13 @@
 &lt;?xml version="1.0" encoding="utf-8"?>
 &lt;shape xmlns:android="http://schemas.android.com/apk/res/android"
     android:shape="rectangle">
-    &lt;gradient 
-        android:startColor="#FFFF0000" 
+    &lt;gradient
+        android:startColor="#FFFF0000"
         android:endColor="#80FF00FF"
         android:angle="45"/>
-    &lt;padding android:left="7dp" 
+    &lt;padding android:left="7dp"
         android:top="7dp"
-        android:right="7dp" 
+        android:right="7dp"
         android:bottom="7dp" />
     &lt;corners android:radius="8dp" />
 &lt;/shape>
diff --git a/docs/html/guide/tutorials/notepad/notepad-ex2.jd b/docs/html/guide/tutorials/notepad/notepad-ex2.jd
index a945a62..854731f 100644
--- a/docs/html/guide/tutorials/notepad/notepad-ex2.jd
+++ b/docs/html/guide/tutorials/notepad/notepad-ex2.jd
@@ -87,8 +87,8 @@
     menu callback used for the options menu. Here, we add just one line, which will add a menu item
     to delete a note. Call <code>menu.add()</code> like so:
       <pre>
-public boolean onCreateContextMenu(Menu menu, View v
-        ContextMenuInfo menuInfo) {
+public void onCreateContextMenu(Menu menu, View v,
+        ContextMenu.ContextMenuInfo menuInfo) {
     super.onCreateContextMenu(menu, v, menuInfo);
     menu.add(0, DELETE_ID, 0, R.string.menu_delete);
 }</pre>
diff --git a/docs/html/resources/tutorials/notepad/notepad-ex2.jd b/docs/html/resources/tutorials/notepad/notepad-ex2.jd
index 289b5fe..499b796 100644
--- a/docs/html/resources/tutorials/notepad/notepad-ex2.jd
+++ b/docs/html/resources/tutorials/notepad/notepad-ex2.jd
@@ -87,8 +87,8 @@
     menu callback used for the options menu. Here, we add just one line, which will add a menu item
     to delete a note. Call <code>menu.add()</code> like so:
       <pre>
-public boolean onCreateContextMenu(Menu menu, View v
-        ContextMenuInfo menuInfo) {
+public void onCreateContextMenu(Menu menu, View v,
+        ContextMenu.ContextMenuInfo menuInfo) {
     super.onCreateContextMenu(menu, v, menuInfo);
     menu.add(0, DELETE_ID, 0, R.string.menu_delete);
 }</pre>
diff --git a/docs/html/sdk/adt_download.jd b/docs/html/sdk/adt_download.jd
index 5e642d7..3da576a 100644
--- a/docs/html/sdk/adt_download.jd
+++ b/docs/html/sdk/adt_download.jd
@@ -22,10 +22,17 @@
     <th>Notes</th>
   </tr>
   <tr>
+     <td>0.9.9</td>
+     <td><a href="http://dl-ssl.google.com/android/ADT-0.9.9.zip">ADT-0.9.9.zip</a></td>
+     <td><nobr>8301681 bytes</nobr></td>
+     <td>7deff0c9b25940a74cea7a0815a3bc36</td>
+     <td>Requires SDK Tools, Revision 7 <em><nobr>September 2010</nobr></em></td>
+  </tr>
+  <tr>
      <td>0.9.8</td>
      <td><a href="http://dl-ssl.google.com/android/ADT-0.9.8.zip">ADT-0.9.8.zip</a></td>
-     <td><nobr>8703591 bytes</nobr></td>
-     <td>22070f8e52924605a3b3abf87c1ba39f</td>
+     <td><nobr>8301417 bytes</nobr></td>
+     <td>27e0de800512f13feae46fb554e6ee2f</td>
      <td>Requires SDK Tools, Revision 7 <em><nobr>September 2010</nobr></em></td>
   </tr>
   <tr>
diff --git a/docs/html/sdk/eclipse-adt.jd b/docs/html/sdk/eclipse-adt.jd
index a984d56..9f3c8b0 100644
--- a/docs/html/sdk/eclipse-adt.jd
+++ b/docs/html/sdk/eclipse-adt.jd
@@ -1,9 +1,9 @@
 page.title=ADT Plugin for Eclipse
 sdk.preview=0
-adt.zip.version=0.9.8
-adt.zip.download=ADT-0.9.8.zip
-adt.zip.bytes=8703591
-adt.zip.checksum=22070f8e52924605a3b3abf87c1ba39f
+adt.zip.version=0.9.9
+adt.zip.download=ADT-0.9.9.zip
+adt.zip.bytes=8301681
+adt.zip.checksum=7deff0c9b25940a74cea7a0815a3bc36
 
 @jd:body
 
@@ -100,11 +100,40 @@
 </style>
 
 
-
-
 <div class="toggleable opened">
   <a href="#" onclick="return toggleDiv(this)">
         <img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-img" height="9px" width="9px" />
+ADT 0.9.9</a> <em>(September 2010)</em>
+  <div class="toggleme">
+
+
+</ul>
+</dd>
+
+<dl>
+
+<dt>Dependencies:</dt>
+
+<dd><p>ADT 0.9.9 replaces ADT 0.9.8 and is designed for use with SDK Tools r7
+and later. ADT 0.9.9 includes the ADT 0.9.8 features as well as an important
+bugfix, so we recommend that you upgrade as soon as possible. If you haven't
+already installed SDK Tools r7 into your SDK, use the Android SDK Manager to do
+so.</p></dd>
+
+<dt>General notes:</dt>
+<dd>
+<ul>
+<li>Fixes a problem in project import, in which source files were deleted in some cases.</li>
+<li>Includes all other ADT 0.9.8 features (see below).</li>
+</ul>
+</dd>
+</dl>
+ </div>
+</div>
+
+<div class="toggleable closed">
+  <a href="#" onclick="return toggleDiv(this)">
+        <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-img" height="9px" width="9px" />
 ADT 0.9.8</a> <em>(September 2010)</em>
   <div class="toggleme">
 
@@ -116,9 +145,7 @@
 
 <dt>Dependencies:</dt>
 
-<dd><p>ADT 0.9.8 is designed for use with SDK Tools r7 and later. Before 
-updating to ADT 0.9.8, we highly recommend that you use the Android SDK and
-AVD Manager to install SDK Tools r7 into your SDK.</p></dd>
+<dd><p>ADT 0.9.8 is now deprecated. Please use ADT 0.9.9 instead.</p></dd>
 
 <dt>General notes:</dt>
 <dd>
diff --git a/docs/html/sdk/requirements.jd b/docs/html/sdk/requirements.jd
index d710b8e..7b11654 100644
--- a/docs/html/sdk/requirements.jd
+++ b/docs/html/sdk/requirements.jd
@@ -6,7 +6,7 @@
 
 <h3>Supported Operating Systems</h3>
 <ul>
-  <li>Windows XP (32-bit) or Vista (32- or 64-bit)</li>
+  <li>Windows XP (32-bit), Vista (32- or 64-bit), or Windows 7 (32- or 64-bit)</li>
   <li>Mac OS X 10.5.8 or later (x86 only)</li>
   <li>Linux (tested on Linux Ubuntu Hardy Heron)
     <ul>
diff --git a/docs/html/sdk/sdk_toc.cs b/docs/html/sdk/sdk_toc.cs
index a665e95..fdf4438 100644
--- a/docs/html/sdk/sdk_toc.cs
+++ b/docs/html/sdk/sdk_toc.cs
@@ -94,7 +94,7 @@
       <span style="display:none" class="zh-TW"></span>
       </h2>
     <ul>
-      <li><a href="<?cs var:toroot ?>sdk/eclipse-adt.html">ADT 0.9.8
+      <li><a href="<?cs var:toroot ?>sdk/eclipse-adt.html">ADT 0.9.9
       <span style="display:none" class="de"></span>
       <span style="display:none" class="es"></span>
       <span style="display:none" class="fr"></span>
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/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 102a933..e032085 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -381,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();
@@ -961,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/rs/java/Samples/src/com/android/samples/RsList.java b/libs/rs/java/Samples/src/com/android/samples/RsList.java
index d8c733d..0f6b1ac 100644
--- a/libs/rs/java/Samples/src/com/android/samples/RsList.java
+++ b/libs/rs/java/Samples/src/com/android/samples/RsList.java
@@ -54,7 +54,7 @@
     @Override
     protected void onResume() {
         // Ideally a game should implement onResume() and onPause()
-        // to take appropriate action when the activity looses focus
+        // to take appropriate action when the activity loses focus
         super.onResume();
         mView.onResume();
     }
@@ -62,7 +62,7 @@
     @Override
     protected void onPause() {
         // Ideally a game should implement onResume() and onPause()
-        // to take appropriate action when the activity looses focus
+        // to take appropriate action when the activity loses focus
         super.onPause();
         mView.onPause();
     }
diff --git a/libs/rs/java/tests/src/com/android/rs/test/RSTestCore.java b/libs/rs/java/tests/src/com/android/rs/test/RSTestCore.java
index cf9c6be..da64e02 100644
--- a/libs/rs/java/tests/src/com/android/rs/test/RSTestCore.java
+++ b/libs/rs/java/tests/src/com/android/rs/test/RSTestCore.java
@@ -19,10 +19,13 @@
 import android.content.res.Resources;
 import android.renderscript.*;
 import android.util.Log;
+import java.util.ArrayList;
+import java.util.ListIterator;
 
 
 public class RSTestCore {
-    public static final int PART_COUNT = 50000;
+    int mWidth;
+    int mHeight;
 
     public RSTestCore() {
     }
@@ -30,31 +33,85 @@
     private Resources mRes;
     private RenderScriptGL mRS;
 
-    private ScriptC_test_root mRootScript;
+    private Font mFont;
+    ScriptField_ListAllocs_s mListAllocs;
+    int mLastX;
+    int mLastY;
+    private ScriptC_rslist mScript;
 
-    private boolean fp_mad() {
-        ScriptC_fp_mad s = new ScriptC_fp_mad(mRS, mRes, R.raw.fp_mad, true);
-        s.invoke_doTest(0, 0);
-        return true;
-    }
-
-    private boolean rs_primitives_test() {
-        ScriptC_primitives s = new ScriptC_primitives(mRS, mRes, R.raw.primitives, true);
-        s.invoke_rs_primitives_test(0, 0);
-        return true;
-    }
+    private ArrayList<UnitTest> unitTests;
 
     public void init(RenderScriptGL rs, Resources res, int width, int height) {
         mRS = rs;
         mRes = res;
+        mWidth = width;
+        mHeight = height;
 
-        mRootScript = new ScriptC_test_root(mRS, mRes, R.raw.test_root, true);
+        mScript = new ScriptC_rslist(mRS, mRes, R.raw.rslist, true);
 
-        rs_primitives_test();
-        fp_mad();
+        unitTests = new ArrayList<UnitTest>();
 
+        unitTests.add(new UT_primitives(mRes));
+        unitTests.add(new UT_fp_mad(mRes));
+        /*
+        unitTests.add(new UnitTest("<Pass>", 1));
+        unitTests.add(new UnitTest());
+        unitTests.add(new UnitTest("<Fail>", -1));
+        */
+
+        UnitTest [] uta = new UnitTest[unitTests.size()];
+        uta = unitTests.toArray(uta);
+
+        /* Run the actual unit tests */
+        ListIterator<UnitTest> test_iter = unitTests.listIterator();
+        while (test_iter.hasNext()) {
+            UnitTest t = test_iter.next();
+            t.start();
+            try {
+                t.join();
+            } catch (InterruptedException e) {
+            }
+        }
+
+        mListAllocs = new ScriptField_ListAllocs_s(mRS, uta.length);
+        for (int i = 0; i < uta.length; i++) {
+            ScriptField_ListAllocs_s.Item listElem = new ScriptField_ListAllocs_s.Item();
+            listElem.text = Allocation.createFromString(mRS, uta[i].name);
+            listElem.result = uta[i].result;
+            mListAllocs.set(listElem, i, false);
+        }
+
+        mListAllocs.copyAll();
+
+        mScript.bind_gList(mListAllocs);
+
+        mFont = Font.createFromFamily(mRS, mRes, "serif", Font.Style.BOLD, 8);
+        mScript.set_gFont(mFont);
+
+        mRS.contextBindRootScript(mScript);
+        mRS.finish();
     }
 
     public void newTouchPosition(float x, float y, float pressure, int id) {
     }
+
+    public void onActionDown(int x, int y) {
+        mScript.set_gDY(0.0f);
+        mLastX = x;
+        mLastY = y;
+    }
+
+    public void onActionMove(int x, int y) {
+        int dx = mLastX - x;
+        int dy = mLastY - y;
+
+        if (Math.abs(dy) <= 2) {
+            dy = 0;
+        }
+
+        mScript.set_gDY(dy);
+
+        mLastX = x;
+        mLastY = y;
+    }
 }
diff --git a/libs/rs/java/tests/src/com/android/rs/test/RSTestView.java b/libs/rs/java/tests/src/com/android/rs/test/RSTestView.java
index 7ae0c08..ce99c6d 100644
--- a/libs/rs/java/tests/src/com/android/rs/test/RSTestView.java
+++ b/libs/rs/java/tests/src/com/android/rs/test/RSTestView.java
@@ -67,40 +67,28 @@
         }
     }
 
-/*
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event)
+    {
+        return super.onKeyDown(keyCode, event);
+    }
+
     @Override
     public boolean onTouchEvent(MotionEvent ev)
     {
-        int act = ev.getActionMasked();
-        if (act == ev.ACTION_UP) {
-            mRender.newTouchPosition(0, 0, 0, ev.getPointerId(0));
-            return false;
-        } else if (act == MotionEvent.ACTION_POINTER_UP) {
-            // only one pointer going up, we can get the index like this
-            int pointerIndex = ev.getActionIndex();
-            int pointerId = ev.getPointerId(pointerIndex);
-            mRender.newTouchPosition(0, 0, 0, pointerId);
+        boolean ret = false;
+        int act = ev.getAction();
+        if (act == ev.ACTION_DOWN) {
+            mRender.onActionDown((int)ev.getX(), (int)ev.getY());
+            ret = true;
         }
-        int count = ev.getHistorySize();
-        int pcount = ev.getPointerCount();
-
-        for (int p=0; p < pcount; p++) {
-            int id = ev.getPointerId(p);
-            mRender.newTouchPosition(ev.getX(p),
-                                     ev.getY(p),
-                                     ev.getPressure(p),
-                                     id);
-
-            for (int i=0; i < count; i++) {
-                mRender.newTouchPosition(ev.getHistoricalX(p, i),
-                                         ev.getHistoricalY(p, i),
-                                         ev.getHistoricalPressure(p, i),
-                                         id);
-            }
+        else if (act == ev.ACTION_MOVE) {
+            mRender.onActionMove((int)ev.getX(), (int)ev.getY());
+            ret = true;
         }
-        return true;
+
+        return ret;
     }
-    */
 }
 
 
diff --git a/libs/rs/java/tests/src/com/android/rs/test/UT_fp_mad.java b/libs/rs/java/tests/src/com/android/rs/test/UT_fp_mad.java
new file mode 100644
index 0000000..c4e57e8
--- /dev/null
+++ b/libs/rs/java/tests/src/com/android/rs/test/UT_fp_mad.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.rs.test;
+
+import android.content.res.Resources;
+import android.renderscript.*;
+
+public class UT_fp_mad extends UnitTest {
+    private Resources mRes;
+
+    protected UT_fp_mad(Resources res) {
+        super("Fp_Mad");
+        mRes = res;
+    }
+
+    public void run() {
+        RenderScript pRS = RenderScript.create();
+        ScriptC_fp_mad s = new ScriptC_fp_mad(pRS, mRes, R.raw.fp_mad, true);
+        pRS.mMessageCallback = mRsMessage;
+        s.invoke_fp_mad_test(0, 0);
+        pRS.finish();
+        pRS.destroy();
+    }
+}
+
diff --git a/libs/rs/java/tests/src/com/android/rs/test/UT_primitives.java b/libs/rs/java/tests/src/com/android/rs/test/UT_primitives.java
new file mode 100644
index 0000000..629f0b2
--- /dev/null
+++ b/libs/rs/java/tests/src/com/android/rs/test/UT_primitives.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.rs.test;
+
+import android.content.res.Resources;
+import android.renderscript.*;
+
+public class UT_primitives extends UnitTest {
+    private Resources mRes;
+
+    protected UT_primitives(Resources res) {
+        super("Primitives");
+        mRes = res;
+    }
+
+    public void run() {
+        RenderScript pRS = RenderScript.create();
+        ScriptC_primitives s = new ScriptC_primitives(pRS, mRes, R.raw.primitives, true);
+        pRS.mMessageCallback = mRsMessage;
+        s.invoke_primitives_test(0, 0);
+        pRS.finish();
+        //android.util.Log.v("UT", "After pRS.finish");
+
+        pRS.destroy();
+    }
+}
+
diff --git a/libs/rs/java/tests/src/com/android/rs/test/UnitTest.java b/libs/rs/java/tests/src/com/android/rs/test/UnitTest.java
new file mode 100644
index 0000000..5b55779
--- /dev/null
+++ b/libs/rs/java/tests/src/com/android/rs/test/UnitTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.rs.test;
+import android.renderscript.RenderScript.RSMessage;
+
+public class UnitTest extends Thread {
+    public String name;
+    public int result;
+
+    /* These constants must match those in shared.rsh */
+    public static final int RS_MSG_TEST_PASSED = 100;
+    public static final int RS_MSG_TEST_FAILED = 101;
+
+    protected UnitTest(String n, int initResult) {
+        super();
+        name = n;
+        result = initResult;
+    }
+
+    protected UnitTest(String n) {
+        this(n, 0);
+    }
+
+    protected UnitTest() {
+        this ("<Unknown>");
+    }
+
+    protected RSMessage mRsMessage = new RSMessage() {
+        public void run() {
+            switch (mID) {
+                case RS_MSG_TEST_PASSED:
+                    result = 1;
+                    break;
+                case RS_MSG_TEST_FAILED:
+                    result = -1;
+                    break;
+                default:
+                    break;
+            }
+        }
+    };
+
+    public void run() {
+        /* This method needs to be implemented for each subclass */
+    }
+}
+
diff --git a/libs/rs/java/tests/src/com/android/rs/test/fp_mad.rs b/libs/rs/java/tests/src/com/android/rs/test/fp_mad.rs
index 494ff35..eb82e56 100644
--- a/libs/rs/java/tests/src/com/android/rs/test/fp_mad.rs
+++ b/libs/rs/java/tests/src/com/android/rs/test/fp_mad.rs
@@ -3,8 +3,7 @@
 const int TEST_COUNT = 1;
 
 #pragma rs export_var(g_results)
-#pragma rs export_func(doTest)
-
+#pragma rs export_func(fp_mad_test)
 
 static float data_f1[1025];
 static float4 data_f4[1025];
@@ -143,7 +142,7 @@
     rsDebug("fp_clamp4 M ops", 100.f / time);
 }
 
-void doTest(uint32_t index, int test_num) {
+void fp_mad_test(uint32_t index, int test_num) {
     for (int x=0; x < 1025; x++) {
         data_f1[x] = (x & 0xf) * 0.1f;
         data_f4[x].x = (x & 0xf) * 0.1f;
@@ -168,6 +167,10 @@
     test_sincos(index);
     test_clamp4(index);
     test_clamp(index);
+
+    // TODO Actually verify test result accuracy
+    rsDebug("fp_mad_test PASSED", 0);
+    rsSendToClient(RS_MSG_TEST_PASSED);
 }
 
 
diff --git a/libs/rs/java/tests/src/com/android/rs/test/primitives.rs b/libs/rs/java/tests/src/com/android/rs/test/primitives.rs
index 39bd2c4..2ba5d52 100644
--- a/libs/rs/java/tests/src/com/android/rs/test/primitives.rs
+++ b/libs/rs/java/tests/src/com/android/rs/test/primitives.rs
@@ -1,10 +1,8 @@
 #include "shared.rsh"
 
-#pragma rs export_func(rs_primitives_test)
+#pragma rs export_func(primitives_test)
 
 // Testing primitive types
-#pragma rs export_var(floatTest)
-#pragma rs export_var(doubleTest)
 static float floatTest = 1.99f;
 static double doubleTest = 2.05;
 static char charTest = -8;
@@ -14,7 +12,7 @@
 static ushort ushortTest = 16;
 static uint uintTest = 32;
 
-static void test_primitive_types(uint32_t index) {
+static bool test_primitive_types(uint32_t index) {
     bool failed = false;
     start();
 
@@ -28,17 +26,26 @@
     _RS_ASSERT(uintTest == 32);
 
     float time = end(index);
+
     if (failed) {
-        rsDebug("test_primitives FAILED ", time);
+        rsDebug("test_primitives FAILED", time);
     }
     else {
-        rsDebug("test_primitives PASSED ", time);
+        rsDebug("test_primitives PASSED", time);
+    }
+
+    return failed;
+}
+
+void primitives_test(uint32_t index, int test_num) {
+    bool failed = false;
+    failed |= test_primitive_types(index);
+
+    if (failed) {
+        rsSendToClient(RS_MSG_TEST_FAILED);
+    }
+    else {
+        rsSendToClient(RS_MSG_TEST_PASSED);
     }
 }
 
-
-void rs_primitives_test(uint32_t index, int test_num) {
-    test_primitive_types(index);
-}
-
-
diff --git a/libs/rs/java/tests/src/com/android/rs/test/rslist.rs b/libs/rs/java/tests/src/com/android/rs/test/rslist.rs
new file mode 100644
index 0000000..fcea3ab
--- /dev/null
+++ b/libs/rs/java/tests/src/com/android/rs/test/rslist.rs
@@ -0,0 +1,86 @@
+// 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.
+
+#pragma version(1)
+
+#pragma rs java_package_name(com.android.rs.test)
+
+#include "rs_graphics.rsh"
+
+float gDY;
+
+rs_font gFont;
+
+typedef struct ListAllocs_s {
+    rs_allocation text;
+    int result;
+} ListAllocs;
+
+ListAllocs *gList;
+
+#pragma rs export_var(gDY, gFont, gList)
+
+void init() {
+    gDY = 0.0f;
+}
+
+int textPos = 0;
+
+int root(int launchID) {
+
+    rsgClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+    rsgClearDepth(1.0f);
+
+    textPos -= (int)gDY*2;
+    gDY *= 0.95;
+
+    rsgFontColor(0.9f, 0.9f, 0.9f, 1.0f);
+    rsgBindFont(gFont);
+    color(0.2, 0.2, 0.2, 0);
+
+    rs_allocation listAlloc = rsGetAllocation(gList);
+    int allocSize = rsAllocationGetDimX(listAlloc);
+
+    int width = rsgGetWidth();
+    int height = rsgGetHeight();
+
+    int itemHeight = 80;
+    int currentYPos = itemHeight + textPos;
+
+    for(int i = 0; i < allocSize; i ++) {
+        if(currentYPos - itemHeight > height) {
+            break;
+        }
+
+        if(currentYPos > 0) {
+            switch(gList[i].result) {
+                case 1: /* Passed */
+                    rsgFontColor(0.5f, 0.9f, 0.5f, 1.0f);
+                    break;
+                case -1: /* Failed */
+                    rsgFontColor(0.9f, 0.5f, 0.5f, 1.0f);
+                    break;
+                case 0: /* Unknown */
+                default:
+                    rsgFontColor(0.9f, 0.9f, 0.9f, 1.0f);
+                    break;
+            }
+            rsgDrawRect(0, currentYPos - 1, width, currentYPos, 0);
+            rsgDrawText(gList[i].text, 30, currentYPos - 32);
+        }
+        currentYPos += itemHeight;
+    }
+
+    return 10;
+}
diff --git a/libs/rs/java/tests/src/com/android/rs/test/shared.rsh b/libs/rs/java/tests/src/com/android/rs/test/shared.rsh
index 8c3e5f4..820fffd 100644
--- a/libs/rs/java/tests/src/com/android/rs/test/shared.rsh
+++ b/libs/rs/java/tests/src/com/android/rs/test/shared.rsh
@@ -25,7 +25,6 @@
 
 #define _RS_ASSERT(b) \
 do { \
-    rsDebug("Checking " #b, ((int) (b))); \
     if (!(b)) { \
         failed = true; \
         rsDebug(#b " FAILED", 0); \
@@ -33,3 +32,7 @@
 \
 } while (0)
 
+/* These constants must match those in UnitTest.java */
+static const int RS_MSG_TEST_PASSED = 100;
+static const int RS_MSG_TEST_FAILED = 101;
+
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 7469133..b97c3c4 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -963,14 +963,17 @@
     //--------------------
 
     /**
-     * Attaches an auxiliary effect to the audio track. A typical auxiliary effect is a
-     * reverberation effect which can be applied on any sound source that directs a certain
-     * amount of its energy to this effect. This amount is defined by setAuxEffectSendLevel().
+     * Attaches an auxiliary effect to the audio track. A typical auxiliary
+     * effect is a reverberation effect which can be applied on any sound source
+     * that directs a certain amount of its energy to this effect. This amount
+     * is defined by setAuxEffectSendLevel().
      * {@see #setAuxEffectSendLevel(float)}.
-     * <p>After creating an auxiliary effect (e.g. {@link android.media.EnvironmentalReverb}),
-     * retrieve its ID with {@link android.media.AudioEffect#getId()} and use it when calling
+     * <p>After creating an auxiliary effect (e.g.
+     * {@link android.media.audiofx.EnvironmentalReverb}), retrieve its ID with
+     * {@link android.media.audiofx.AudioEffect#getId()} and use it when calling
      * this method to attach the audio track to the effect.
-     * <p>To detach the effect from the audio track, call this method with a null effect id.
+     * <p>To detach the effect from the audio track, call this method with a
+     * null effect id.
      *
      * @param effectId system wide unique id of the effect to attach
      * @return error code or success, see {@link #SUCCESS},
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 280def9..cb46a29 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -1210,9 +1210,10 @@
      * effect which can be applied on any sound source that directs a certain amount of its
      * energy to this effect. This amount is defined by setAuxEffectSendLevel().
      * {@see #setAuxEffectSendLevel(float)}.
-     * <p>After creating an auxiliary effect (e.g. {@link android.media.EnvironmentalReverb}),
-     * retrieve its ID with {@link android.media.AudioEffect#getId()} and use it when calling
-     * this method to attach the player to the effect.
+     * <p>After creating an auxiliary effect (e.g.
+     * {@link android.media.audiofx.EnvironmentalReverb}), retrieve its ID with
+     * {@link android.media.audiofx.AudioEffect#getId()} and use it when calling this method
+     * to attach the player to the effect.
      * <p>To detach the effect from the player, call this method with a null effect id.
      * <p>This method must be called after one of the overloaded <code> setDataSource </code>
      * methods.
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/MtpDatabase.java b/media/java/android/media/MtpDatabase.java
index b64299a..403ed58 100644
--- a/media/java/android/media/MtpDatabase.java
+++ b/media/java/android/media/MtpDatabase.java
@@ -246,8 +246,30 @@
         return new int[] {
             // allow transfering arbitrary files
             MtpConstants.FORMAT_UNDEFINED,
+
             MtpConstants.FORMAT_ASSOCIATION,
+            MtpConstants.FORMAT_TEXT,
+            MtpConstants.FORMAT_HTML,
+            MtpConstants.FORMAT_WAV,
+            MtpConstants.FORMAT_MP3,
+            MtpConstants.FORMAT_MPEG,
+            MtpConstants.FORMAT_EXIF_JPEG,
+            MtpConstants.FORMAT_TIFF_EP,
+            MtpConstants.FORMAT_GIF,
+            MtpConstants.FORMAT_JFIF,
+            MtpConstants.FORMAT_PNG,
+            MtpConstants.FORMAT_TIFF,
+            MtpConstants.FORMAT_WMA,
+            MtpConstants.FORMAT_OGG,
+            MtpConstants.FORMAT_AAC,
+            MtpConstants.FORMAT_MP4_CONTAINER,
+            MtpConstants.FORMAT_MP2,
+            MtpConstants.FORMAT_3GP_CONTAINER,
             MtpConstants.FORMAT_ABSTRACT_AV_PLAYLIST,
+            MtpConstants.FORMAT_WPL_PLAYLIST,
+            MtpConstants.FORMAT_M3U_PLAYLIST,
+            MtpConstants.FORMAT_PLS_PLAYLIST,
+            MtpConstants.FORMAT_XML_DOCUMENT,
         };
     }
 
@@ -260,9 +282,13 @@
         return new int[] {
             MtpConstants.PROPERTY_STORAGE_ID,
             MtpConstants.PROPERTY_OBJECT_FORMAT,
+            MtpConstants.PROPERTY_PROTECTION_STATUS,
             MtpConstants.PROPERTY_OBJECT_SIZE,
             MtpConstants.PROPERTY_OBJECT_FILE_NAME,
+            MtpConstants.PROPERTY_DATE_MODIFIED,
             MtpConstants.PROPERTY_PARENT_OBJECT,
+            MtpConstants.PROPERTY_PERSISTENT_UID,
+            MtpConstants.PROPERTY_NAME,
         };
     }
 
@@ -279,6 +305,11 @@
         String column = null;
         boolean isString = false;
 
+        // temporary hack
+        if (property == MtpConstants.PROPERTY_NAME) {
+            property = MtpConstants.PROPERTY_OBJECT_FILE_NAME;
+        }
+
         switch (property) {
             case MtpConstants.PROPERTY_STORAGE_ID:
                 outIntValue[0] = mStorageID;
diff --git a/media/java/android/media/AudioEffect.java b/media/java/android/media/audiofx/AudioEffect.java
similarity index 89%
rename from media/java/android/media/AudioEffect.java
rename to media/java/android/media/audiofx/AudioEffect.java
index ed7601e..3e54627 100644
--- a/media/java/android/media/AudioEffect.java
+++ b/media/java/android/media/audiofx/AudioEffect.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.media;
+package android.media.audiofx;
 
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
@@ -29,22 +29,35 @@
 import java.util.UUID;
 
 /**
- * AudioEffect is the base class for implementing audio effect control in Java
- * applications.
- * <p>Creating an AudioEffect object will create the effect engine in
- * audio framework if no instance of the same effect type exists in the
- * specified audio session. If one exists, this instance will be used.
- * <p>The application creating the AudioEffect object (or a derived class) will either
- * receive control of the effect engine or not depending on the priority
- * parameter. If priority is higher than the priority used by the current effect
- * engine owner, the control will be transfered to the new object. Otherwise
- * control will remain with the previous object. In this case, the new
- * application will be notified of changes in effect engine state or control
- * ownership by the appropiate listener.
- * <p>If the effect is to be applied to a specific AudioTrack or MediaPlayer instance,
- * the application must specify the audio session ID of that instance when calling the AudioEffect
- * constructor.
+ * AudioEffect is the base class for controlling audio effects provided by the android audio
+ * framework.
+ * <p>Applications should not use the AudioEffect class directly but one of its derived classes to
+ * control specific effects:
+ * <ul>
+ *   <li> {@link android.media.audiofx.Equalizer}</li>
+ *   <li> {@link android.media.audiofx.Virtualizer}</li>
+ *   <li> {@link android.media.audiofx.BassBoost}</li>
+ *   <li> {@link android.media.audiofx.PresetReverb}</li>
+ *   <li> {@link android.media.audiofx.EnvironmentalReverb}</li>
+ * </ul>
+ * <p>If the audio effect is to be applied to a specific AudioTrack or MediaPlayer instance,
+ * the application must specify the audio session ID of that instance when creating the AudioEffect.
+ * (see {@link android.media.MediaPlayer#getAudioSessionId()} for details on audio sessions).
+ * To apply an effect to the global audio output mix, session 0 must be specified when creating the
+ * AudioEffect.
+ * <p>Creating an effect on the output mix (audio session 0) requires permission
+ * {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS}
+ * <p>Creating an AudioEffect object will create the corresponding effect engine in the audio
+ * framework if no instance of the same effect type exists in the specified audio session.
+ * If one exists, this instance will be used.
+ * <p>The application creating the AudioEffect object (or a derived class) will either receive
+ * control of the effect engine or not depending on the priority parameter. If priority is higher
+ * than the priority used by the current effect engine owner, the control will be transfered to the
+ * new object. Otherwise control will remain with the previous object. In this case, the new
+ * application will be notified of changes in effect engine state or control ownership by the
+ * appropiate listener.
  */
+
 public class AudioEffect {
     static {
         System.loadLibrary("audioeffect_jni");
@@ -62,32 +75,38 @@
 
     /**
      * UUID for environmental reverb effect
+     * @hide
      */
     public static final UUID EFFECT_TYPE_ENV_REVERB = UUID
             .fromString("c2e5d5f0-94bd-4763-9cac-4e234d06839e");
     /**
      * UUID for preset reverb effect
+     * @hide
      */
     public static final UUID EFFECT_TYPE_PRESET_REVERB = UUID
             .fromString("47382d60-ddd8-11db-bf3a-0002a5d5c51b");
     /**
      * UUID for equalizer effect
+     * @hide
      */
     public static final UUID EFFECT_TYPE_EQUALIZER = UUID
             .fromString("0bed4300-ddd6-11db-8f34-0002a5d5c51b");
     /**
      * UUID for bass boost effect
+     * @hide
      */
     public static final UUID EFFECT_TYPE_BASS_BOOST = UUID
             .fromString("0634f220-ddd4-11db-a0fc-0002a5d5c51b");
     /**
      * UUID for virtualizer effect
+     * @hide
      */
     public static final UUID EFFECT_TYPE_VIRTUALIZER = UUID
             .fromString("37cc2c00-dddd-11db-8577-0002a5d5c51b");
 
     /**
      * Null effect UUID. Used when the UUID for effect type of
+     * @hide
      */
     public static final UUID EFFECT_TYPE_NULL = UUID
             .fromString("ec7178ec-e5e1-4432-a3f4-4657e6795210");
@@ -95,10 +114,12 @@
     /**
      * State of an AudioEffect object that was not successfully initialized upon
      * creation
+     * @hide
      */
     public static final int STATE_UNINITIALIZED = 0;
     /**
      * State of an AudioEffect object that is ready to be used.
+     * @hide
      */
     public static final int STATE_INITIALIZED = 1;
 
@@ -106,14 +127,17 @@
     // frameworks/base/include/media/AudioEffect.h
     /**
      * Event id for engine control ownership change notification.
+     * @hide
      */
     public static final int NATIVE_EVENT_CONTROL_STATUS = 0;
     /**
      * Event id for engine state change notification.
+     * @hide
      */
     public static final int NATIVE_EVENT_ENABLED_STATUS = 1;
     /**
      * Event id for engine parameter change notification.
+     * @hide
      */
     public static final int NATIVE_EVENT_PARAMETER_CHANGED = 2;
 
@@ -151,15 +175,17 @@
     public static final int ERROR_DEAD_OBJECT = -7;
 
     /**
-     * The effect descriptor contains necessary information to facilitate
-     * effects enumeration:<br>
+     * The effect descriptor contains information on a particular effect implemented in the
+     * audio framework:<br>
      * <ul>
-     *  <li>mType: UUID corresponding to the OpenSL ES interface implemented by this effect</li>
-     *  <li>mUuid: UUID for this particular implementation</li>
-     *  <li>mConnectMode: {@link #EFFECT_INSERT} or {@link #EFFECT_AUXILIARY}</li>
-     *  <li>mName: human readable effect name</li>
-     *  <li>mImplementor: human readable effect implementor name</li>
+     *  <li>type: UUID corresponding to the OpenSL ES interface implemented by this effect</li>
+     *  <li>uuid: UUID for this particular implementation</li>
+     *  <li>connectMode: {@link #EFFECT_INSERT} or {@link #EFFECT_AUXILIARY}</li>
+     *  <li>name: human readable effect name</li>
+     *  <li>implementor: human readable effect implementor name</li>
      * </ul>
+     * The method {@link #queryEffects()} returns an array of Descriptors to facilitate effects
+     * enumeration.
      */
     public static class Descriptor {
 
@@ -168,18 +194,39 @@
 
         public Descriptor(String type, String uuid, String connectMode,
                 String name, String implementor) {
-            mType = UUID.fromString(type);
-            mUuid = UUID.fromString(uuid);
-            mConnectMode = connectMode;
-            mName = name;
-            mImplementor = implementor;
+            this.type = UUID.fromString(type);
+            this.uuid = UUID.fromString(uuid);
+            this.connectMode = connectMode;
+            this.name = name;
+            this.implementor = implementor;
         }
 
-        public UUID mType;
-        public UUID mUuid;
-        public String mConnectMode;
-        public String mName;
-        public String mImplementor;
+        /**
+         *  Indicates the generic type of the effect (Equalizer, Bass boost ...). The UUID
+         *  corresponds to the OpenSL ES Interface ID for this type of effect.
+         */
+        public UUID type;
+        /**
+         *  Indicates the particular implementation of the effect in that type. Several effects
+         *  can have the same type but this uuid is unique to a given implementation.
+         */
+        public UUID uuid;
+        /**
+         *  Indicates if the effect is of insert category {@link #EFFECT_INSERT} or auxiliary
+         *  category {@link #EFFECT_AUXILIARY}. Insert effects (Typically an Equalizer) are applied
+         *  to the entire audio source and usually not shared by several sources. Auxiliary effects
+         *  (typically a reverberator) are applied to part of the signal (wet) and the effect output
+         *  is added to the original signal (dry).
+         */
+        public String connectMode;
+        /**
+         * Human readable effect name
+         */
+        public String name;
+        /**
+         * Human readable effect implementor name
+         */
+        public String implementor;
     };
 
     /**
@@ -242,10 +289,12 @@
     private OnParameterChangeListener mParameterChangeListener = null;
     /**
      * Lock to protect listeners updates against event notifications
+     * @hide
      */
     public final Object mListenerLock = new Object();
     /**
      * Handler for events coming from the native code
+     * @hide
      */
     public NativeEventHandler mNativeEventHandler = null;
 
@@ -283,6 +332,7 @@
      * @throws java.lang.IllegalArgumentException
      * @throws java.lang.UnsupportedOperationException
      * @throws java.lang.RuntimeException
+     * @hide
      */
 
     public AudioEffect(UUID type, UUID uuid, int priority, int audioSession)
@@ -337,7 +387,7 @@
     /**
      * Get the effect descriptor.
      *
-     * @see android.media.AudioEffect.Descriptor
+     * @see android.media.audiofx.AudioEffect.Descriptor
      * @throws IllegalStateException
      */
     public Descriptor getDescriptor() throws IllegalStateException {
@@ -351,7 +401,7 @@
 
     /**
      * Query all effects available on the platform. Returns an array of
-     * {@link android.media.AudioEffect.Descriptor} objects
+     * {@link android.media.audiofx.AudioEffect.Descriptor} objects
      *
      * @throws IllegalStateException
      */
@@ -365,7 +415,11 @@
     // --------------------
 
     /**
-     * Enable or disable effect engine.
+     * Enable or disable the effect.
+     * Creating an audio effect does not automatically apply this effect on the audio source. It
+     * creates the resources necessary to process this effect but the audio signal is still bypassed
+     * through the effect engine. Calling this method will make that the effect is actually applied
+     * or not to the audio content being played in the corresponding audio session.
      *
      * @param enabled the requested enable state
      * @return {@link #SUCCESS} in case of success, {@link #ERROR_INVALID_OPERATION}
@@ -392,6 +446,7 @@
      *         {@link #ERROR_NO_MEMORY}, {@link #ERROR_INVALID_OPERATION} or
      *         {@link #ERROR_DEAD_OBJECT} in case of failure
      * @throws IllegalStateException
+     * @hide
      */
     public int setParameter(byte[] param, byte[] value)
             throws IllegalStateException {
@@ -403,6 +458,7 @@
      * Set effect parameter. The parameter and its value are integers.
      *
      * @see #setParameter(byte[], byte[])
+     * @hide
      */
     public int setParameter(int param, int value) throws IllegalStateException {
         byte[] p = intToByteArray(param);
@@ -415,6 +471,7 @@
      * short integer.
      *
      * @see #setParameter(byte[], byte[])
+     * @hide
      */
     public int setParameter(int param, short value)
             throws IllegalStateException {
@@ -428,6 +485,7 @@
      * array of bytes.
      *
      * @see #setParameter(byte[], byte[])
+     * @hide
      */
     public int setParameter(int param, byte[] value)
             throws IllegalStateException {
@@ -440,6 +498,7 @@
      * the value is also an array of 1 or 2 integers
      *
      * @see #setParameter(byte[], byte[])
+     * @hide
      */
     public int setParameter(int[] param, int[] value)
             throws IllegalStateException {
@@ -464,6 +523,7 @@
      * the value is an array of 1 or 2 short integers
      *
      * @see #setParameter(byte[], byte[])
+     * @hide
      */
     public int setParameter(int[] param, short[] value)
             throws IllegalStateException {
@@ -489,6 +549,7 @@
      * the value is an array of bytes
      *
      * @see #setParameter(byte[], byte[])
+     * @hide
      */
     public int setParameter(int[] param, byte[] value)
             throws IllegalStateException {
@@ -519,6 +580,7 @@
      *         returning, value.length is updated with the actual size of the
      *         returned value.
      * @throws IllegalStateException
+     * @hide
      */
     public int getParameter(byte[] param, byte[] value)
             throws IllegalStateException {
@@ -539,6 +601,7 @@
      * array of bytes.
      *
      * @see #getParameter(byte[], byte[])
+     * @hide
      */
     public int getParameter(int param, byte[] value)
             throws IllegalStateException {
@@ -552,6 +615,7 @@
      * array of 1 or 2 integers
      *
      * @see #getParameter(byte[], byte[])
+     * @hide
      */
     public int getParameter(int param, int[] value)
             throws IllegalStateException {
@@ -576,6 +640,7 @@
      * array of 1 or 2 short integers
      *
      * @see #getParameter(byte[], byte[])
+     * @hide
      */
     public int getParameter(int param, short[] value)
             throws IllegalStateException {
@@ -600,6 +665,7 @@
      * the value is also an array of 1 or 2 integers
      *
      * @see #getParameter(byte[], byte[])
+     * @hide
      */
     public int getParameter(int[] param, int[] value)
             throws IllegalStateException {
@@ -627,6 +693,7 @@
      * the value is an array of 1 or 2 short integers
      *
      * @see #getParameter(byte[], byte[])
+     * @hide
      */
     public int getParameter(int[] param, short[] value)
             throws IllegalStateException {
@@ -654,6 +721,7 @@
      * the value is an array of bytes
      *
      * @see #getParameter(byte[], byte[])
+     * @hide
      */
     public int getParameter(int[] param, byte[] value)
             throws IllegalStateException {
@@ -673,6 +741,7 @@
      * Send a command to the effect engine. This method is intended to send
      * proprietary commands to a particular effect implementation.
      *
+     * @hide
      */
     public int command(int cmdCode, byte[] command, byte[] reply)
             throws IllegalStateException {
@@ -709,7 +778,7 @@
     }
 
     /**
-     * Returns effect engine enable state
+     * Returns effect enabled state
      *
      * @return true if the effect is enabled, false otherwise.
      * @throws IllegalStateException
@@ -768,6 +837,7 @@
      * Sets the listener AudioEffect notifies when a parameter is changed.
      *
      * @param listener
+     * @hide
      */
     public void setParameterListener(OnParameterChangeListener listener) {
         synchronized (mListenerLock) {
@@ -828,6 +898,7 @@
     /**
      * The OnParameterChangeListener interface defines a method called by the AudioEffect
      * when a parameter is changed in the effect engine by the controlling application.
+     * @hide
      */
     public interface OnParameterChangeListener {
         /**
@@ -914,8 +985,7 @@
      * {@link #ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION} and
      * {@link #ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION} intents.
      * <p>The extra value is of type int and is the audio session ID.
-     *
-     *  @see android.media.MediaPlayer#setAudioSessionId(int)
+     *  @see android.media.MediaPlayer#getAudioSessionId() for details on audio sessions.
      */
      public static final String EXTRA_AUDIO_SESSION = "android.media.extra.AUDIO_SESSION";
 
@@ -1086,6 +1156,9 @@
     // Utility methods
     // ------------------
 
+    /**
+    * @hide
+    */
     public void checkState(String methodName) throws IllegalStateException {
         synchronized (mStateLock) {
             if (mState != STATE_INITIALIZED) {
@@ -1095,6 +1168,9 @@
         }
     }
 
+    /**
+     * @hide
+     */
     public void checkStatus(int status) {
         switch (status) {
         case AudioEffect.SUCCESS:
@@ -1110,11 +1186,17 @@
         }
     }
 
+    /**
+     * @hide
+     */
     public int byteArrayToInt(byte[] valueBuf) {
         return byteArrayToInt(valueBuf, 0);
 
     }
 
+    /**
+     * @hide
+     */
     public int byteArrayToInt(byte[] valueBuf, int offset) {
         ByteBuffer converter = ByteBuffer.wrap(valueBuf);
         converter.order(ByteOrder.nativeOrder());
@@ -1122,6 +1204,9 @@
 
     }
 
+    /**
+     * @hide
+     */
     public byte[] intToByteArray(int value) {
         ByteBuffer converter = ByteBuffer.allocate(4);
         converter.order(ByteOrder.nativeOrder());
@@ -1129,10 +1214,16 @@
         return converter.array();
     }
 
+    /**
+     * @hide
+     */
     public short byteArrayToShort(byte[] valueBuf) {
         return byteArrayToShort(valueBuf, 0);
     }
 
+    /**
+     * @hide
+     */
     public short byteArrayToShort(byte[] valueBuf, int offset) {
         ByteBuffer converter = ByteBuffer.wrap(valueBuf);
         converter.order(ByteOrder.nativeOrder());
@@ -1140,6 +1231,9 @@
 
     }
 
+    /**
+     * @hide
+     */
     public byte[] shortToByteArray(short value) {
         ByteBuffer converter = ByteBuffer.allocate(2);
         converter.order(ByteOrder.nativeOrder());
@@ -1148,6 +1242,9 @@
         return converter.array();
     }
 
+    /**
+     * @hide
+     */
     public byte[] concatArrays(byte[]... arrays) {
         int len = 0;
         for (byte[] a : arrays) {
@@ -1162,5 +1259,4 @@
         }
         return b;
     }
-
 }
diff --git a/media/java/android/media/BassBoost.java b/media/java/android/media/audiofx/BassBoost.java
similarity index 94%
rename from media/java/android/media/BassBoost.java
rename to media/java/android/media/audiofx/BassBoost.java
index 476b056..ca55f0f 100644
--- a/media/java/android/media/BassBoost.java
+++ b/media/java/android/media/audiofx/BassBoost.java
@@ -14,12 +14,12 @@
  * limitations under the License.
  */
 
-package android.media;
+package android.media.audiofx;
 
 import android.app.Activity;
 import android.content.Context;
 import android.content.Intent;
-import android.media.AudioEffect;
+import android.media.audiofx.AudioEffect;
 import android.os.Bundle;
 import android.util.Log;
 
@@ -32,15 +32,19 @@
 /**
  * Bass boost is an audio effect to boost or amplify low frequencies of the sound. It is comparable
  * to a simple equalizer but limited to one band amplification in the low frequency range.
- * <p>An application creates a BassBoost object to instantiate and control a bass boost engine
- * in the audio framework.
+ * <p>An application creates a BassBoost object to instantiate and control a bass boost engine in
+ * the audio framework.
  * <p>The methods, parameter types and units exposed by the BassBoost implementation are directly
  * mapping those defined by the OpenSL ES 1.0.1 Specification (http://www.khronos.org/opensles/)
  * for the SLBassBoostItf interface. Please refer to this specification for more details.
  * <p>To attach the BassBoost to a particular AudioTrack or MediaPlayer, specify the audio session
- * ID of this AudioTrack or MediaPlayer when constructing the BassBoost. If the audio session ID 0
- * is specified, the BassBoost applies to the main audio output mix.
- * <p> See {@link android.media.AudioEffect} class for more details on controlling audio effects.
+ * ID of this AudioTrack or MediaPlayer when constructing the BassBoost.
+ * If the audio session ID 0 is specified, the BassBoost applies to the main audio output mix.
+ * <p>Creating a BassBoost on the output mix (audio session 0) requires permission
+ * {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS}
+ * <p>See {@link android.media.MediaPlayer#getAudioSessionId()} for details on audio sessions.
+ * <p>See {@link android.media.audiofx.AudioEffect} class for more details on
+ * controlling audio effects.
  */
 
 public class BassBoost extends AudioEffect {
@@ -55,7 +59,7 @@
     public static final int PARAM_STRENGTH_SUPPORTED = 0;
     /**
      * Bass boost effect strength. Parameter ID for
-     * {@link android.media.BassBoost.OnParameterChangeListener}
+     * {@link android.media.audiofx.BassBoost.OnParameterChangeListener}
      */
     public static final int PARAM_STRENGTH = 1;
 
@@ -155,7 +159,6 @@
          * BassBoost engine.
          * @param effect the BassBoost on which the interface is registered.
          * @param status status of the set parameter operation.
-         * See {@link android.media.AudioEffect#setParameter(byte[], byte[])}.
          * @param param ID of the modified parameter. See {@link #PARAM_STRENGTH} ...
          * @param value the new parameter value.
          */
diff --git a/media/java/android/media/EnvironmentalReverb.java b/media/java/android/media/audiofx/EnvironmentalReverb.java
similarity index 96%
rename from media/java/android/media/EnvironmentalReverb.java
rename to media/java/android/media/audiofx/EnvironmentalReverb.java
index b50febc..f1f582e 100644
--- a/media/java/android/media/EnvironmentalReverb.java
+++ b/media/java/android/media/audiofx/EnvironmentalReverb.java
@@ -14,12 +14,12 @@
  * limitations under the License.
  */
 
-package android.media;
+package android.media.audiofx;
 
 import android.app.Activity;
 import android.content.Context;
 import android.content.Intent;
-import android.media.AudioEffect;
+import android.media.audiofx.AudioEffect;
 import android.os.Bundle;
 import android.util.Log;
 
@@ -28,18 +28,18 @@
 import java.util.StringTokenizer;
 
 /**
- * A sound generated within a room travels in many directions. The listener first hears the
- * direct sound from the source itself. Later, he or she hears discrete echoes caused by sound
- * bouncing off nearby walls, the ceiling and the floor. As sound waves arrive after
- * undergoing more and more reflections, individual reflections become indistinguishable and
- * the listener hears continuous reverberation that decays over time.
+ * A sound generated within a room travels in many directions. The listener first hears the direct
+ * sound from the source itself. Later, he or she hears discrete echoes caused by sound bouncing off
+ * nearby walls, the ceiling and the floor. As sound waves arrive after undergoing more and more
+ * reflections, individual reflections become indistinguishable and the listener hears continuous
+ * reverberation that decays over time.
  * Reverb is vital for modeling a listener's environment. It can be used in music applications
  * to simulate music being played back in various environments, or in games to immerse the
  * listener within the game's environment.
  * The EnvironmentalReverb class allows an application to control each reverb engine property in a
  * global reverb environment and is more suitable for games. For basic control, more suitable for
  * music applications, it is recommended to use the
- * {@link android.media.PresetReverb} class.
+ * {@link android.media.audiofx.PresetReverb} class.
  * <p>An application creates a EnvironmentalReverb object to instantiate and control a reverb engine
  * in the audio framework.
  * <p>The methods, parameter types and units exposed by the EnvironmentalReverb implementation are
@@ -51,7 +51,9 @@
  * they must be explicitely attached to it and a send level must be specified. Use the effect ID
  * returned by getId() method to designate this particular effect when attaching it to the
  * MediaPlayer or AudioTrack.
- * <p> See {@link android.media.AudioEffect} class for more details on controlling
+ * <p>Creating a reverb on the output mix (audio session 0) requires permission
+ * {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS}
+ * <p>See {@link android.media.audiofx.AudioEffect} class for more details on controlling
  * audio effects.
  */
 
@@ -76,7 +78,7 @@
     public static final int PARAM_DECAY_TIME = 2;
     /**
      * Decay HF ratio. Parameter ID for
-     * {@link android.media.EnvironmentalReverb.OnParameterChangeListener}
+     * {@link android.media.audiofx.EnvironmentalReverb.OnParameterChangeListener}
      */
     public static final int PARAM_DECAY_HF_RATIO = 3;
     /**
@@ -444,7 +446,6 @@
          * EnvironmentalReverb engine.
          * @param effect the EnvironmentalReverb on which the interface is registered.
          * @param status status of the set parameter operation.
-         * See {@link android.media.AudioEffect#setParameter(byte[], byte[])}.
          * @param param ID of the modified parameter. See {@link #PARAM_ROOM_LEVEL} ...
          * @param value the new parameter value.
          */
diff --git a/media/java/android/media/Equalizer.java b/media/java/android/media/audiofx/Equalizer.java
similarity index 97%
rename from media/java/android/media/Equalizer.java
rename to media/java/android/media/audiofx/Equalizer.java
index 6fa48c5..b3bafa9 100644
--- a/media/java/android/media/Equalizer.java
+++ b/media/java/android/media/audiofx/Equalizer.java
@@ -14,12 +14,12 @@
  * limitations under the License.
  */
 
-package android.media;
+package android.media.audiofx;
 
 import android.app.Activity;
 import android.content.Context;
 import android.content.Intent;
-import android.media.AudioEffect;
+import android.media.audiofx.AudioEffect;
 import android.os.Bundle;
 import android.util.Log;
 
@@ -41,7 +41,11 @@
  * <p>To attach the Equalizer to a particular AudioTrack or MediaPlayer, specify the audio session
  * ID of this AudioTrack or MediaPlayer when constructing the Equalizer. If the audio session ID 0
  * is specified, the Equalizer applies to the main audio output mix.
- * <p> See {@link android.media.AudioEffect} class for more details on controlling audio effects.
+ * <p>Creating an Equalizer on the output mix (audio session 0) requires permission
+ * {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS}
+ * <p>See {@link android.media.MediaPlayer#getAudioSessionId()} for details on audio sessions.
+ * <p>See {@link android.media.audiofx.AudioEffect} class for more details on controlling audio
+ * effects.
  */
 
 public class Equalizer extends AudioEffect {
@@ -68,7 +72,7 @@
     public static final int PARAM_CENTER_FREQ = 3;
     /**
      * Band frequency range. Parameter ID for
-     * {@link android.media.Equalizer.OnParameterChangeListener}
+     * {@link android.media.audiofx.Equalizer.OnParameterChangeListener}
      */
     public static final int PARAM_BAND_FREQ_RANGE = 4;
     /**
@@ -380,7 +384,6 @@
          * Equalizer engine.
          * @param effect the Equalizer on which the interface is registered.
          * @param status status of the set parameter operation.
-         * See {@link android.media.AudioEffect#setParameter(byte[], byte[])}.
          * @param param1 ID of the modified parameter. See {@link #PARAM_BAND_LEVEL} ...
          * @param param2 additional parameter qualifier (e.g the band for band level parameter).
          * @param value the new parameter value.
diff --git a/media/java/android/media/PresetReverb.java b/media/java/android/media/audiofx/PresetReverb.java
similarity index 96%
rename from media/java/android/media/PresetReverb.java
rename to media/java/android/media/audiofx/PresetReverb.java
index 65175ff..7a89ae7 100644
--- a/media/java/android/media/PresetReverb.java
+++ b/media/java/android/media/audiofx/PresetReverb.java
@@ -14,12 +14,12 @@
  * limitations under the License.
  */
 
-package android.media;
+package android.media.audiofx;
 
 import android.app.Activity;
 import android.content.Context;
 import android.content.Intent;
-import android.media.AudioEffect;
+import android.media.audiofx.AudioEffect;
 import android.os.Bundle;
 import android.util.Log;
 
@@ -40,7 +40,7 @@
  * The PresetReverb class allows an application to configure the global reverb using a reverb preset.
  * This is primarily used for adding some reverb in a music playback context. Applications
  * requiring control over a more advanced environmental reverb are advised to use the
- * {@link android.media.EnvironmentalReverb} class.
+ * {@link android.media.audiofx.EnvironmentalReverb} class.
  * <p>An application creates a PresetReverb object to instantiate and control a reverb engine in the
  * audio framework.
  * <p>The methods, parameter types and units exposed by the PresetReverb implementation are
@@ -52,7 +52,10 @@
  * they must be explicitely attached to it and a send level must be specified. Use the effect ID
  * returned by getId() method to designate this particular effect when attaching it to the
  * MediaPlayer or AudioTrack.
- * <p> See {@link android.media.AudioEffect} class for more details on controlling audio effects.
+ * <p>Creating a reverb on the output mix (audio session 0) requires permission
+ * {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS}
+ * <p>See {@link android.media.audiofx.AudioEffect} class for more details on controlling
+ * audio effects.
  */
 
 public class PresetReverb extends AudioEffect {
@@ -64,7 +67,7 @@
 
     /**
      * Preset. Parameter ID for
-     * {@link android.media.PresetReverb.OnParameterChangeListener}
+     * {@link android.media.audiofx.PresetReverb.OnParameterChangeListener}
      */
     public static final int PARAM_PRESET = 0;
 
@@ -174,7 +177,6 @@
          * PresetReverb engine.
          * @param effect the PresetReverb on which the interface is registered.
          * @param status status of the set parameter operation.
-         * See {@link android.media.AudioEffect#setParameter(byte[], byte[])}.
          * @param param ID of the modified parameter. See {@link #PARAM_PRESET} ...
          * @param value the new parameter value.
          */
diff --git a/media/java/android/media/Virtualizer.java b/media/java/android/media/audiofx/Virtualizer.java
similarity index 95%
rename from media/java/android/media/Virtualizer.java
rename to media/java/android/media/audiofx/Virtualizer.java
index b08f36e..a682a45 100644
--- a/media/java/android/media/Virtualizer.java
+++ b/media/java/android/media/audiofx/Virtualizer.java
@@ -14,12 +14,12 @@
  * limitations under the License.
  */
 
-package android.media;
+package android.media.audiofx;
 
 import android.app.Activity;
 import android.content.Context;
 import android.content.Intent;
-import android.media.AudioEffect;
+import android.media.audiofx.AudioEffect;
 import android.os.Bundle;
 import android.util.Log;
 
@@ -42,7 +42,11 @@
  * <p>To attach the Virtualizer to a particular AudioTrack or MediaPlayer, specify the audio session
  * ID of this AudioTrack or MediaPlayer when constructing the Virtualizer. If the audio session ID 0
  * is specified, the Virtualizer applies to the main audio output mix.
- * <p> See {@link android.media.AudioEffect} class for more details on controlling audio effects.
+ * <p>Creating a Virtualizer on the output mix (audio session 0) requires permission
+ * {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS}
+ * <p>See {@link android.media.MediaPlayer#getAudioSessionId()} for details on audio sessions.
+ * <p>See {@link android.media.audiofx.AudioEffect} class for more details on controlling
+ * audio effects.
  */
 
 public class Virtualizer extends AudioEffect {
@@ -56,7 +60,7 @@
     public static final int PARAM_STRENGTH_SUPPORTED = 0;
     /**
      * Virtualizer effect strength. Parameter ID for
-     * {@link android.media.Virtualizer.OnParameterChangeListener}
+     * {@link android.media.audiofx.Virtualizer.OnParameterChangeListener}
      */
     public static final int PARAM_STRENGTH = 1;
 
@@ -156,7 +160,6 @@
          * Virtualizer engine.
          * @param effect the Virtualizer on which the interface is registered.
          * @param status status of the set parameter operation.
-         * See {@link android.media.AudioEffect#setParameter(byte[], byte[])}.
          * @param param ID of the modified parameter. See {@link #PARAM_STRENGTH} ...
          * @param value the new parameter value.
          */
diff --git a/media/java/android/media/Visualizer.java b/media/java/android/media/audiofx/Visualizer.java
similarity index 98%
rename from media/java/android/media/Visualizer.java
rename to media/java/android/media/audiofx/Visualizer.java
index 33222ff..0c48556 100755
--- a/media/java/android/media/Visualizer.java
+++ b/media/java/android/media/audiofx/Visualizer.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.media;
+package android.media.audiofx;
 
 import android.util.Log;
 import java.lang.ref.WeakReference;
@@ -32,8 +32,8 @@
  * visualized:<br>
  * <ul>
  *   <li>If the session is 0, the audio output mix is visualized</li>
- *   <li>If the session is not 0, the audio from a particular {@link MediaPlayer} or
- *   {@link AudioTrack}
+ *   <li>If the session is not 0, the audio from a particular {@link android.media.MediaPlayer} or
+ *   {@link android.media.AudioTrack}
  *   using this audio session is visualized </li>
  * </ul>
  * <p>Two types of representation of audio content can be captured: <br>
@@ -57,6 +57,8 @@
  * When data capture is not needed any more, the Visualizer should be disabled.
  * <p>It is good practice to call the {@link #release()} method when the Visualizer is not used
  * anymore to free up native resources associated to the Visualizer instance.
+ * <p>Creating a Visualizer on the output mix (audio session 0) requires permission
+ * {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS}
  */
 
 public class Visualizer {
diff --git a/media/java/android/media/audiofx/package.html b/media/java/android/media/audiofx/package.html
new file mode 100644
index 0000000..c6e4892
--- /dev/null
+++ b/media/java/android/media/audiofx/package.html
@@ -0,0 +1,5 @@
+<HTML>
+<BODY>
+Provides classes that manage audio effects implemented in the media framework.
+</BODY>
+</HTML>
diff --git a/media/java/android/media/videoeditor/Effect.java b/media/java/android/media/videoeditor/Effect.java
index 177b863..8c39577 100755
--- a/media/java/android/media/videoeditor/Effect.java
+++ b/media/java/android/media/videoeditor/Effect.java
@@ -84,7 +84,7 @@
      * Set start time of the effect. If a preview or export is in progress, then

      * this change is effective for next preview or export session.

      *

-     * @param startTimeMs The start time of the effect relative to the begining

+     * @param startTimeMs The start time of the effect relative to the beginning

      *            of the media item in milliseconds

      */

     public void setStartTime(long startTimeMs) {

diff --git a/media/java/android/media/videoeditor/MediaItem.java b/media/java/android/media/videoeditor/MediaItem.java
index 9e32744..e7be35d 100755
--- a/media/java/android/media/videoeditor/MediaItem.java
+++ b/media/java/android/media/videoeditor/MediaItem.java
@@ -64,8 +64,8 @@
     private int mRenderingMode;

 

     // Beginning and end transitions

-    private Transition mBeginTransition;

-    private Transition mEndTransition;

+    protected Transition mBeginTransition;

+    protected Transition mEndTransition;

 

     /**

      * Constructor

@@ -113,6 +113,13 @@
      */

     public void setRenderingMode(int renderingMode) {

         mRenderingMode = renderingMode;

+        if (mBeginTransition != null) {

+            mBeginTransition.invalidate();

+        }

+

+        if (mEndTransition != null) {

+            mEndTransition.invalidate();

+        }

     }

 

     /**

@@ -271,7 +278,9 @@
      *

      * @param overlay The overlay to add

      * @throws IllegalStateException if a preview or an export is in progress or

-     *             if the overlay id is not unique across all the overlays added.

+     *             if the overlay id is not unique across all the overlays

+     *             added or if the bitmap is not specified or if the dimensions of

+     *             the bitmap do not match the dimensions of the media item

      */

     public void addOverlay(Overlay overlay) {

         if (mOverlays.contains(overlay)) {

@@ -283,6 +292,23 @@
                     "Overlay start time + overlay duration > media clip duration");

         }

 

+        if (overlay instanceof OverlayFrame) {

+            final OverlayFrame frame = (OverlayFrame)overlay;

+            final Bitmap bitmap = frame.getBitmap();

+            if (bitmap == null) {

+                throw new IllegalArgumentException("Overlay bitmap not specified");

+            }

+

+            // The dimensions of the overlay bitmap must be the same as the

+            // media item dimensions

+            if (bitmap.getWidth() != getWidth() || bitmap.getHeight() != getHeight()) {

+                throw new IllegalArgumentException(

+                        "Bitmap dimensions must match media item dimensions");

+            }

+        } else {

+            throw new IllegalArgumentException("Overlay not supported");

+        }

+

         mOverlays.add(overlay);

         invalidateTransitions(overlay);

     }

@@ -302,6 +328,9 @@
         for (Overlay overlay : mOverlays) {

             if (overlay.getId().equals(overlayId)) {

                 mOverlays.remove(overlay);

+                if (overlay instanceof OverlayFrame) {

+                    ((OverlayFrame)overlay).invalidate();

+                }

                 invalidateTransitions(overlay);

                 return overlay;

             }

diff --git a/media/java/android/media/videoeditor/MediaVideoItem.java b/media/java/android/media/videoeditor/MediaVideoItem.java
index 87e9a22..af50b83 100755
--- a/media/java/android/media/videoeditor/MediaVideoItem.java
+++ b/media/java/android/media/videoeditor/MediaVideoItem.java
@@ -264,8 +264,19 @@
             throw new IllegalArgumentException("Invalid end time");

         }

 

-        mBeginBoundaryTimeMs = beginMs;

-        mEndBoundaryTimeMs = endMs;

+        if (beginMs != mBeginBoundaryTimeMs) {

+            mBeginBoundaryTimeMs = beginMs;

+            if (mBeginTransition != null) {

+                mBeginTransition.invalidate();

+            }

+        }

+

+        if (endMs == mEndBoundaryTimeMs) {

+            mEndBoundaryTimeMs = endMs;

+            if (mEndTransition != null) {

+                mEndTransition.invalidate();

+            }

+        }

         // TODO: Validate/modify the start and the end time of effects and overlays

     }

 

diff --git a/media/java/android/media/videoeditor/OverlayFrame.java b/media/java/android/media/videoeditor/OverlayFrame.java
index e5d9b81..3abac6c 100755
--- a/media/java/android/media/videoeditor/OverlayFrame.java
+++ b/media/java/android/media/videoeditor/OverlayFrame.java
@@ -16,16 +16,24 @@
 

 package android.media.videoeditor;

 

+import java.io.File;

+import java.io.FileNotFoundException;

+import java.io.FileOutputStream;

+import java.io.IOException;

+

+import android.graphics.Bitmap;

+import android.graphics.BitmapFactory;

+import android.graphics.Bitmap.CompressFormat;

+

 

 /**

- * This class is used to overlay an image on top of a media item. This class

- * does not manage deletion of the overlay file so application may use

- * {@link #getFilename()} for this purpose.

+ * This class is used to overlay an image on top of a media item.

  * {@hide}

  */

 public class OverlayFrame extends Overlay {

     // Instance variables

-    private final String mFilename;

+    private final Bitmap mBitmap;

+    private String mFilename;

 

     /**

      * An object of this type cannot be instantiated by using the default

@@ -33,30 +41,90 @@
      */

     @SuppressWarnings("unused")

     private OverlayFrame() {

-        this(null, null, 0, 0);

+        this(null, (String)null, 0, 0);

     }

 

     /**

      * Constructor for an OverlayFrame

      *

      * @param overlayId The overlay id

-     * @param filename The file name that contains the overlay. Only PNG

-     *            supported.

+     * @param bitmap The bitmap to be used as an overlay. The size of the

+     *      bitmap must equal to the size of the media item to which it is

+     *      added. The bitmap is typically a decoded PNG file.

      * @param startTimeMs The overlay start time in milliseconds

      * @param durationMs The overlay duration in milliseconds

      *

      * @throws IllegalArgumentException if the file type is not PNG or the

      *      startTimeMs and durationMs are incorrect.

      */

-    public OverlayFrame(String overlayId, String filename, long startTimeMs, long durationMs) {

+    public OverlayFrame(String overlayId, Bitmap bitmap, long startTimeMs,

+            long durationMs) {

+        super(overlayId, startTimeMs, durationMs);

+        mBitmap = bitmap;

+        mFilename = null;

+    }

+

+    /**

+     * Constructor for an OverlayFrame. This constructor can be used to

+     * restore the overlay after it was saved internally by the video editor.

+     *

+     * @param overlayId The overlay id

+     * @param filename The file name that contains the overlay.

+     * @param startTimeMs The overlay start time in milliseconds

+     * @param durationMs The overlay duration in milliseconds

+     *

+     * @throws IllegalArgumentException if the file type is not PNG or the

+     *      startTimeMs and durationMs are incorrect.

+     */

+    OverlayFrame(String overlayId, String filename, long startTimeMs, long durationMs) {

         super(overlayId, startTimeMs, durationMs);

         mFilename = filename;

+        mBitmap = BitmapFactory.decodeFile(mFilename);

+    }

+

+    /**

+     * @return Get the overlay bitmap

+     */

+    public Bitmap getBitmap() {

+        return mBitmap;

     }

 

     /**

      * Get the file name of this overlay

      */

-    public String getFilename() {

+    String getFilename() {

         return mFilename;

     }

+

+    /**

+     * Save the overlay to the project folder

+     *

+     * @param editor The video editor

+     *

+     * @return

+     * @throws FileNotFoundException if the bitmap cannot be saved

+     * @throws IOException if the bitmap file cannot be saved

+     */

+    String save(VideoEditor editor) throws FileNotFoundException, IOException {

+        if (mFilename != null) {

+            return mFilename;

+        }

+

+        mFilename = editor.getPath() + "/" + getId() + ".png";

+        // Save the image to a local file

+        final FileOutputStream out = new FileOutputStream(mFilename);

+        mBitmap.compress(CompressFormat.PNG, 100, out);

+        out.flush();

+        out.close();

+        return mFilename;

+    }

+

+    /**

+     * Delete the overlay file

+     */

+    void invalidate() {

+        if (mFilename != null) {

+            new File(mFilename).delete();

+        }

+    }

 }

diff --git a/media/java/android/media/videoeditor/TransitionAlpha.java b/media/java/android/media/videoeditor/TransitionAlpha.java
index 0a4a12f..30e66fc 100755
--- a/media/java/android/media/videoeditor/TransitionAlpha.java
+++ b/media/java/android/media/videoeditor/TransitionAlpha.java
@@ -58,17 +58,19 @@
      * Constructor

      *

      * @param transitionId The transition id

-     * @param afterMediaItem The transition is applied to the end of this

-     *      media item

-     * @param beforeMediaItem The transition is applied to the beginning of

-     *      this media item

+     * @param afterMediaItem The transition is applied to the end of this media

+     *            item

+     * @param beforeMediaItem The transition is applied to the beginning of this

+     *            media item

      * @param durationMs duration of the transition in milliseconds

      * @param behavior behavior is one of the behavior defined in Transition

      *            class

-     * @param maskFilename JPEG file name

+     * @param maskFilename JPEG file name. The dimension of the image

+     *           corresponds to 720p (16:9 aspect ratio). Mask files are

+     *           shared between video editors and can be created in the

+     *           projects folder (the parent folder for all projects).

      * @param blendingPercent The blending percent applied

      * @param invert true to invert the direction of the alpha blending

-     *

      * @throws IllegalArgumentException if behavior is not supported, or if

      *             direction are not supported.

      */

diff --git a/media/java/android/media/videoeditor/VideoEditorTestImpl.java b/media/java/android/media/videoeditor/VideoEditorTestImpl.java
index 14e2658..7226d5d 100644
--- a/media/java/android/media/videoeditor/VideoEditorTestImpl.java
+++ b/media/java/android/media/videoeditor/VideoEditorTestImpl.java
@@ -48,8 +48,8 @@
     private static final String TAG_PROJECT = "project";
     private static final String TAG_MEDIA_ITEMS = "media_items";
     private static final String TAG_MEDIA_ITEM = "media_item";
-    private static final String TAG_BEGIN_TRANSITION = "begin_transition";
-    private static final String TAG_END_TRANSITION = "end_transition";
+    private static final String TAG_TRANSITIONS = "transitions";
+    private static final String TAG_TRANSITION = "transition";
     private static final String ATTR_ID = "id";
     private static final String ATTR_FILENAME = "filename";
     private static final String ATTR_AUDIO_WAVEFORM_FILENAME = "wavefoem";
@@ -65,6 +65,8 @@
     private static final String ATTR_BLENDING = "blending";
     private static final String ATTR_INVERT = "invert";
     private static final String ATTR_MASK = "mask";
+    private static final String ATTR_BEFORE_MEDIA_ITEM_ID = "before_media_item";
+    private static final String ATTR_AFTER_MEDIA_ITEM_ID = "after_media_item";
 
     // Instance variables
     private long mDurationMs;
@@ -400,6 +402,7 @@
             }
             beforeMediaItem.setBeginTransition(transition);
         }
+
         computeTimelineDuration();
     }
 
@@ -412,22 +415,25 @@
         }
 
         final Transition transition = getTransition(transitionId);
-        if (transition != null) {
-            mTransitions.remove(transition);
-            transition.invalidate();
-            computeTimelineDuration();
+        if (transition == null) {
+            throw new IllegalStateException("Transition not found: " + transitionId);
         }
 
-        // Cross reference the transitions
+        // Remove the transition references
         final MediaItem afterMediaItem = transition.getAfterMediaItem();
         if (afterMediaItem != null) {
             afterMediaItem.setEndTransition(null);
         }
+
         final MediaItem beforeMediaItem = transition.getBeforeMediaItem();
         if (beforeMediaItem != null) {
             beforeMediaItem.setBeginTransition(null);
         }
 
+        mTransitions.remove(transition);
+        transition.invalidate();
+        computeTimelineDuration();
+
         return transition;
     }
 
@@ -543,15 +549,14 @@
         serializer.startTag("", TAG_PROJECT);
         serializer.attribute("", ATTR_ASPECT_RATIO, Integer.toString(mAspectRatio));
 
-        boolean firstMediaItem = true;
         serializer.startTag("", TAG_MEDIA_ITEMS);
         for (MediaItem mediaItem : mMediaItems) {
             serializer.startTag("", TAG_MEDIA_ITEM);
             serializer.attribute("", ATTR_ID, mediaItem.getId());
             serializer.attribute("", ATTR_TYPE, mediaItem.getClass().getSimpleName());
             serializer.attribute("", ATTR_FILENAME, mediaItem.getFilename());
-            serializer.attribute("", ATTR_RENDERING_MODE, Integer.toString(mediaItem
-                    .getRenderingMode()));
+            serializer.attribute("", ATTR_RENDERING_MODE, Integer.toString(
+                    mediaItem.getRenderingMode()));
             if (mediaItem instanceof MediaVideoItem) {
                 final MediaVideoItem mvi = (MediaVideoItem)mediaItem;
                 serializer
@@ -559,55 +564,50 @@
                 serializer.attribute("", ATTR_END_TIME, Long.toString(mvi.getBoundaryEndTime()));
                 serializer.attribute("", ATTR_VOLUME, Integer.toString(mvi.getVolume()));
                 if (mvi.getAudioWaveformFilename() != null) {
-                    serializer.attribute("", ATTR_AUDIO_WAVEFORM_FILENAME, mvi
-                            .getAudioWaveformFilename());
+                    serializer.attribute("", ATTR_AUDIO_WAVEFORM_FILENAME,
+                            mvi.getAudioWaveformFilename());
                 }
             } else if (mediaItem instanceof MediaImageItem) {
                 serializer.attribute("", ATTR_DURATION, Long.toString(mediaItem.getDuration()));
             }
 
-            if (firstMediaItem) {
-                firstMediaItem = false;
-                final Transition beginTransition = mediaItem.getBeginTransition();
-                if (beginTransition != null) {
-                    serializer.startTag("", TAG_BEGIN_TRANSITION);
-                    serializer.attribute("", ATTR_ID, beginTransition.getId());
-                    serializer.attribute("", ATTR_TYPE, beginTransition.getClass()
-                            .getSimpleName());
-                    serializer.attribute("", ATTR_DURATION, Long.toString(beginTransition
-                            .getDuration()));
-                    serializer.attribute("", ATTR_BEHAVIOR, Integer.toString(beginTransition
-                            .getBehavior()));
-                    serializer.endTag("", TAG_BEGIN_TRANSITION);
-                }
-            }
-
-            final Transition endTransition = mediaItem.getEndTransition();
-            if (endTransition != null) {
-                serializer.startTag("", TAG_END_TRANSITION);
-                serializer.attribute("", ATTR_ID, endTransition.getId());
-                serializer.attribute("", ATTR_TYPE, endTransition.getClass().getSimpleName());
-                serializer.attribute("", ATTR_DURATION, Long.toString(endTransition
-                        .getDuration()));
-                serializer.attribute("", ATTR_BEHAVIOR, Integer.toString(endTransition
-                        .getBehavior()));
-                if (endTransition instanceof TransitionSliding) {
-                    serializer.attribute("", ATTR_DIRECTION, Integer
-                            .toString(((TransitionSliding)endTransition).getDirection()));
-                } else if (endTransition instanceof TransitionAlpha) {
-                    TransitionAlpha ta = (TransitionAlpha)endTransition;
-                    serializer.attribute("", ATTR_BLENDING, Integer.toString(ta
-                            .getBlendingPercent()));
-                    serializer.attribute("", ATTR_INVERT, Boolean.toString(ta.isInvert()));
-                    serializer.attribute("", ATTR_MASK, ta.getMaskFilename());
-                }
-                serializer.endTag("", TAG_END_TRANSITION);
-            }
-
             serializer.endTag("", TAG_MEDIA_ITEM);
         }
         serializer.endTag("", TAG_MEDIA_ITEMS);
 
+        serializer.startTag("", TAG_TRANSITIONS);
+
+        for (Transition transition : mTransitions) {
+            serializer.startTag("", TAG_TRANSITION);
+            serializer.attribute("", ATTR_ID, transition.getId());
+            serializer.attribute("", ATTR_TYPE, transition.getClass().getSimpleName());
+            serializer.attribute("", ATTR_DURATION, Long.toString(transition.getDuration()));
+            serializer.attribute("", ATTR_BEHAVIOR, Integer.toString(transition.getBehavior()));
+            final MediaItem afterMediaItem = transition.getAfterMediaItem();
+            if (afterMediaItem != null) {
+                serializer.attribute("", ATTR_AFTER_MEDIA_ITEM_ID, afterMediaItem.getId());
+            }
+
+            final MediaItem beforeMediaItem = transition.getBeforeMediaItem();
+            if (beforeMediaItem != null) {
+                serializer.attribute("", ATTR_BEFORE_MEDIA_ITEM_ID, beforeMediaItem.getId());
+            }
+
+            if (transition instanceof TransitionSliding) {
+                serializer.attribute("", ATTR_DIRECTION,
+                        Integer.toString(((TransitionSliding)transition).getDirection()));
+            } else if (transition instanceof TransitionAlpha) {
+                TransitionAlpha ta = (TransitionAlpha)transition;
+                serializer.attribute("", ATTR_BLENDING, Integer.toString(ta.getBlendingPercent()));
+                serializer.attribute("", ATTR_INVERT, Boolean.toString(ta.isInvert()));
+                if (ta.getMaskFilename() != null) {
+                    serializer.attribute("", ATTR_MASK, ta.getMaskFilename());
+                }
+            }
+            serializer.endTag("", TAG_TRANSITION);
+        }
+        serializer.endTag("", TAG_TRANSITIONS);
+
         serializer.endTag("", TAG_PROJECT);
         serializer.endDocument();
 
@@ -628,8 +628,6 @@
         parser.setInput(new FileInputStream(file), "UTF-8");
         int eventType = parser.getEventType();
         String name;
-        MediaItem currentMediaItem = null;
-        MediaItem previousMediaItem = null;
         while (eventType != XmlPullParser.END_DOCUMENT) {
             switch (eventType) {
                 case XmlPullParser.START_TAG: {
@@ -644,6 +642,7 @@
                         final int renderingMode = Integer.parseInt(parser.getAttributeValue("",
                                 ATTR_RENDERING_MODE));
 
+                        MediaItem currentMediaItem;
                         if (MediaImageItem.class.getSimpleName().equals(type)) {
                             final long durationMs = Long.parseLong(parser.getAttributeValue("",
                                     ATTR_DURATION));
@@ -671,29 +670,13 @@
                         }
 
                         if (currentMediaItem != null) {
-                            if (previousMediaItem != null) {
-                                currentMediaItem.setBeginTransition(previousMediaItem
-                                        .getEndTransition());
-                            }
                             mMediaItems.add(currentMediaItem);
                         }
-                    } else if (name.equals(TAG_BEGIN_TRANSITION)) {
-                        final Transition transition = parseTransition(parser, currentMediaItem,
-                                null);
-                        currentMediaItem.setBeginTransition(transition);
-                    } else if (name.equals(TAG_END_TRANSITION)) {
-                        final Transition transition = parseTransition(parser, previousMediaItem,
-                                currentMediaItem);
-                        currentMediaItem.setEndTransition(transition);
-                    }
-                    break;
-                }
-
-                case XmlPullParser.END_TAG: {
-                    name = parser.getName();
-                    if (name.equals(TAG_MEDIA_ITEM)) {
-                        previousMediaItem = currentMediaItem;
-                        currentMediaItem = null;
+                    } else if (name.equals(TAG_TRANSITION)) {
+                        final Transition transition = parseTransition(parser);
+                        if (transition != null) {
+                            mTransitions.add(transition);
+                        }
                     }
                     break;
                 }
@@ -712,47 +695,72 @@
      * Parse the transition
      *
      * @param parser The parser
-     * @param afterMediaItem The transition is at the end of this media item
-     * @param beforeMediaItem The transition is at the beginning of this media
-     *            item
      * @return The transition
      */
-    private Transition parseTransition(XmlPullParser parser, MediaItem beforeMediaItem,
-            MediaItem afterMediaItem) {
+    private Transition parseTransition(XmlPullParser parser) {
         final String transitionId = parser.getAttributeValue("", ATTR_ID);
         final String type = parser.getAttributeValue("", ATTR_TYPE);
         final long durationMs = Long.parseLong(parser.getAttributeValue("", ATTR_DURATION));
         final int behavior = Integer.parseInt(parser.getAttributeValue("", ATTR_BEHAVIOR));
+
+        final String beforeMediaItemId = parser.getAttributeValue("", ATTR_BEFORE_MEDIA_ITEM_ID);
+        final MediaItem beforeMediaItem;
+        if (beforeMediaItemId != null) {
+            beforeMediaItem = getMediaItem(beforeMediaItemId);
+        } else {
+            beforeMediaItem = null;
+        }
+
+        final String afterMediaItemId = parser.getAttributeValue("", ATTR_AFTER_MEDIA_ITEM_ID);
+        final MediaItem afterMediaItem;
+        if (afterMediaItemId != null) {
+            afterMediaItem = getMediaItem(afterMediaItemId);
+        } else {
+            afterMediaItem = null;
+        }
+
+        final Transition transition;
         if (TransitionStartCurtainOpening.class.getSimpleName().equals(type)) {
-            return new TransitionStartCurtainOpening(transitionId, beforeMediaItem, durationMs,
-                    behavior);
+            transition = new TransitionStartCurtainOpening(transitionId, beforeMediaItem,
+                    durationMs, behavior);
         } else if (TransitionStartFadeFromBlack.class.getSimpleName().equals(type)) {
-            return new TransitionStartFadeFromBlack(transitionId, beforeMediaItem, durationMs,
-                    behavior);
+            transition = new TransitionStartFadeFromBlack(transitionId, beforeMediaItem,
+                    durationMs, behavior);
         } else if (TransitionAlpha.class.getSimpleName().equals(type)) {
             final int blending = Integer.parseInt(parser.getAttributeValue("", ATTR_BLENDING));
             final String maskFilename = parser.getAttributeValue("", ATTR_MASK);
             final boolean invert = Boolean.getBoolean(parser.getAttributeValue("", ATTR_INVERT));
-            return new TransitionAlpha(transitionId, afterMediaItem, beforeMediaItem, durationMs,
-                    behavior, maskFilename, blending, invert);
-        } else if (TransitionAlpha.class.getSimpleName().equals(type)) {
-            return new TransitionCrossfade(transitionId, afterMediaItem, beforeMediaItem,
+            transition = new TransitionAlpha(transitionId, afterMediaItem, beforeMediaItem,
+                    durationMs, behavior, maskFilename, blending, invert);
+        } else if (TransitionCrossfade.class.getSimpleName().equals(type)) {
+            transition = new TransitionCrossfade(transitionId, afterMediaItem, beforeMediaItem,
                     durationMs, behavior);
         } else if (TransitionSliding.class.getSimpleName().equals(type)) {
             final int direction = Integer.parseInt(parser.getAttributeValue("", ATTR_DIRECTION));
-            return new TransitionSliding(transitionId, afterMediaItem, beforeMediaItem, durationMs,
-                    behavior, direction);
+            transition = new TransitionSliding(transitionId, afterMediaItem, beforeMediaItem,
+                    durationMs, behavior, direction);
         } else if (TransitionFadeToBlack.class.getSimpleName().equals(type)) {
-            return new TransitionFadeToBlack(transitionId, afterMediaItem, beforeMediaItem,
+            transition = new TransitionFadeToBlack(transitionId, afterMediaItem, beforeMediaItem,
                     durationMs, behavior);
         } else if (TransitionEndCurtainClosing.class.getSimpleName().equals(type)) {
-            return new TransitionEndCurtainClosing(transitionId, beforeMediaItem, durationMs,
+            transition = new TransitionEndCurtainClosing(transitionId, afterMediaItem, durationMs,
                     behavior);
         } else if (TransitionEndFadeToBlack.class.getSimpleName().equals(type)) {
-            return new TransitionEndFadeToBlack(transitionId, beforeMediaItem, durationMs, behavior);
+            transition = new TransitionEndFadeToBlack(transitionId, afterMediaItem, durationMs,
+                    behavior);
+        } else {
+            transition = null;
         }
 
-        return null;
+        if (beforeMediaItem != null) {
+            beforeMediaItem.setBeginTransition(transition);
+        }
+
+        if (afterMediaItem != null) {
+            afterMediaItem.setEndTransition(transition);
+        }
+
+        return transition;
     }
 
     public void cancelExport(String filename) {
diff --git a/media/jni/android_media_MtpDatabase.cpp b/media/jni/android_media_MtpDatabase.cpp
index 1842cb2..227ee92 100644
--- a/media/jni/android_media_MtpDatabase.cpp
+++ b/media/jni/android_media_MtpDatabase.cpp
@@ -672,12 +672,15 @@
 };
 
 static const PropertyTableEntry   kObjectPropertyTable[] = {
-    {   MTP_PROPERTY_PARENT_OBJECT,     MTP_TYPE_UINT32 },
-    {   MTP_PROPERTY_STORAGE_ID,        MTP_TYPE_UINT32 },
-    {   MTP_PROPERTY_OBJECT_FORMAT,     MTP_TYPE_UINT16 },
-    {   MTP_PROPERTY_OBJECT_FILE_NAME,  MTP_TYPE_STR    },
-    {   MTP_PROPERTY_OBJECT_SIZE,       MTP_TYPE_UINT64 },
-    {   MTP_PROPERTY_DATE_MODIFIED,     MTP_TYPE_STR    },
+    {   MTP_PROPERTY_STORAGE_ID,        MTP_TYPE_UINT32     },
+    {   MTP_PROPERTY_OBJECT_FORMAT,     MTP_TYPE_UINT16     },
+    {   MTP_PROPERTY_PROTECTION_STATUS, MTP_TYPE_UINT16     },
+    {   MTP_PROPERTY_OBJECT_SIZE,       MTP_TYPE_UINT64     },
+    {   MTP_PROPERTY_OBJECT_FILE_NAME,  MTP_TYPE_STR        },
+    {   MTP_PROPERTY_DATE_MODIFIED,     MTP_TYPE_STR        },
+    {   MTP_PROPERTY_PARENT_OBJECT,     MTP_TYPE_UINT32     },
+    {   MTP_PROPERTY_PERSISTENT_UID,    MTP_TYPE_UINT128    },
+    {   MTP_PROPERTY_NAME,              MTP_TYPE_STR        },
 };
 
 static const PropertyTableEntry   kDevicePropertyTable[] = {
@@ -764,6 +767,7 @@
         case MTP_PROPERTY_PERSISTENT_UID:
             result = new MtpProperty(property, MTP_TYPE_UINT128);
             break;
+        case MTP_PROPERTY_NAME:
         case MTP_PROPERTY_OBJECT_FILE_NAME:
         case MTP_PROPERTY_DATE_MODIFIED:
             result = new MtpProperty(property, MTP_TYPE_STR);
@@ -780,6 +784,7 @@
         case MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME:
             // writeable string properties
             result = new MtpProperty(property, MTP_TYPE_STR, true);
+            // FIXME - set current value here!
             break;
     }
 
diff --git a/media/jni/audioeffect/android_media_AudioEffect.cpp b/media/jni/audioeffect/android_media_AudioEffect.cpp
index 8d9f4fe..b16372d 100644
--- a/media/jni/audioeffect/android_media_AudioEffect.cpp
+++ b/media/jni/audioeffect/android_media_AudioEffect.cpp
@@ -37,7 +37,7 @@
 #define AUDIOEFFECT_ERROR_DEAD_OBJECT           -7
 
 // ----------------------------------------------------------------------------
-static const char* const kClassPathName = "android/media/AudioEffect";
+static const char* const kClassPathName = "android/media/audiofx/AudioEffect";
 
 struct fields_t {
     // these fields provide access from C++ to the...
@@ -228,9 +228,9 @@
         return;
     }
 
-    clazz = env->FindClass("android/media/AudioEffect$Descriptor");
+    clazz = env->FindClass("android/media/audiofx/AudioEffect$Descriptor");
     if (clazz == NULL) {
-        LOGE("Can't find android/media/AudioEffect$Descriptor class");
+        LOGE("Can't find android/media/audiofx/AudioEffect$Descriptor class");
         return;
     }
     fields.clazzDesc = (jclass)env->NewGlobalRef(clazz);
@@ -241,7 +241,7 @@
                     "<init>",
                     "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
     if (fields.midDescCstor == NULL) {
-        LOGE("Can't find android/media/AudioEffect$Descriptor class constructor");
+        LOGE("Can't find android/media/audiofx/AudioEffect$Descriptor class constructor");
         return;
     }
 }
diff --git a/media/jni/audioeffect/android_media_Visualizer.cpp b/media/jni/audioeffect/android_media_Visualizer.cpp
index 31119f8..7b271ce 100644
--- a/media/jni/audioeffect/android_media_Visualizer.cpp
+++ b/media/jni/audioeffect/android_media_Visualizer.cpp
@@ -40,7 +40,7 @@
 #define NATIVE_EVENT_FFT_CAPTURE                1
 
 // ----------------------------------------------------------------------------
-static const char* const kClassPathName = "android/media/Visualizer";
+static const char* const kClassPathName = "android/media/audiofx/Visualizer";
 
 struct fields_t {
     // these fields provide access from C++ to the...
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index f0d8943..ba1e218 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -2111,7 +2111,15 @@
                       mOwner->endBox();  // d263
                   } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
                       CHECK(mCodecSpecificData);
-                      CHECK(mCodecSpecificDataSize > 0);
+                      CHECK(mCodecSpecificDataSize >= 5);
+
+                      // Patch avcc's lengthSize field to match the number
+                      // of bytes we use to indicate the size of a nal unit.
+                      uint8_t *ptr = (uint8_t *)mCodecSpecificData;
+                      ptr[4] =
+                          (ptr[4] & 0xfc)
+                            | (mOwner->useNalLengthFour() ? 3 : 1);
+
                       mOwner->beginBox("avcC");
                         mOwner->write(mCodecSpecificData, mCodecSpecificDataSize);
                       mOwner->endBox();  // avcC
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/mtp/MtpCursor.cpp b/media/mtp/MtpCursor.cpp
index 8c964b4..865a294 100644
--- a/media/mtp/MtpCursor.cpp
+++ b/media/mtp/MtpCursor.cpp
@@ -66,7 +66,8 @@
 #define OBJECT_THUMB                221
 
 MtpCursor::MtpCursor(MtpClient* client, int queryType, int deviceID,
-                int storageID, int objectID, int columnCount, int* columns)
+                MtpStorageID storageID, MtpObjectHandle objectID,
+                int columnCount, int* columns)
         :   mClient(client),
             mQueryType(queryType),
             mDeviceID(deviceID),
@@ -427,7 +428,8 @@
     return true;
 }
 
-bool MtpCursor::putThumbnail(CursorWindow* window, int objectID, int format, int row, int column) {
+bool MtpCursor::putThumbnail(CursorWindow* window, MtpObjectHandle objectID,
+                            MtpObjectFormat format, int row, int column) {
     MtpDevice* device = mClient->getDevice(mDeviceID);
     void* thumbnail;
     int size, offset;
diff --git a/media/mtp/MtpCursor.h b/media/mtp/MtpCursor.h
index 3f84753..9e9833f 100644
--- a/media/mtp/MtpCursor.h
+++ b/media/mtp/MtpCursor.h
@@ -36,17 +36,18 @@
         OBJECT_CHILDREN     = 8,
     };
 
-    MtpClient*  mClient;
-    int         mQueryType;
-    int         mDeviceID;
-    int         mStorageID;
-    int         mQbjectID;
-    int         mColumnCount;
-    int*        mColumns;
+    MtpClient*      mClient;
+    int             mQueryType;
+    int             mDeviceID;
+    MtpStorageID    mStorageID;
+    MtpObjectHandle mQbjectID;
+    int             mColumnCount;
+    int*            mColumns;
 
 public:
                 MtpCursor(MtpClient* client, int queryType, int deviceID,
-                        int storageID, int objectID, int columnCount, int* columns);
+                        MtpStorageID storageID, MtpObjectHandle objectID,
+                        int columnCount, int* columns);
     virtual     ~MtpCursor();
 
     int         fillWindow(CursorWindow* window, int startPos);
@@ -68,7 +69,8 @@
     bool        prepareRow(CursorWindow* window);
     bool        putLong(CursorWindow* window, int value, int row, int column);
     bool        putString(CursorWindow* window, const char* text, int row, int column);
-    bool        putThumbnail(CursorWindow* window, int objectID, int format, int row, int column);
+    bool        putThumbnail(CursorWindow* window, MtpObjectHandle objectID,
+                            MtpObjectFormat format, int row, int column);
 };
 
 }; // namespace android
diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp
index 6332b4e..84a3e2c 100644
--- a/media/mtp/MtpServer.cpp
+++ b/media/mtp/MtpServer.cpp
@@ -26,6 +26,8 @@
 
 #include <cutils/properties.h>
 
+#define LOG_TAG "MtpServer"
+
 #include "MtpDebug.h"
 #include "MtpDatabase.h"
 #include "MtpProperty.h"
@@ -68,8 +70,8 @@
 //    MTP_OPERATION_INITIATE_OPEN_CAPTURE,
     MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED,
     MTP_OPERATION_GET_OBJECT_PROP_DESC,
-//    MTP_OPERATION_GET_OBJECT_PROP_VALUE,
-//    MTP_OPERATION_SET_OBJECT_PROP_VALUE,
+    MTP_OPERATION_GET_OBJECT_PROP_VALUE,
+    MTP_OPERATION_SET_OBJECT_PROP_VALUE,
     MTP_OPERATION_GET_OBJECT_REFERENCES,
     MTP_OPERATION_SET_OBJECT_REFERENCES,
 //    MTP_OPERATION_SKIP,
@@ -294,6 +296,7 @@
             response = doGetDevicePropDesc();
             break;
         default:
+            LOGE("got unsupported command %s", MtpDebug::getOperationCodeName(operation));
             response = MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
             break;
     }
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/EnergyProbe.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/EnergyProbe.java
index d339e06..4e4df3b 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/EnergyProbe.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/EnergyProbe.java
@@ -16,7 +16,7 @@
 
 package com.android.mediaframeworktest.functional;
 
-import android.media.Visualizer;
+import android.media.audiofx.Visualizer;
 import android.util.Log;
 
 /**
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaAudioEffectTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaAudioEffectTest.java
index fd939ae..34025f69e 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaAudioEffectTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaAudioEffectTest.java
@@ -19,12 +19,12 @@
 import com.android.mediaframeworktest.MediaFrameworkTest;
 import com.android.mediaframeworktest.MediaNames;
 import android.content.res.AssetFileDescriptor;
-import android.media.AudioEffect;
+import android.media.audiofx.AudioEffect;
 import android.media.AudioFormat;
 import android.media.AudioManager;
 import android.media.AudioTrack;
-import android.media.EnvironmentalReverb;
-import android.media.Equalizer;
+import android.media.audiofx.EnvironmentalReverb;
+import android.media.audiofx.Equalizer;
 import android.media.MediaPlayer;
 
 import android.os.Looper;
@@ -103,14 +103,14 @@
         boolean hasEnvReverb = false;
 
         for (int i = 0; i < desc.length; i++) {
-            if (desc[i].mType.equals(AudioEffect.EFFECT_TYPE_EQUALIZER)) {
+            if (desc[i].type.equals(AudioEffect.EFFECT_TYPE_EQUALIZER)) {
                 hasEQ = true;
-            } if (desc[i].mType.equals(AudioEffect.EFFECT_TYPE_BASS_BOOST)) {
+            } if (desc[i].type.equals(AudioEffect.EFFECT_TYPE_BASS_BOOST)) {
                 hasBassBoost = true;
-            } else if (desc[i].mType.equals(AudioEffect.EFFECT_TYPE_VIRTUALIZER)) {
+            } else if (desc[i].type.equals(AudioEffect.EFFECT_TYPE_VIRTUALIZER)) {
                 hasVirtualizer = true;
             }
-            else if (desc[i].mType.equals(AudioEffect.EFFECT_TYPE_ENV_REVERB)) {
+            else if (desc[i].type.equals(AudioEffect.EFFECT_TYPE_ENV_REVERB)) {
                 hasEnvReverb = true;
             }
         }
@@ -132,7 +132,7 @@
         AudioEffect.Descriptor[] desc = AudioEffect.queryEffects();
         assertTrue(msg+": no effects found", (desc.length != 0));
         try {
-            AudioEffect effect = new AudioEffect(desc[0].mType,
+            AudioEffect effect = new AudioEffect(desc[0].type,
                     AudioEffect.EFFECT_TYPE_NULL,
                     0,
                     0);
@@ -146,7 +146,7 @@
                 effect.release();
             }
         } catch (IllegalArgumentException e) {
-            msg = msg.concat(": Effect not found: "+desc[0].mName);
+            msg = msg.concat(": Effect not found: "+desc[0].name);
             result = false;
         } catch (UnsupportedOperationException e) {
             msg = msg.concat(": Effect library not loaded");
@@ -164,13 +164,13 @@
         assertTrue(msg+"no effects found", (desc.length != 0));
         try {
             AudioEffect effect = new AudioEffect(AudioEffect.EFFECT_TYPE_NULL,
-                    desc[0].mUuid,
+                    desc[0].uuid,
                     0,
                     0);
             assertNotNull(msg + ": could not create AudioEffect", effect);
             effect.release();
         } catch (IllegalArgumentException e) {
-            msg = msg.concat(": Effect not found: "+desc[0].mName);
+            msg = msg.concat(": Effect not found: "+desc[0].name);
             result = false;
         } catch (UnsupportedOperationException e) {
             msg = msg.concat(": Effect library not loaded");
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 b5b1c3e..aa5c4d7 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaBassBoostTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaBassBoostTest.java
@@ -20,10 +20,10 @@
 import com.android.mediaframeworktest.MediaNames;
 import android.content.Context;
 import android.content.res.AssetFileDescriptor;
-import android.media.AudioEffect;
+import android.media.audiofx.AudioEffect;
 import android.media.AudioManager;
-import android.media.BassBoost;
-import android.media.Visualizer;
+import android.media.audiofx.BassBoost;
+import android.media.audiofx.Visualizer;
 import android.media.MediaPlayer;
 
 import android.os.Looper;
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 d5538f1..ba202a7 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEnvReverbTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEnvReverbTest.java
@@ -20,10 +20,10 @@
 import com.android.mediaframeworktest.MediaNames;
 import android.content.Context;
 import android.content.res.AssetFileDescriptor;
-import android.media.AudioEffect;
+import android.media.audiofx.AudioEffect;
 import android.media.AudioManager;
-import android.media.EnvironmentalReverb;
-import android.media.Visualizer;
+import android.media.audiofx.EnvironmentalReverb;
+import android.media.audiofx.Visualizer;
 import android.media.MediaPlayer;
 
 import android.os.Looper;
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 a78668c..9146fb8 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEqualizerTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEqualizerTest.java
@@ -20,10 +20,10 @@
 import com.android.mediaframeworktest.MediaNames;
 import android.content.Context;
 import android.content.res.AssetFileDescriptor;
-import android.media.AudioEffect;
+import android.media.audiofx.AudioEffect;
 import android.media.AudioManager;
-import android.media.Equalizer;
-import android.media.Visualizer;
+import android.media.audiofx.Equalizer;
+import android.media.audiofx.Visualizer;
 import android.media.MediaPlayer;
 
 import android.os.Looper;
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 fbd8a78..242e6bb 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPresetReverbTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPresetReverbTest.java
@@ -20,10 +20,10 @@
 import com.android.mediaframeworktest.MediaNames;
 import android.content.Context;
 import android.content.res.AssetFileDescriptor;
-import android.media.AudioEffect;
+import android.media.audiofx.AudioEffect;
 import android.media.AudioManager;
-import android.media.PresetReverb;
-import android.media.Visualizer;
+import android.media.audiofx.PresetReverb;
+import android.media.audiofx.Visualizer;
 import android.media.MediaPlayer;
 
 import android.os.Looper;
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 7123db4..7a35429 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVirtualizerTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVirtualizerTest.java
@@ -20,10 +20,10 @@
 import com.android.mediaframeworktest.MediaNames;
 import android.content.Context;
 import android.content.res.AssetFileDescriptor;
-import android.media.AudioEffect;
+import android.media.audiofx.AudioEffect;
 import android.media.AudioManager;
-import android.media.Virtualizer;
-import android.media.Visualizer;
+import android.media.audiofx.Virtualizer;
+import android.media.audiofx.Visualizer;
 import android.media.MediaPlayer;
 
 import android.os.Looper;
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVisualizerTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVisualizerTest.java
index 26fdbfe..542ca8d 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVisualizerTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVisualizerTest.java
@@ -20,9 +20,9 @@
 import com.android.mediaframeworktest.MediaNames;
 import android.content.Context;
 import android.content.res.AssetFileDescriptor;
-import android.media.AudioEffect;
+import android.media.audiofx.AudioEffect;
 import android.media.AudioManager;
-import android.media.Visualizer;
+import android.media.audiofx.Visualizer;
 import android.media.MediaPlayer;
 
 import android.os.Looper;
diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp
index a8200be..bc944a0 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -444,13 +444,14 @@
 
 // ----------------------------------------------------------------------------
 
-static void gl_no_context() {
+static int gl_no_context() {
     tls_t* tls = getTLS();
     if (tls->logCallWithNoContext == EGL_TRUE) {
         tls->logCallWithNoContext = EGL_FALSE;
         LOGE("call to OpenGL ES API with no current context "
              "(logged once per thread)");
     }
+    return 0;
 }
 
 static void early_egl_init(void) 
@@ -463,6 +464,7 @@
             (uint32_t*)(void*)&gHooksNoContext, 
             addr, 
             sizeof(gHooksNoContext));
+
     setGlThreadSpecific(&gHooksNoContext);
 }
 
diff --git a/opengl/libs/GLES2/gl2.cpp b/opengl/libs/GLES2/gl2.cpp
index a12edf2..fee4609 100644
--- a/opengl/libs/GLES2/gl2.cpp
+++ b/opengl/libs/GLES2/gl2.cpp
@@ -60,6 +60,7 @@
             "ldr   r12, [r12, %[tls]] \n"                       \
             "cmp   r12, #0            \n"                       \
             "ldrne pc,  [r12, %[api]] \n"                       \
+            "mov   r0, #0             \n"                       \
             "bx    lr                 \n"                       \
             :                                                   \
             : [tls] "J"(TLS_SLOT_OPENGL_API*4),                 \
diff --git a/opengl/libs/GLES_CM/gl.cpp b/opengl/libs/GLES_CM/gl.cpp
index d71ff76..ee29f12 100644
--- a/opengl/libs/GLES_CM/gl.cpp
+++ b/opengl/libs/GLES_CM/gl.cpp
@@ -114,6 +114,7 @@
             "ldr   r12, [r12, %[tls]] \n"                       \
             "cmp   r12, #0            \n"                       \
             "ldrne pc,  [r12, %[api]] \n"                       \
+            "mov   r0, #0             \n"                       \
             "bx    lr                 \n"                       \
             :                                                   \
             : [tls] "J"(TLS_SLOT_OPENGL_API*4),                 \
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index d98bd7d..0ca0572 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -22,6 +22,7 @@
 import android.graphics.drawable.Drawable;
 import android.graphics.Canvas;
 import android.util.Slog;
+import android.util.Log;
 import android.view.ViewDebug;
 import android.widget.FrameLayout;
 
@@ -124,4 +125,10 @@
     public StatusBarIcon getStatusBarIcon() {
         return mIcon;
     }
+
+    protected void debug(int depth) {
+        super.debug(depth);
+        Log.d("View", debugIndent(depth) + "slot=" + mSlot);
+        Log.d("View", debugIndent(depth) + "icon=" + mIcon);
+    }
 }
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/policy/src/com/android/internal/policy/impl/LockScreen.java b/policy/src/com/android/internal/policy/impl/LockScreen.java
index f8c0aba..3583ab9 100644
--- a/policy/src/com/android/internal/policy/impl/LockScreen.java
+++ b/policy/src/com/android/internal/policy/impl/LockScreen.java
@@ -20,6 +20,8 @@
 import com.android.internal.telephony.IccCard;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.SlidingTab;
+import com.android.internal.widget.WaveView;
+import com.android.internal.widget.WaveView.OnTriggerListener;
 
 import android.content.Context;
 import android.content.res.Configuration;
@@ -46,8 +48,9 @@
  * information about the device depending on its state, and how to get
  * past it, as applicable.
  */
-class LockScreen extends LinearLayout implements KeyguardScreen, KeyguardUpdateMonitor.InfoCallback,
-        KeyguardUpdateMonitor.SimStateCallback, SlidingTab.OnTriggerListener {
+class LockScreen extends LinearLayout implements KeyguardScreen,
+        KeyguardUpdateMonitor.InfoCallback,
+        KeyguardUpdateMonitor.SimStateCallback {
 
     private static final boolean DBG = false;
     private static final String TAG = "LockScreen";
@@ -59,7 +62,7 @@
     private final KeyguardUpdateMonitor mUpdateMonitor;
     private final KeyguardScreenCallback mCallback;
 
-    private SlidingTab mSelector;
+    private SlidingTab mSlidingTab;
     private TextView mScreenLocked;
     private TextView mEmergencyCallText;
     private Button mEmergencyCallButton;
@@ -89,6 +92,9 @@
     private boolean mEnableMenuKeyInLockScreen;
 
     private StatusView mStatusView;
+    private WaveView mEnergyWave;
+    private SlidingTabMethods mSlidingTabMethods;
+    private WaveViewMethods mWaveViewMethods;
 
     /**
      * The status of this lock screen.
@@ -141,6 +147,91 @@
         }
     }
 
+    class SlidingTabMethods implements SlidingTab.OnTriggerListener {
+
+        private void updateRightTabResources() {
+            boolean vibe = mSilentMode
+                && (mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE);
+
+            mSlidingTab.setRightTabResources(
+                    mSilentMode ? ( vibe ? R.drawable.ic_jog_dial_vibrate_on
+                                         : R.drawable.ic_jog_dial_sound_off )
+                                : R.drawable.ic_jog_dial_sound_on,
+                    mSilentMode ? R.drawable.jog_tab_target_yellow
+                                : R.drawable.jog_tab_target_gray,
+                    mSilentMode ? R.drawable.jog_tab_bar_right_sound_on
+                                : R.drawable.jog_tab_bar_right_sound_off,
+                    mSilentMode ? R.drawable.jog_tab_right_sound_on
+                                : R.drawable.jog_tab_right_sound_off);
+        }
+
+        /** {@inheritDoc} */
+        public void onTrigger(View v, int whichHandle) {
+            if (whichHandle == SlidingTab.OnTriggerListener.LEFT_HANDLE) {
+                mCallback.goToUnlockScreen();
+            } else if (whichHandle == SlidingTab.OnTriggerListener.RIGHT_HANDLE) {
+                // toggle silent mode
+                mSilentMode = !mSilentMode;
+                if (mSilentMode) {
+                    final boolean vibe = (Settings.System.getInt(
+                        getContext().getContentResolver(),
+                        Settings.System.VIBRATE_IN_SILENT, 1) == 1);
+
+                    mAudioManager.setRingerMode(vibe
+                        ? AudioManager.RINGER_MODE_VIBRATE
+                        : AudioManager.RINGER_MODE_SILENT);
+                } else {
+                    mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
+                }
+
+                updateRightTabResources();
+
+                String message = mSilentMode ?
+                        getContext().getString(R.string.global_action_silent_mode_on_status) :
+                        getContext().getString(R.string.global_action_silent_mode_off_status);
+
+                final int toastIcon = mSilentMode
+                    ? R.drawable.ic_lock_ringer_off
+                    : R.drawable.ic_lock_ringer_on;
+
+                final int toastColor = mSilentMode
+                    ? getContext().getResources().getColor(R.color.keyguard_text_color_soundoff)
+                    : getContext().getResources().getColor(R.color.keyguard_text_color_soundon);
+                toastMessage(mScreenLocked, message, toastColor, toastIcon);
+                mCallback.pokeWakelock();
+            }
+        }
+
+        /** {@inheritDoc} */
+        public void onGrabbedStateChange(View v, int grabbedState) {
+            if (grabbedState == SlidingTab.OnTriggerListener.RIGHT_HANDLE) {
+                mSilentMode = isSilentMode();
+                mSlidingTab.setRightHintText(mSilentMode ? R.string.lockscreen_sound_on_label
+                        : R.string.lockscreen_sound_off_label);
+            }
+            mCallback.pokeWakelock();
+        }
+    }
+
+    class WaveViewMethods implements WaveView.OnTriggerListener {
+        /** {@inheritDoc} */
+        public void onTrigger(View v, int whichHandle) {
+            if (whichHandle == WaveView.OnTriggerListener.CENTER_HANDLE) {
+                // Delay hiding lock screen long enough for animation to finish
+                postDelayed(new Runnable() {
+                    public void run() {
+                        mCallback.goToUnlockScreen();
+                    }
+                }, 500);
+            }
+        }
+
+        /** {@inheritDoc} */
+        public void onGrabbedStateChange(View v, int grabbedState) {
+            mCallback.pokeWakelock();
+        }
+    }
+
     /**
      * In general, we enable unlocking the insecure key guard with the menu key. However, there are
      * some cases where we wish to disable it, notably when the menu button placement or technology
@@ -195,9 +286,6 @@
         mStatusView = new StatusView(this, mUpdateMonitor, mLockPatternUtils);
 
         mScreenLocked = (TextView) findViewById(R.id.screenLocked);
-        mSelector = (SlidingTab) findViewById(R.id.tab_selector);
-        mSelector.setHoldAfterTrigger(true, false);
-        mSelector.setLeftHintText(R.string.lockscreen_unlock_label);
 
         mEmergencyCallText = (TextView) findViewById(R.id.emergencyCallText);
         mEmergencyCallButton = (Button) findViewById(R.id.emergencyCallButton);
@@ -220,15 +308,25 @@
         mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
         mSilentMode = isSilentMode();
 
-        mSelector.setLeftTabResources(
-                R.drawable.ic_jog_dial_unlock,
-                R.drawable.jog_tab_target_green,
-                R.drawable.jog_tab_bar_left_unlock,
-                R.drawable.jog_tab_left_unlock);
-
-        updateRightTabResources();
-
-        mSelector.setOnTriggerListener(this);
+        mSlidingTab = (SlidingTab) findViewById(R.id.tab_selector);
+        mEnergyWave = (WaveView) findViewById(R.id.wave_view);
+        if (mSlidingTab != null) {
+            mSlidingTab.setHoldAfterTrigger(true, false);
+            mSlidingTab.setLeftHintText(R.string.lockscreen_unlock_label);
+            mSlidingTab.setLeftTabResources(
+                    R.drawable.ic_jog_dial_unlock,
+                    R.drawable.jog_tab_target_green,
+                    R.drawable.jog_tab_bar_left_unlock,
+                    R.drawable.jog_tab_left_unlock);
+            mSlidingTabMethods = new SlidingTabMethods();
+            mSlidingTab.setOnTriggerListener(mSlidingTabMethods);
+            mSlidingTabMethods.updateRightTabResources();
+        } else if (mEnergyWave != null) {
+            mWaveViewMethods = new WaveViewMethods();
+            mEnergyWave.setOnTriggerListener(mWaveViewMethods);
+        } else {
+            throw new IllegalStateException("Must have either SlidingTab or WaveView defined");
+        }
 
         resetStatusInfo(updateMonitor);
     }
@@ -237,22 +335,6 @@
         return mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL;
     }
 
-    private void updateRightTabResources() {
-        boolean vibe = mSilentMode
-            && (mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE);
-
-        mSelector.setRightTabResources(
-                mSilentMode ? ( vibe ? R.drawable.ic_jog_dial_vibrate_on
-                                     : R.drawable.ic_jog_dial_sound_off )
-                            : R.drawable.ic_jog_dial_sound_on,
-                mSilentMode ? R.drawable.jog_tab_target_yellow
-                            : R.drawable.jog_tab_target_gray,
-                mSilentMode ? R.drawable.jog_tab_bar_right_sound_on
-                            : R.drawable.jog_tab_bar_right_sound_off,
-                mSilentMode ? R.drawable.jog_tab_right_sound_on
-                            : R.drawable.jog_tab_right_sound_off);
-    }
-
     private void resetStatusInfo(KeyguardUpdateMonitor updateMonitor) {
         mShowingBatteryInfo = updateMonitor.shouldShowBatteryInfo();
         mPluggedIn = updateMonitor.isDevicePluggedIn();
@@ -278,53 +360,6 @@
         return false;
     }
 
-    /** {@inheritDoc} */
-    public void onTrigger(View v, int whichHandle) {
-        if (whichHandle == SlidingTab.OnTriggerListener.LEFT_HANDLE) {
-            mCallback.goToUnlockScreen();
-        } else if (whichHandle == SlidingTab.OnTriggerListener.RIGHT_HANDLE) {
-            // toggle silent mode
-            mSilentMode = !mSilentMode;
-            if (mSilentMode) {
-                final boolean vibe = (Settings.System.getInt(
-                    getContext().getContentResolver(),
-                    Settings.System.VIBRATE_IN_SILENT, 1) == 1);
-
-                mAudioManager.setRingerMode(vibe
-                    ? AudioManager.RINGER_MODE_VIBRATE
-                    : AudioManager.RINGER_MODE_SILENT);
-            } else {
-                mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
-            }
-
-            updateRightTabResources();
-
-            String message = mSilentMode ?
-                    getContext().getString(R.string.global_action_silent_mode_on_status) :
-                    getContext().getString(R.string.global_action_silent_mode_off_status);
-
-            final int toastIcon = mSilentMode
-                ? R.drawable.ic_lock_ringer_off
-                : R.drawable.ic_lock_ringer_on;
-
-            final int toastColor = mSilentMode
-                ? getContext().getResources().getColor(R.color.keyguard_text_color_soundoff)
-                : getContext().getResources().getColor(R.color.keyguard_text_color_soundon);
-            toastMessage(mScreenLocked, message, toastColor, toastIcon);
-            mCallback.pokeWakelock();
-        }
-    }
-
-    /** {@inheritDoc} */
-    public void onGrabbedStateChange(View v, int grabbedState) {
-        if (grabbedState == SlidingTab.OnTriggerListener.RIGHT_HANDLE) {
-            mSilentMode = isSilentMode();
-            mSelector.setRightHintText(mSilentMode ? R.string.lockscreen_sound_on_label
-                    : R.string.lockscreen_sound_off_label);
-        }
-        mCallback.pokeWakelock();
-    }
-
     /**
      * Displays a message in a text view and then restores the previous text.
      * @param textView The text view.
@@ -460,6 +495,22 @@
     }
 
     /**
+     * Enables unlocking of this screen. Typically just shows the unlock widget.
+     */
+    private void enableUnlock() {
+        if (mEnergyWave != null) mEnergyWave.setVisibility(View.VISIBLE);
+        if (mSlidingTab != null) mSlidingTab.setVisibility(View.VISIBLE);
+    }
+
+    /**
+     * Disable unlocking of this screen. Typically just hides the unlock widget.
+     */
+    private void disableUnlock() {
+        if (mEnergyWave != null) mEnergyWave.setVisibility(View.GONE);
+        if (mSlidingTab != null) mSlidingTab.setVisibility(View.GONE);
+    }
+
+    /**
      * Update the layout to match the current status.
      */
     private void updateLayout(Status status) {
@@ -481,9 +532,10 @@
 
                 // layout
                 mScreenLocked.setVisibility(View.INVISIBLE);
-                mSelector.setVisibility(View.VISIBLE);
                 mEmergencyCallText.setVisibility(View.GONE);
+                enableUnlock();
                 break;
+
             case NetworkLocked:
                 // The carrier string shows both sim card status (i.e. No Sim Card) and
                 // carrier's name and/or "Emergency Calls Only" status
@@ -495,9 +547,10 @@
 
                 // layout
                 mScreenLocked.setVisibility(View.VISIBLE);
-                mSelector.setVisibility(View.VISIBLE);
                 mEmergencyCallText.setVisibility(View.GONE);
+                enableUnlock();
                 break;
+
             case SimMissing:
                 // text
                 mStatusView.setCarrierText(R.string.lockscreen_missing_sim_message_short);
@@ -505,10 +558,10 @@
 
                 // layout
                 mScreenLocked.setVisibility(View.VISIBLE);
-                mSelector.setVisibility(View.VISIBLE);
                 mEmergencyCallText.setVisibility(View.VISIBLE);
-                // do not need to show the e-call button; user may unlock
+                enableUnlock(); // do not need to show the e-call button; user may unlock
                 break;
+
             case SimMissingLocked:
                 // text
                 mStatusView.setCarrierText(
@@ -519,10 +572,11 @@
 
                 // layout
                 mScreenLocked.setVisibility(View.VISIBLE);
-                mSelector.setVisibility(View.GONE); // cannot unlock
                 mEmergencyCallText.setVisibility(View.VISIBLE);
                 mEmergencyCallButton.setVisibility(View.VISIBLE);
+                disableUnlock();
                 break;
+
             case SimLocked:
                 // text
                 mStatusView.setCarrierText(
@@ -532,9 +586,10 @@
 
                 // layout
                 mScreenLocked.setVisibility(View.INVISIBLE);
-                mSelector.setVisibility(View.VISIBLE);
                 mEmergencyCallText.setVisibility(View.GONE);
+                enableUnlock();
                 break;
+
             case SimPukLocked:
                 // text
                 mStatusView.setCarrierText(
@@ -545,9 +600,9 @@
 
                 // layout
                 mScreenLocked.setVisibility(View.VISIBLE);
-                mSelector.setVisibility(View.GONE); // cannot unlock
                 mEmergencyCallText.setVisibility(View.VISIBLE);
                 mEmergencyCallButton.setVisibility(View.VISIBLE);
+                disableUnlock();
                 break;
         }
     }
@@ -614,7 +669,9 @@
 
     /** {@inheritDoc} */
     public void onPause() {
-
+        if (mEnergyWave != null) {
+            mEnergyWave.reset();
+        }
     }
 
     /** {@inheritDoc} */
@@ -632,7 +689,7 @@
         boolean silent = AudioManager.RINGER_MODE_NORMAL != state;
         if (silent != mSilentMode) {
             mSilentMode = silent;
-            updateRightTabResources();
+            if (mSlidingTabMethods != null) mSlidingTabMethods.updateRightTabResources();
         }
     }
 
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 7009c65..c047522 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -235,8 +235,11 @@
         @Override
         public void handleMotion(MotionEvent event, Runnable finishedCallback) {
             finishedCallback.run();
+            
             synchronized (mLock) {
-                mPointerLocationView.addTouchEvent(event);
+                if (mPointerLocationView != null) {
+                    mPointerLocationView.addTouchEvent(event);
+                }
             }
         }
     };
@@ -287,7 +290,8 @@
     // (See Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR.)
     int mIncallPowerBehavior;
 
-    int mLandscapeRotation = -1;
+    int mLandscapeRotation = -1; // default landscape rotation
+    int mSeascapeRotation = -1; // "other" landscape rotation, 180 degrees from mLandscapeRotation
     int mPortraitRotation = -1;
 
     // Nothing to see here, move along...
@@ -360,9 +364,12 @@
             return true;
         }
         // The user preference says we can rotate, and the app is willing to rotate.
+        // Note we include SCREEN_ORIENTATION_LANDSCAPE since we can use the sensor to choose
+        // between the two possible landscape rotations.
         if (mAccelerometerDefault != 0 &&
                 (appOrientation == ActivityInfo.SCREEN_ORIENTATION_USER
-                 || appOrientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)) {
+                 || appOrientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
+                 || appOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)) {
             return true;
         }
         // We're in a dock that has a rotation affinity, an the app is willing to rotate.
@@ -371,7 +378,8 @@
             // Note we override the nosensor flag here.
             if (appOrientation == ActivityInfo.SCREEN_ORIENTATION_USER
                     || appOrientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
-                    || appOrientation == ActivityInfo.SCREEN_ORIENTATION_NOSENSOR) {
+                    || appOrientation == ActivityInfo.SCREEN_ORIENTATION_NOSENSOR
+                    || appOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
                 return true;
             }
         }
@@ -2117,20 +2125,20 @@
             if (d.getWidth() > d.getHeight()) {
                 mPortraitRotation = Surface.ROTATION_90;
                 mLandscapeRotation = Surface.ROTATION_0;
+                mSeascapeRotation = Surface.ROTATION_180;
             } else {
                 mPortraitRotation = Surface.ROTATION_0;
                 mLandscapeRotation = Surface.ROTATION_90;
+                mSeascapeRotation = Surface.ROTATION_270;
             }
         }
 
         synchronized (mLock) {
-            switch (orientation) {
-                case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE:
-                    //always return landscape if orientation set to landscape
-                    return mLandscapeRotation;
-                case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT:
-                    //always return portrait if orientation set to portrait
-                    return mPortraitRotation;
+            if (orientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) {
+                //always return portrait if orientation set to portrait
+                return mPortraitRotation;
+            } else if (orientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
+                return getCurrentLandscapeRotation(lastRotation);
             }
             // case for nosensor meaning ignore sensor and consider only lid
             // or orientation sensor disabled
@@ -2150,6 +2158,26 @@
         }
     }
 
+    private int getCurrentLandscapeRotation(int lastRotation) {
+        // landscape-only apps can take either landscape rotation
+        if (useSensorForOrientationLp(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)) {
+            int sensorRotation = mOrientationListener.getCurrentRotation(lastRotation);
+            if (isLandscapeOrSeascape(sensorRotation)) {
+                return sensorRotation;
+            }
+        }
+        // try to preserve the old rotation if it was landscape
+        if (isLandscapeOrSeascape(lastRotation)) {
+            return lastRotation;
+        }
+        // default to one of the two landscape rotations
+        return mLandscapeRotation;
+    }
+
+    private boolean isLandscapeOrSeascape(int sensorRotation) {
+        return sensorRotation == mLandscapeRotation || sensorRotation == mSeascapeRotation;
+    }
+
     public boolean detectSafeMode() {
         try {
             int menuState = mWindowManager.getKeycodeState(KeyEvent.KEYCODE_MENU);
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/ProcessStats.java b/services/java/com/android/server/ProcessStats.java
index 5bdadcc..43dbcc0 100644
--- a/services/java/com/android/server/ProcessStats.java
+++ b/services/java/com/android/server/ProcessStats.java
@@ -80,16 +80,24 @@
         PROC_SPACE_TERM|PROC_OUT_LONG,                  // 11: major faults
         PROC_SPACE_TERM,
         PROC_SPACE_TERM|PROC_OUT_LONG,                  // 13: utime
-        PROC_SPACE_TERM|PROC_OUT_LONG                   // 14: stime
+        PROC_SPACE_TERM|PROC_OUT_LONG,                  // 14: stime
+        PROC_SPACE_TERM,
+        PROC_SPACE_TERM,
+        PROC_SPACE_TERM,
+        PROC_SPACE_TERM,
+        PROC_SPACE_TERM,
+        PROC_SPACE_TERM,
+        PROC_SPACE_TERM|PROC_OUT_LONG,                  // 21: vsize
     };
 
     static final int PROCESS_FULL_STAT_MINOR_FAULTS = 1;
     static final int PROCESS_FULL_STAT_MAJOR_FAULTS = 2;
     static final int PROCESS_FULL_STAT_UTIME = 3;
     static final int PROCESS_FULL_STAT_STIME = 4;
+    static final int PROCESS_FULL_STAT_VSIZE = 5;
 
-    private final String[] mProcessFullStatsStringData = new String[5];
-    private final long[] mProcessFullStatsData = new long[5];
+    private final String[] mProcessFullStatsStringData = new String[6];
+    private final long[] mProcessFullStatsData = new long[6];
 
     private static final int[] SYSTEM_CPU_FORMAT = new int[] {
         PROC_SPACE_TERM|PROC_COMBINE,
@@ -171,6 +179,8 @@
         final ArrayList<Stats> threadStats;
         final ArrayList<Stats> workingThreads;
         
+        public boolean interesting;
+
         public String baseName;
         public String name;
         int nameWidth;
@@ -349,59 +359,62 @@
                         + (parentPid < 0 ? "process" : "thread")
                         + " pid " + pid + ": " + st);
 
-                final long uptime = SystemClock.uptimeMillis();
+                if (st.interesting) {
+                    final long uptime = SystemClock.uptimeMillis();
 
-                final long[] procStats = mProcessStatsData;
-                if (!Process.readProcFile(st.statFile.toString(),
-                        PROCESS_STATS_FORMAT, null, procStats, null)) {
-                    continue;
-                }
-                
-                final long minfaults = procStats[PROCESS_STAT_MINOR_FAULTS];
-                final long majfaults = procStats[PROCESS_STAT_MAJOR_FAULTS];
-                final long utime = procStats[PROCESS_STAT_UTIME];
-                final long stime = procStats[PROCESS_STAT_STIME];
-
-                if (utime == st.base_utime && stime == st.base_stime) {
-                    st.rel_utime = 0;
-                    st.rel_stime = 0;
-                    st.rel_minfaults = 0;
-                    st.rel_majfaults = 0;
-                    if (st.active) {
-                        st.active = false;
+                    final long[] procStats = mProcessStatsData;
+                    if (!Process.readProcFile(st.statFile.toString(),
+                            PROCESS_STATS_FORMAT, null, procStats, null)) {
+                        continue;
                     }
-                    continue;
-                }
                     
-                if (!st.active) {
-                    st.active = true;
-                }
+                    final long minfaults = procStats[PROCESS_STAT_MINOR_FAULTS];
+                    final long majfaults = procStats[PROCESS_STAT_MAJOR_FAULTS];
+                    final long utime = procStats[PROCESS_STAT_UTIME];
+                    final long stime = procStats[PROCESS_STAT_STIME];
 
-                if (parentPid < 0) {
-                    getName(st, st.cmdlineFile);
-                    if (st.threadStats != null) {
-                        mCurThreadPids = collectStats(st.threadsDir, pid, false,
-                                mCurThreadPids, st.threadStats);
+                    if (utime == st.base_utime && stime == st.base_stime) {
+                        st.rel_utime = 0;
+                        st.rel_stime = 0;
+                        st.rel_minfaults = 0;
+                        st.rel_majfaults = 0;
+                        if (st.active) {
+                            st.active = false;
+                        }
+                        continue;
                     }
+
+                    if (!st.active) {
+                        st.active = true;
+                    }
+
+                    if (parentPid < 0) {
+                        getName(st, st.cmdlineFile);
+                        if (st.threadStats != null) {
+                            mCurThreadPids = collectStats(st.threadsDir, pid, false,
+                                    mCurThreadPids, st.threadStats);
+                        }
+                    }
+
+                    if (DEBUG) Slog.v("Load", "Stats changed " + st.name + " pid=" + st.pid
+                            + " utime=" + utime + "-" + st.base_utime
+                            + " stime=" + stime + "-" + st.base_stime
+                            + " minfaults=" + minfaults + "-" + st.base_minfaults
+                            + " majfaults=" + majfaults + "-" + st.base_majfaults);
+
+                    st.rel_uptime = uptime - st.base_uptime;
+                    st.base_uptime = uptime;
+                    st.rel_utime = (int)(utime - st.base_utime);
+                    st.rel_stime = (int)(stime - st.base_stime);
+                    st.base_utime = utime;
+                    st.base_stime = stime;
+                    st.rel_minfaults = (int)(minfaults - st.base_minfaults);
+                    st.rel_majfaults = (int)(majfaults - st.base_majfaults);
+                    st.base_minfaults = minfaults;
+                    st.base_majfaults = majfaults;
+                    st.working = true;
                 }
 
-                if (DEBUG) Slog.v("Load", "Stats changed " + st.name + " pid=" + st.pid
-                        + " utime=" + utime + "-" + st.base_utime
-                        + " stime=" + stime + "-" + st.base_stime
-                        + " minfaults=" + minfaults + "-" + st.base_minfaults
-                        + " majfaults=" + majfaults + "-" + st.base_majfaults);
-
-                st.rel_uptime = uptime - st.base_uptime;
-                st.base_uptime = uptime;
-                st.rel_utime = (int)(utime - st.base_utime);
-                st.rel_stime = (int)(stime - st.base_stime);
-                st.base_utime = utime;
-                st.base_stime = stime;
-                st.rel_minfaults = (int)(minfaults - st.base_minfaults);
-                st.rel_majfaults = (int)(majfaults - st.base_majfaults);
-                st.base_minfaults = minfaults;
-                st.base_majfaults = majfaults;
-                st.working = true;
                 continue;
             }
             
@@ -421,12 +434,24 @@
                 if (Process.readProcFile(st.statFile.toString(),
                         PROCESS_FULL_STATS_FORMAT, procStatsString,
                         procStats, null)) {
-                    st.baseName = procStatsString[0];
-                    st.base_minfaults = procStats[PROCESS_FULL_STAT_MINOR_FAULTS];
-                    st.base_majfaults = procStats[PROCESS_FULL_STAT_MAJOR_FAULTS];
-                    st.base_utime = procStats[PROCESS_FULL_STAT_UTIME];
-                    st.base_stime = procStats[PROCESS_FULL_STAT_STIME];
+                    // This is a possible way to filter out processes that
+                    // are actually kernel threads...  do we want to?  Some
+                    // of them do use CPU, but there can be a *lot* that are
+                    // not doing anything.
+                    if (true || procStats[PROCESS_FULL_STAT_VSIZE] != 0) {
+                        st.interesting = true;
+                        st.baseName = procStatsString[0];
+                        st.base_minfaults = procStats[PROCESS_FULL_STAT_MINOR_FAULTS];
+                        st.base_majfaults = procStats[PROCESS_FULL_STAT_MAJOR_FAULTS];
+                        st.base_utime = procStats[PROCESS_FULL_STAT_UTIME];
+                        st.base_stime = procStats[PROCESS_FULL_STAT_STIME];
+                    } else {
+                        Slog.i(TAG, "Skipping kernel process pid " + pid
+                                + " name " + procStatsString[0]);
+                        st.baseName = procStatsString[0];
+                    }
                 } else {
+                    Slog.w(TAG, "Skipping unknown process pid " + pid);
                     st.baseName = "<unknown>";
                     st.base_utime = st.base_stime = 0;
                     st.base_minfaults = st.base_majfaults = 0;
@@ -438,7 +463,7 @@
                         mCurThreadPids = collectStats(st.threadsDir, pid, true,
                                 mCurThreadPids, st.threadStats);
                     }
-                } else {
+                } else if (st.interesting) {
                     st.name = st.baseName;
                     st.nameWidth = onMeasureProcessName(st.name);
                 }
@@ -452,7 +477,7 @@
                 st.rel_minfaults = 0;
                 st.rel_majfaults = 0;
                 st.added = true;
-                if (!first) {
+                if (!first && st.interesting) {
                     st.working = true;
                 }
                 continue;
@@ -624,6 +649,14 @@
         }
     }
 
+    final public int countStats() {
+        return mProcStats.size();
+    }
+
+    final public Stats getStats(int index) {
+        return mProcStats.get(index);
+    }
+
     final public int countWorkingStats() {
         buildWorkingProcs();
         return mWorkingProcs.size();
@@ -788,7 +821,8 @@
 
     private void getName(Stats st, String cmdlineFile) {
         String newName = st.name;
-        if (st.name == null || st.name.equals("app_process")) {
+        if (st.name == null || st.name.equals("app_process")
+                || st.name.equals("<pre-initialized>")) {
             String cmdName = readFile(cmdlineFile, '\0');
             if (cmdName != null && cmdName.length() > 1) {
                 newName = cmdName;
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/WifiService.java b/services/java/com/android/server/WifiService.java
index c52567a..7b2a570 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -804,6 +804,16 @@
         mWifiStateMachine.forgetNetwork(netId);
     }
 
+    public void startWpsPbc(String bssid) {
+        enforceChangePermission();
+        mWifiStateMachine.startWpsPbc(bssid);
+    }
+
+    public void startWpsPin(String bssid, int apPin) {
+        enforceChangePermission();
+        mWifiStateMachine.startWpsPin(bssid, apPin);
+    }
+
     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index c896c94..8e22652 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -122,6 +122,8 @@
 import java.lang.IllegalStateException;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -145,6 +147,7 @@
     static final boolean DEBUG_BROADCAST = localLOGV || false;
     static final boolean DEBUG_BROADCAST_LIGHT = DEBUG_BROADCAST || false;
     static final boolean DEBUG_SERVICE = localLOGV || false;
+    static final boolean DEBUG_SERVICE_EXECUTING = localLOGV || false;
     static final boolean DEBUG_VISBILITY = localLOGV || false;
     static final boolean DEBUG_PROCESSES = localLOGV || false;
     static final boolean DEBUG_PROVIDER = localLOGV || false;
@@ -153,6 +156,8 @@
     static final boolean DEBUG_RESULTS = localLOGV || false;
     static final boolean DEBUG_BACKUP = localLOGV || false;
     static final boolean DEBUG_CONFIGURATION = localLOGV || false;
+    static final boolean DEBUG_POWER = localLOGV || false;
+    static final boolean DEBUG_POWER_QUICK = DEBUG_POWER || false;
     static final boolean VALIDATE_TOKENS = false;
     static final boolean SHOW_ACTIVITY_START_TIME = true;
     
@@ -198,8 +203,16 @@
     // The minimum amount of time between successive GC requests for a process.
     static final int GC_MIN_INTERVAL = 60*1000;
 
-    // The rate at which we check for apps using excessive wake locks -- 15 mins.
-    static final int WAKE_LOCK_CHECK_DELAY = 15*60*1000;
+    // The rate at which we check for apps using excessive power -- 15 mins.
+    static final int POWER_CHECK_DELAY = (DEBUG_POWER_QUICK ? 2 : 15) * 60*1000;
+
+    // The minimum sample duration we will allow before deciding we have
+    // enough data on wake locks to start killing things.
+    static final int WAKE_LOCK_MIN_CHECK_DURATION = (DEBUG_POWER_QUICK ? 1 : 5) * 60*1000;
+
+    // The minimum sample duration we will allow before deciding we have
+    // enough data on CPU usage to start killing things.
+    static final int CPU_MIN_CHECK_DURATION = (DEBUG_POWER_QUICK ? 1 : 5) * 60*1000;
 
     // How long we allow a receiver to run before giving up on it.
     static final int BROADCAST_TIMEOUT = 10*1000;
@@ -780,9 +793,14 @@
     boolean mDidAppSwitch;
     
     /**
-     * Last time (in realtime) at which we checked for wake lock usage.
+     * Last time (in realtime) at which we checked for power usage.
      */
-    long mLastWakeLockCheckTime;
+    long mLastPowerCheckRealtime;
+
+    /**
+     * Last time (in uptime) at which we checked for power usage.
+     */
+    long mLastPowerCheckUptime;
 
     /**
      * Set while we are wanting to sleep, to prevent any
@@ -1195,12 +1213,10 @@
             } break;
             case CHECK_EXCESSIVE_WAKE_LOCKS_MSG: {
                 synchronized (ActivityManagerService.this) {
-                    checkExcessiveWakeLocksLocked(true);
+                    checkExcessivePowerUsageLocked(true);
                     removeMessages(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
-                    if (mSleeping) {
-                        Message nmsg = obtainMessage(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
-                        sendMessageDelayed(nmsg, WAKE_LOCK_CHECK_DELAY);
-                    }
+                    Message nmsg = obtainMessage(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
+                    sendMessageDelayed(nmsg, POWER_CHECK_DELAY);
                 }
             } break;
             }
@@ -1395,7 +1411,8 @@
                 systemDir, "batterystats.bin").toString());
         mBatteryStatsService.getActiveStatistics().readLocked();
         mBatteryStatsService.getActiveStatistics().writeAsyncLocked();
-        mOnBattery = mBatteryStatsService.getActiveStatistics().getIsOnBattery();
+        mOnBattery = DEBUG_POWER ? true
+                : mBatteryStatsService.getActiveStatistics().getIsOnBattery();
         mBatteryStatsService.getActiveStatistics().setCallback(this);
         
         mUsageStatsService = new UsageStatsService(new File(
@@ -1515,10 +1532,12 @@
                             int perc = bstats.startAddingCpuLocked();
                             int totalUTime = 0;
                             int totalSTime = 0;
-                            final int N = mProcessStats.countWorkingStats();
+                            final int N = mProcessStats.countStats();
                             for (int i=0; i<N; i++) {
-                                ProcessStats.Stats st
-                                        = mProcessStats.getWorkingStats(i);
+                                ProcessStats.Stats st = mProcessStats.getStats(i);
+                                if (!st.working) {
+                                    continue;
+                                }
                                 ProcessRecord pr = mPidsSelfLocked.get(st.pid);
                                 int otherUTime = (st.rel_utime*perc)/100;
                                 int otherSTime = (st.rel_stime*perc)/100;
@@ -1529,6 +1548,7 @@
                                     ps.addCpuTimeLocked(st.rel_utime-otherUTime,
                                             st.rel_stime-otherSTime);
                                     ps.addSpeedStepTimes(cpuSpeedTimes);
+                                    pr.curCpuTime += (st.rel_utime+st.rel_stime) * 10;
                                 } else {
                                     BatteryStatsImpl.Uid.Proc ps =
                                             bstats.getProcessStatsLocked(st.name, st.pid);
@@ -1565,7 +1585,7 @@
         updateCpuStatsNow();
         synchronized (this) {
             synchronized(mPidsSelfLocked) {
-                mOnBattery = onBattery;
+                mOnBattery = DEBUG_POWER ? true : onBattery;
             }
         }
     }
@@ -2795,6 +2815,16 @@
         ArrayList<Integer> firstPids = new ArrayList<Integer>(5);
         SparseArray<Boolean> lastPids = new SparseArray<Boolean>(20);
 
+        if (mController != null) {
+            try {
+                // 0 == continue, -1 = kill process immediately
+                int res = mController.appEarlyNotResponding(app.processName, app.pid, annotation);
+                if (res < 0 && app.pid != MY_PID) Process.killProcess(app.pid);
+            } catch (RemoteException e) {
+                mController = null;
+            }
+        }
+
         long anrTime = SystemClock.uptimeMillis();
         if (MONITOR_CPU_USAGE) {
             updateCpuStatsNow();
@@ -2845,10 +2875,6 @@
             }
         }
 
-        final ProcessStats processStats = new ProcessStats(true);
-
-        File tracesFile = dumpStackTraces(true, firstPids, processStats, lastPids);
-
         // Log the ANR to the main log.
         StringBuilder info = mStringBuilder;
         info.setLength(0);
@@ -2864,6 +2890,10 @@
             info.append("Parent: ").append(parent.shortComponentName).append("\n");
         }
 
+        final ProcessStats processStats = new ProcessStats(true);
+
+        File tracesFile = dumpStackTraces(true, firstPids, processStats, lastPids);
+
         String cpuInfo = null;
         if (MONITOR_CPU_USAGE) {
             updateCpuStatsNow();
@@ -3742,7 +3772,7 @@
             if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
                 // Start looking for apps that are abusing wake locks.
                 Message nmsg = mHandler.obtainMessage(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
-                mHandler.sendMessageDelayed(nmsg, WAKE_LOCK_CHECK_DELAY);
+                mHandler.sendMessageDelayed(nmsg, POWER_CHECK_DELAY);
                 // Tell anyone interested that we are done booting!
                 SystemProperties.set("sys.boot_completed", "1");
                 broadcastIntentLocked(null, null,
@@ -5655,10 +5685,10 @@
             }
 
             // Initialize the wake times of all processes.
-            checkExcessiveWakeLocksLocked(false);
+            checkExcessivePowerUsageLocked(false);
             mHandler.removeMessages(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
             Message nmsg = mHandler.obtainMessage(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
-            mHandler.sendMessageDelayed(nmsg, WAKE_LOCK_CHECK_DELAY);
+            mHandler.sendMessageDelayed(nmsg, POWER_CHECK_DELAY);
         }
     }
 
@@ -5708,7 +5738,6 @@
             mWindowManager.setEventDispatching(true);
             mSleeping = false;
             mMainStack.resumeTopActivityLocked(null);
-            mHandler.removeMessages(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
         }
     }
 
@@ -7055,12 +7084,13 @@
                 pw.println("Activity manager dump options:");
                 pw.println("  [-a] [-h] [cmd] ...");
                 pw.println("  cmd may be one of:");
-                pw.println("    activities: activity stack state");
-                pw.println("    broadcasts: broadcast state");
-                pw.println("    intents: pending intent state");
-                pw.println("    processes: process state");
-                pw.println("    providers: content provider state");
-                pw.println("    services: service state");
+                pw.println("    a[ctivities]: activity stack state");
+                pw.println("    b[roadcasts]: broadcast state");
+                pw.println("    i[ntents]: pending intent state");
+                pw.println("    p[rocesses]: process state");
+                pw.println("    o[om]: out of memory management");
+                pw.println("    prov[iders]: content provider state");
+                pw.println("    s[ervices]: service state");
                 pw.println("    service [name]: service client-side state");
                 return;
             } else {
@@ -7092,6 +7122,11 @@
                     dumpProcessesLocked(fd, pw, args, opti, true);
                 }
                 return;
+            } else if ("oom".equals(cmd) || "o".equals(cmd)) {
+                synchronized (this) {
+                    dumpOomLocked(fd, pw, args, opti, true);
+                }
+                return;
             } else if ("providers".equals(cmd) || "prov".equals(cmd)) {
                 synchronized (this) {
                     dumpProvidersLocked(fd, pw, args, opti, true);
@@ -7213,7 +7248,7 @@
         
         return true;
     }
-        
+
     boolean dumpProcessesLocked(FileDescriptor fd, PrintWriter pw, String[] args,
             int opti, boolean dumpAll) {
         boolean needSep = false;
@@ -7243,8 +7278,8 @@
             if (needSep) pw.println(" ");
             needSep = true;
             pw.println("  Running processes (most recent first):");
-            dumpProcessList(pw, this, mLruProcesses, "    ",
-                    "Proc", "PERS", true);
+            dumpProcessOomList(pw, this, mLruProcesses, "    ",
+                    "Proc", "PERS", false);
             needSep = true;
         }
 
@@ -7275,7 +7310,7 @@
             needSep = true;
             pw.println("  Persisent processes that are starting:");
             dumpProcessList(pw, this, mPersistentStartingProcesses, "    ",
-                    "Starting Norm", "Restarting PERS", false);
+                    "Starting Norm", "Restarting PERS");
         }
 
         if (mStartingProcesses.size() > 0) {
@@ -7283,7 +7318,7 @@
             needSep = true;
             pw.println("  Processes that are starting:");
             dumpProcessList(pw, this, mStartingProcesses, "    ",
-                    "Starting Norm", "Starting PERS", false);
+                    "Starting Norm", "Starting PERS");
         }
 
         if (mRemovedProcesses.size() > 0) {
@@ -7291,7 +7326,7 @@
             needSep = true;
             pw.println("  Processes that are being removed:");
             dumpProcessList(pw, this, mRemovedProcesses, "    ",
-                    "Removed Norm", "Removed PERS", false);
+                    "Removed Norm", "Removed PERS");
         }
         
         if (mProcessesOnHold.size() > 0) {
@@ -7299,26 +7334,10 @@
             needSep = true;
             pw.println("  Processes that are on old until the system is ready:");
             dumpProcessList(pw, this, mProcessesOnHold, "    ",
-                    "OnHold Norm", "OnHold PERS", false);
+                    "OnHold Norm", "OnHold PERS");
         }
 
-        if (mProcessesToGc.size() > 0) {
-            if (needSep) pw.println(" ");
-            needSep = true;
-            pw.println("  Processes that are waiting to GC:");
-            long now = SystemClock.uptimeMillis();
-            for (int i=0; i<mProcessesToGc.size(); i++) {
-                ProcessRecord proc = mProcessesToGc.get(i);
-                pw.print("    Process "); pw.println(proc);
-                pw.print("      lowMem="); pw.print(proc.reportLowMemory);
-                        pw.print(", last gced=");
-                        pw.print(now-proc.lastRequestedGc);
-                        pw.print(" ms ago, last lowMem=");
-                        pw.print(now-proc.lastLowMemory);
-                        pw.println(" ms ago");
-                
-            }
-        }
+        needSep = dumpProcessesToGc(fd, pw, args, opti, needSep, dumpAll);
         
         if (mProcessCrashTimes.getMap().size() > 0) {
             if (needSep) pw.println(" ");
@@ -7382,6 +7401,12 @@
             pw.println("  mBooting=" + mBooting
                     + " mBooted=" + mBooted
                     + " mFactoryTest=" + mFactoryTest);
+            pw.print("  mLastPowerCheckRealtime=");
+                    TimeUtils.formatDuration(mLastPowerCheckRealtime, pw);
+                    pw.println("");
+            pw.print("  mLastPowerCheckUptime=");
+                    TimeUtils.formatDuration(mLastPowerCheckUptime, pw);
+                    pw.println("");
             pw.println("  mGoingToSleep=" + mMainStack.mGoingToSleep);
             pw.println("  mLaunchingActivity=" + mMainStack.mLaunchingActivity);
             pw.println("  mAdjSeq=" + mAdjSeq + " mLruSeq=" + mLruSeq);
@@ -7390,6 +7415,75 @@
         return true;
     }
 
+    boolean dumpProcessesToGc(FileDescriptor fd, PrintWriter pw, String[] args,
+            int opti, boolean needSep, boolean dumpAll) {
+        if (mProcessesToGc.size() > 0) {
+            if (needSep) pw.println(" ");
+            needSep = true;
+            pw.println("  Processes that are waiting to GC:");
+            long now = SystemClock.uptimeMillis();
+            for (int i=0; i<mProcessesToGc.size(); i++) {
+                ProcessRecord proc = mProcessesToGc.get(i);
+                pw.print("    Process "); pw.println(proc);
+                pw.print("      lowMem="); pw.print(proc.reportLowMemory);
+                        pw.print(", last gced=");
+                        pw.print(now-proc.lastRequestedGc);
+                        pw.print(" ms ago, last lowMem=");
+                        pw.print(now-proc.lastLowMemory);
+                        pw.println(" ms ago");
+
+            }
+        }
+        return needSep;
+    }
+
+    boolean dumpOomLocked(FileDescriptor fd, PrintWriter pw, String[] args,
+            int opti, boolean dumpAll) {
+        boolean needSep = false;
+
+        if (mLruProcesses.size() > 0) {
+            ArrayList<ProcessRecord> procs = new ArrayList<ProcessRecord>(mLruProcesses);
+
+            Comparator<ProcessRecord> comparator = new Comparator<ProcessRecord>() {
+                @Override
+                public int compare(ProcessRecord object1, ProcessRecord object2) {
+                    if (object1.setAdj != object2.setAdj) {
+                        return object1.setAdj > object2.setAdj ? -1 : 1;
+                    }
+                    if (object1.setSchedGroup != object2.setSchedGroup) {
+                        return object1.setSchedGroup > object2.setSchedGroup ? -1 : 1;
+                    }
+                    if (object1.keeping != object2.keeping) {
+                        return object1.keeping ? -1 : 1;
+                    }
+                    if (object1.pid != object2.pid) {
+                        return object1.pid > object2.pid ? -1 : 1;
+                    }
+                    return 0;
+                }
+            };
+
+            Collections.sort(procs, comparator);
+
+            if (needSep) pw.println(" ");
+            needSep = true;
+            pw.println("  Process OOM control:");
+            dumpProcessOomList(pw, this, procs, "    ",
+                    "Proc", "PERS", true);
+            needSep = true;
+        }
+
+        needSep = dumpProcessesToGc(fd, pw, args, opti, needSep, dumpAll);
+
+        pw.println(" ");
+        pw.println("  mHomeProcess: " + mHomeProcess);
+        if (mHeavyWeightProcess != null) {
+            pw.println("  mHeavyWeightProcess: " + mHeavyWeightProcess);
+        }
+
+        return true;
+    }
+
     /**
      * There are three ways to call this:
      *  - no service specified: dump all the services
@@ -7833,84 +7927,14 @@
     
     private static final int dumpProcessList(PrintWriter pw,
             ActivityManagerService service, List list,
-            String prefix, String normalLabel, String persistentLabel,
-            boolean inclOomAdj) {
+            String prefix, String normalLabel, String persistentLabel) {
         int numPers = 0;
         final int N = list.size()-1;
         for (int i=N; i>=0; i--) {
             ProcessRecord r = (ProcessRecord)list.get(i);
-            if (false) {
-                pw.println(prefix + (r.persistent ? persistentLabel : normalLabel)
-                      + " #" + i + ":");
-                r.dump(pw, prefix + "  ");
-            } else if (inclOomAdj) {
-                String oomAdj;
-                if (r.setAdj >= EMPTY_APP_ADJ) {
-                    oomAdj = buildOomTag("empty", null, r.setAdj, EMPTY_APP_ADJ);
-                } else if (r.setAdj >= HIDDEN_APP_MIN_ADJ) {
-                    oomAdj = buildOomTag("bak", "  ", r.setAdj, HIDDEN_APP_MIN_ADJ);
-                } else if (r.setAdj >= HOME_APP_ADJ) {
-                    oomAdj = buildOomTag("home ", null, r.setAdj, HOME_APP_ADJ);
-                } else if (r.setAdj >= SECONDARY_SERVER_ADJ) {
-                    oomAdj = buildOomTag("svc", "  ", r.setAdj, SECONDARY_SERVER_ADJ);
-                } else if (r.setAdj >= BACKUP_APP_ADJ) {
-                    oomAdj = buildOomTag("bckup", null, r.setAdj, BACKUP_APP_ADJ);
-                } else if (r.setAdj >= HEAVY_WEIGHT_APP_ADJ) {
-                    oomAdj = buildOomTag("hvy  ", null, r.setAdj, HEAVY_WEIGHT_APP_ADJ);
-                } else if (r.setAdj >= PERCEPTIBLE_APP_ADJ) {
-                    oomAdj = buildOomTag("prcp ", null, r.setAdj, PERCEPTIBLE_APP_ADJ);
-                } else if (r.setAdj >= VISIBLE_APP_ADJ) {
-                    oomAdj = buildOomTag("vis  ", null, r.setAdj, VISIBLE_APP_ADJ);
-                } else if (r.setAdj >= FOREGROUND_APP_ADJ) {
-                    oomAdj = buildOomTag("fore ", null, r.setAdj, FOREGROUND_APP_ADJ);
-                } else if (r.setAdj >= CORE_SERVER_ADJ) {
-                    oomAdj = buildOomTag("core ", null, r.setAdj, CORE_SERVER_ADJ);
-                } else if (r.setAdj >= SYSTEM_ADJ) {
-                    oomAdj = buildOomTag("sys  ", null, r.setAdj, SYSTEM_ADJ);
-                } else {
-                    oomAdj = Integer.toString(r.setAdj);
-                }
-                String schedGroup;
-                switch (r.setSchedGroup) {
-                    case Process.THREAD_GROUP_BG_NONINTERACTIVE:
-                        schedGroup = "B";
-                        break;
-                    case Process.THREAD_GROUP_DEFAULT:
-                        schedGroup = "F";
-                        break;
-                    default:
-                        schedGroup = Integer.toString(r.setSchedGroup);
-                        break;
-                }
-                pw.println(String.format("%s%s #%2d: adj=%s/%s %s (%s)",
-                        prefix, (r.persistent ? persistentLabel : normalLabel),
-                        N-i, oomAdj, schedGroup, r.toShortString(), r.adjType));
-                if (r.adjSource != null || r.adjTarget != null) {
-                    pw.print(prefix);
-                    pw.print("          ");
-                    if (r.adjTarget instanceof ComponentName) {
-                        pw.print(((ComponentName)r.adjTarget).flattenToShortString());
-                    } else if (r.adjTarget != null) {
-                        pw.print(r.adjTarget.toString());
-                    } else {
-                        pw.print("{null}");
-                    }
-                    pw.print("<=");
-                    if (r.adjSource instanceof ProcessRecord) {
-                        pw.print("Proc{");
-                        pw.print(((ProcessRecord)r.adjSource).toShortString());
-                        pw.println("}");
-                    } else if (r.adjSource != null) {
-                        pw.println(r.adjSource.toString());
-                    } else {
-                        pw.println("{null}");
-                    }
-                }
-            } else {
-                pw.println(String.format("%s%s #%2d: %s",
-                        prefix, (r.persistent ? persistentLabel : normalLabel),
-                        i, r.toString()));
-            }
+            pw.println(String.format("%s%s #%2d: %s",
+                    prefix, (r.persistent ? persistentLabel : normalLabel),
+                    i, r.toString()));
             if (r.persistent) {
                 numPers++;
             }
@@ -7918,6 +7942,132 @@
         return numPers;
     }
 
+    private static final void dumpProcessOomList(PrintWriter pw,
+            ActivityManagerService service, List<ProcessRecord> list,
+            String prefix, String normalLabel, String persistentLabel,
+            boolean inclDetails) {
+
+        final long curRealtime = SystemClock.elapsedRealtime();
+        final long realtimeSince = curRealtime - service.mLastPowerCheckRealtime;
+        final long curUptime = SystemClock.uptimeMillis();
+        final long uptimeSince = curUptime - service.mLastPowerCheckUptime;
+
+        final int N = list.size()-1;
+        for (int i=N; i>=0; i--) {
+            ProcessRecord r = list.get(i);
+            String oomAdj;
+            if (r.setAdj >= EMPTY_APP_ADJ) {
+                oomAdj = buildOomTag("empty", null, r.setAdj, EMPTY_APP_ADJ);
+            } else if (r.setAdj >= HIDDEN_APP_MIN_ADJ) {
+                oomAdj = buildOomTag("bak", "  ", r.setAdj, HIDDEN_APP_MIN_ADJ);
+            } else if (r.setAdj >= HOME_APP_ADJ) {
+                oomAdj = buildOomTag("home ", null, r.setAdj, HOME_APP_ADJ);
+            } else if (r.setAdj >= SECONDARY_SERVER_ADJ) {
+                oomAdj = buildOomTag("svc", "  ", r.setAdj, SECONDARY_SERVER_ADJ);
+            } else if (r.setAdj >= BACKUP_APP_ADJ) {
+                oomAdj = buildOomTag("bckup", null, r.setAdj, BACKUP_APP_ADJ);
+            } else if (r.setAdj >= HEAVY_WEIGHT_APP_ADJ) {
+                oomAdj = buildOomTag("hvy  ", null, r.setAdj, HEAVY_WEIGHT_APP_ADJ);
+            } else if (r.setAdj >= PERCEPTIBLE_APP_ADJ) {
+                oomAdj = buildOomTag("prcp ", null, r.setAdj, PERCEPTIBLE_APP_ADJ);
+            } else if (r.setAdj >= VISIBLE_APP_ADJ) {
+                oomAdj = buildOomTag("vis  ", null, r.setAdj, VISIBLE_APP_ADJ);
+            } else if (r.setAdj >= FOREGROUND_APP_ADJ) {
+                oomAdj = buildOomTag("fore ", null, r.setAdj, FOREGROUND_APP_ADJ);
+            } else if (r.setAdj >= CORE_SERVER_ADJ) {
+                oomAdj = buildOomTag("core ", null, r.setAdj, CORE_SERVER_ADJ);
+            } else if (r.setAdj >= SYSTEM_ADJ) {
+                oomAdj = buildOomTag("sys  ", null, r.setAdj, SYSTEM_ADJ);
+            } else {
+                oomAdj = Integer.toString(r.setAdj);
+            }
+            String schedGroup;
+            switch (r.setSchedGroup) {
+                case Process.THREAD_GROUP_BG_NONINTERACTIVE:
+                    schedGroup = "B";
+                    break;
+                case Process.THREAD_GROUP_DEFAULT:
+                    schedGroup = "F";
+                    break;
+                default:
+                    schedGroup = Integer.toString(r.setSchedGroup);
+                    break;
+            }
+            pw.println(String.format("%s%s #%2d: adj=%s/%s %s (%s)",
+                    prefix, (r.persistent ? persistentLabel : normalLabel),
+                    N-i, oomAdj, schedGroup, r.toShortString(), r.adjType));
+            if (r.adjSource != null || r.adjTarget != null) {
+                pw.print(prefix);
+                pw.print("          ");
+                if (r.adjTarget instanceof ComponentName) {
+                    pw.print(((ComponentName)r.adjTarget).flattenToShortString());
+                } else if (r.adjTarget != null) {
+                    pw.print(r.adjTarget.toString());
+                } else {
+                    pw.print("{null}");
+                }
+                pw.print("<=");
+                if (r.adjSource instanceof ProcessRecord) {
+                    pw.print("Proc{");
+                    pw.print(((ProcessRecord)r.adjSource).toShortString());
+                    pw.println("}");
+                } else if (r.adjSource != null) {
+                    pw.println(r.adjSource.toString());
+                } else {
+                    pw.println("{null}");
+                }
+            }
+            if (inclDetails) {
+                pw.print(prefix);
+                pw.print("    ");
+                pw.print("oom: max="); pw.print(r.maxAdj);
+                pw.print(" hidden="); pw.print(r.hiddenAdj);
+                pw.print(" curRaw="); pw.print(r.curRawAdj);
+                pw.print(" setRaw="); pw.print(r.setRawAdj);
+                pw.print(" cur="); pw.print(r.curAdj);
+                pw.print(" set="); pw.println(r.setAdj);
+                pw.print(prefix);
+                pw.print("    ");
+                pw.print("keeping="); pw.print(r.keeping);
+                pw.print(" hidden="); pw.print(r.hidden);
+                pw.print(" empty="); pw.println(r.empty);
+
+                if (!r.keeping) {
+                    if (r.lastWakeTime != 0) {
+                        long wtime;
+                        BatteryStatsImpl stats = service.mBatteryStatsService.getActiveStatistics();
+                        synchronized (stats) {
+                            wtime = stats.getProcessWakeTime(r.info.uid,
+                                    r.pid, curRealtime);
+                        }
+                        long timeUsed = wtime - r.lastWakeTime;
+                        pw.print(prefix);
+                        pw.print("    ");
+                        pw.print("keep awake over ");
+                        TimeUtils.formatDuration(realtimeSince, pw);
+                        pw.print(" used ");
+                        TimeUtils.formatDuration(timeUsed, pw);
+                        pw.print(" (");
+                        pw.print((timeUsed*100)/realtimeSince);
+                        pw.println("%)");
+                    }
+                    if (r.lastCpuTime != 0) {
+                        long timeUsed = r.curCpuTime - r.lastCpuTime;
+                        pw.print(prefix);
+                        pw.print("    ");
+                        pw.print("run cpu over ");
+                        TimeUtils.formatDuration(uptimeSince, pw);
+                        pw.print(" used ");
+                        TimeUtils.formatDuration(timeUsed, pw);
+                        pw.print(" (");
+                        pw.print((timeUsed*100)/uptimeSince);
+                        pw.println("%)");
+                    }
+                }
+            }
+        }
+    }
+
     static final void dumpApplicationMemoryUsage(FileDescriptor fd,
             PrintWriter pw, List list, String prefix, String[] args) {
         final boolean isCheckinRequest = scanArgs(args, "--checkin");
@@ -8544,7 +8694,11 @@
         return null;
     }
 
-    private final void bumpServiceExecutingLocked(ServiceRecord r) {
+    private final void bumpServiceExecutingLocked(ServiceRecord r, String why) {
+        if (DEBUG_SERVICE) Log.v(TAG, ">>> EXECUTING "
+                + why + " of " + r + " in app " + r.app);
+        else if (DEBUG_SERVICE_EXECUTING) Log.v(TAG, ">>> EXECUTING "
+                + why + " of " + r.shortName);
         long now = SystemClock.uptimeMillis();
         if (r.executeNesting == 0 && r.app != null) {
             if (r.app.executingServices.size() == 0) {
@@ -8582,8 +8736,7 @@
                     grantUriPermissionUncheckedFromIntentLocked(si.targetPermissionUid,
                             r.packageName, si.intent, si.getUriPermissionsLocked());
                 }
-                if (DEBUG_SERVICE) Slog.v(TAG, ">>> EXECUTING start of " + r);
-                bumpServiceExecutingLocked(r);
+                bumpServiceExecutingLocked(r, "start");
                 if (!oomAdjusted) {
                     oomAdjusted = true;
                     updateOomAdjLocked(r.app);
@@ -8616,9 +8769,7 @@
         }
         if ((!i.requested || rebind) && i.apps.size() > 0) {
             try {
-                if (DEBUG_SERVICE) Slog.v(TAG, ">>> EXECUTING bind of " + r
-                        + " in " + i + ": shouldUnbind=" + i.hasBound);
-                bumpServiceExecutingLocked(r);
+                bumpServiceExecutingLocked(r, "bind");
                 r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind);
                 if (!rebind) {
                     i.requested = true;
@@ -8653,8 +8804,7 @@
         r.restartTime = r.lastActivity = SystemClock.uptimeMillis();
 
         app.services.add(r);
-        if (DEBUG_SERVICE) Slog.v(TAG, ">>> EXECUTING create of " + r + " " + r.intent);
-        bumpServiceExecutingLocked(r);
+        bumpServiceExecutingLocked(r, "create");
         updateLruProcessLocked(app, true, true);
 
         boolean created = false;
@@ -8906,9 +9056,7 @@
                         + ": hasBound=" + ibr.hasBound);
                 if (r.app != null && r.app.thread != null && ibr.hasBound) {
                     try {
-                        if (DEBUG_SERVICE) Slog.v(TAG, ">>> EXECUTING bring down unbind of " + r
-                                + " for " + ibr);
-                        bumpServiceExecutingLocked(r);
+                        bumpServiceExecutingLocked(r, "bring down unbind");
                         updateOomAdjLocked(r.app);
                         ibr.hasBound = false;
                         r.app.thread.scheduleUnbindService(r,
@@ -8959,12 +9107,7 @@
             r.app.services.remove(r);
             if (r.app.thread != null) {
                 try {
-                    if (DEBUG_SERVICE) {
-                        RuntimeException here = new RuntimeException();
-                        here.fillInStackTrace();
-                        Slog.v(TAG, ">>> EXECUTING stop of " + r, here);
-                    }
-                    bumpServiceExecutingLocked(r);
+                    bumpServiceExecutingLocked(r, "stop");
                     mStoppingServices.add(r);
                     updateOomAdjLocked(r.app);
                     r.app.thread.scheduleStopService(r);
@@ -9420,9 +9563,7 @@
         if (s.app != null && s.app.thread != null && b.intent.apps.size() == 0
                 && b.intent.hasBound) {
             try {
-                if (DEBUG_SERVICE) Slog.v(TAG, ">>> EXECUTING unbind of " + s
-                        + " from " + b);
-                bumpServiceExecutingLocked(s);
+                bumpServiceExecutingLocked(s, "unbind");
                 updateOomAdjLocked(s.app);
                 b.intent.hasBound = false;
                 // Assume the client doesn't want to know about a rebind;
@@ -9643,14 +9784,20 @@
         if (DEBUG_SERVICE) Slog.v(TAG, "<<< DONE EXECUTING " + r
                 + ": nesting=" + r.executeNesting
                 + ", inStopping=" + inStopping + ", app=" + r.app);
+        else if (DEBUG_SERVICE_EXECUTING) Slog.v(TAG, "<<< DONE EXECUTING " + r.shortName);
         r.executeNesting--;
         if (r.executeNesting <= 0 && r.app != null) {
+            if (DEBUG_SERVICE) Slog.v(TAG,
+                    "Nesting at 0 of " + r.shortName);
             r.app.executingServices.remove(r);
             if (r.app.executingServices.size() == 0) {
+                if (DEBUG_SERVICE || DEBUG_SERVICE_EXECUTING) Slog.v(TAG,
+                        "No more executingServices of " + r.shortName);
                 mHandler.removeMessages(SERVICE_TIMEOUT_MSG, r.app);
             }
             if (inStopping) {
-                if (DEBUG_SERVICE) Slog.v(TAG, "doneExecuting remove stopping " + r);
+                if (DEBUG_SERVICE) Slog.v(TAG,
+                        "doneExecuting remove stopping " + r);
                 mStoppingServices.remove(r);
             }
             updateOomAdjLocked(r.app);
@@ -9760,20 +9907,20 @@
                 Slog.e(TAG, "Backup agent created for " + agentPackageName + " but not requested!");
                 return;
             }
+        }
 
-            long oldIdent = Binder.clearCallingIdentity();
-            try {
-                IBackupManager bm = IBackupManager.Stub.asInterface(
-                        ServiceManager.getService(Context.BACKUP_SERVICE));
-                bm.agentConnected(agentPackageName, agent);
-            } catch (RemoteException e) {
-                // can't happen; the backup manager service is local
-            } catch (Exception e) {
-                Slog.w(TAG, "Exception trying to deliver BackupAgent binding: ");
-                e.printStackTrace();
-            } finally {
-                Binder.restoreCallingIdentity(oldIdent);
-            }
+        long oldIdent = Binder.clearCallingIdentity();
+        try {
+            IBackupManager bm = IBackupManager.Stub.asInterface(
+                    ServiceManager.getService(Context.BACKUP_SERVICE));
+            bm.agentConnected(agentPackageName, agent);
+        } catch (RemoteException e) {
+            // can't happen; the backup manager service is local
+        } catch (Exception e) {
+            Slog.w(TAG, "Exception trying to deliver BackupAgent binding: ");
+            e.printStackTrace();
+        } finally {
+            Binder.restoreCallingIdentity(oldIdent);
         }
     }
 
@@ -11326,6 +11473,7 @@
             app.adjType = "fixed";
             app.adjSeq = mAdjSeq;
             app.curRawAdj = app.maxAdj;
+            app.keeping = true;
             app.curSchedGroup = Process.THREAD_GROUP_DEFAULT;
             return (app.curAdj=app.maxAdj);
        }
@@ -11333,6 +11481,7 @@
         app.adjTypeCode = ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN;
         app.adjSource = null;
         app.adjTarget = null;
+        app.keeping = false;
         app.empty = false;
         app.hidden = false;
 
@@ -11462,6 +11611,9 @@
                     if (adj > SECONDARY_SERVER_ADJ) {
                         app.adjType = "started-bg-services";
                     }
+                    // Don't kill this process because it is doing work; it
+                    // has said it is doing work.
+                    app.keeping = true;
                 }
                 if (s.connections.size() > 0 && (adj > FOREGROUND_APP_ADJ
                         || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE)) {
@@ -11495,6 +11647,9 @@
                                     if (!client.hidden) {
                                         app.hidden = false;
                                     }
+                                    if (client.keeping) {
+                                        app.keeping = true;
+                                    }
                                     app.adjType = "service";
                                     app.adjTypeCode = ActivityManager.RunningAppProcessInfo
                                             .REASON_SERVICE_IN_USE;
@@ -11528,7 +11683,7 @@
                 }
             }
             
-            // Finally, f this process has active services running in it, we
+            // Finally, if this process has active services running in it, we
             // would like to avoid killing it unless it would prevent the current
             // application from running.  By default we put the process in
             // with the rest of the background processes; as we scan through
@@ -11570,6 +11725,9 @@
                             if (!client.hidden) {
                                 app.hidden = false;
                             }
+                            if (client.keeping) {
+                                app.keeping = true;
+                            }
                             app.adjType = "provider";
                             app.adjTypeCode = ActivityManager.RunningAppProcessInfo
                                     .REASON_PROVIDER_IN_USE;
@@ -11589,6 +11747,7 @@
                         adj = FOREGROUND_APP_ADJ;
                         schedGroup = Process.THREAD_GROUP_DEFAULT;
                         app.hidden = false;
+                        app.keeping = true;
                         app.adjType = "provider";
                         app.adjTarget = cpr.name;
                     }
@@ -11606,6 +11765,9 @@
                 schedGroup = Process.THREAD_GROUP_DEFAULT;
             }
         }
+        if (adj < HIDDEN_APP_MIN_ADJ) {
+            app.keeping = true;
+        }
 
         app.curAdj = adj;
         app.curSchedGroup = schedGroup;
@@ -11743,57 +11905,99 @@
         }
     }
 
-    final void checkExcessiveWakeLocksLocked(boolean doKills) {
+    final void checkExcessivePowerUsageLocked(boolean doKills) {
+        updateCpuStatsNow();
+
         BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
-        if (mLastWakeLockCheckTime == 0) {
-            doKills = false;
+        boolean doWakeKills = doKills;
+        boolean doCpuKills = doKills;
+        if (mLastPowerCheckRealtime == 0) {
+            doWakeKills = false;
+        }
+        if (mLastPowerCheckUptime == 0) {
+            doCpuKills = false;
         }
         if (stats.isScreenOn()) {
-            doKills = false;
+            doWakeKills = false;
         }
         final long curRealtime = SystemClock.elapsedRealtime();
-        final long timeSince = curRealtime - mLastWakeLockCheckTime;
-        mLastWakeLockCheckTime = curRealtime;
-        if (timeSince < (WAKE_LOCK_CHECK_DELAY/3)) {
-            doKills = false;
+        final long realtimeSince = curRealtime - mLastPowerCheckRealtime;
+        final long curUptime = SystemClock.uptimeMillis();
+        final long uptimeSince = curUptime - mLastPowerCheckUptime;
+        mLastPowerCheckRealtime = curRealtime;
+        mLastPowerCheckUptime = curUptime;
+        if (realtimeSince < WAKE_LOCK_MIN_CHECK_DURATION) {
+            doWakeKills = false;
+        }
+        if (uptimeSince < CPU_MIN_CHECK_DURATION) {
+            doCpuKills = false;
         }
         int i = mLruProcesses.size();
         while (i > 0) {
             i--;
             ProcessRecord app = mLruProcesses.get(i);
-            if (app.curAdj >= HIDDEN_APP_MIN_ADJ) {
+            if (!app.keeping) {
                 long wtime;
                 synchronized (stats) {
                     wtime = stats.getProcessWakeTime(app.info.uid,
                             app.pid, curRealtime);
                 }
-                long timeUsed = wtime - app.lastWakeTime;
-                if (false) {
+                long wtimeUsed = wtime - app.lastWakeTime;
+                long cputimeUsed = app.curCpuTime - app.lastCpuTime;
+                if (DEBUG_POWER) {
                     StringBuilder sb = new StringBuilder(128);
                     sb.append("Wake for ");
                     app.toShortString(sb);
                     sb.append(": over ");
-                    TimeUtils.formatDuration(timeSince, sb);
+                    TimeUtils.formatDuration(realtimeSince, sb);
                     sb.append(" used ");
-                    TimeUtils.formatDuration(timeUsed, sb);
+                    TimeUtils.formatDuration(wtimeUsed, sb);
                     sb.append(" (");
-                    sb.append((timeUsed*100)/timeSince);
+                    sb.append((wtimeUsed*100)/realtimeSince);
+                    sb.append("%)");
+                    Slog.i(TAG, sb.toString());
+                    sb.setLength(0);
+                    sb.append("CPU for ");
+                    app.toShortString(sb);
+                    sb.append(": over ");
+                    TimeUtils.formatDuration(uptimeSince, sb);
+                    sb.append(" used ");
+                    TimeUtils.formatDuration(cputimeUsed, sb);
+                    sb.append(" (");
+                    sb.append((cputimeUsed*100)/uptimeSince);
                     sb.append("%)");
                     Slog.i(TAG, sb.toString());
                 }
                 // If a process has held a wake lock for more
                 // than 50% of the time during this period,
                 // that sounds pad.  Kill!
-                if (doKills && timeSince > 0
-                        && ((timeUsed*100)/timeSince) >= 50) {
-                    Slog.i(TAG, "Excessive wake lock in " + app.processName
-                            + " (pid " + app.pid + "): held " + timeUsed
-                            + " during " + timeSince);
+                if (doWakeKills && realtimeSince > 0
+                        && ((wtimeUsed*100)/realtimeSince) >= 50) {
+                    synchronized (stats) {
+                        stats.reportExcessiveWakeLocked(app.info.uid, app.processName,
+                                realtimeSince, wtimeUsed);
+                    }
+                    Slog.w(TAG, "Excessive wake lock in " + app.processName
+                            + " (pid " + app.pid + "): held " + wtimeUsed
+                            + " during " + realtimeSince);
                     EventLog.writeEvent(EventLogTags.AM_KILL, app.pid,
                             app.processName, app.setAdj, "excessive wake lock");
                     Process.killProcessQuiet(app.pid);
+                } else if (doCpuKills && uptimeSince > 0
+                        && ((cputimeUsed*100)/uptimeSince) >= 50) {
+                    synchronized (stats) {
+                        stats.reportExcessiveCpuLocked(app.info.uid, app.processName,
+                                uptimeSince, cputimeUsed);
+                    }
+                    Slog.w(TAG, "Excessive CPU in " + app.processName
+                            + " (pid " + app.pid + "): used " + cputimeUsed
+                            + " during " + uptimeSince);
+                    EventLog.writeEvent(EventLogTags.AM_KILL, app.pid,
+                            app.processName, app.setAdj, "excessive cpu");
+                    Process.killProcessQuiet(app.pid);
                 } else {
                     app.lastWakeTime = wtime;
+                    app.lastCpuTime = app.curCpuTime;
                 }
             }
         }
@@ -11807,6 +12011,8 @@
             return true;
         }
 
+        final boolean wasKeeping = app.keeping;
+
         int adj = computeOomAdjLocked(app, hiddenAdj, TOP_APP, false);
 
         if ((app.pid != 0 && app.pid != MY_PID) || Process.supportsProcesses()) {
@@ -11821,13 +12027,20 @@
                     // Likewise do a gc when an app is moving in to the
                     // background (such as a service stopping).
                     scheduleAppGcLocked(app);
-                    // And note its current wake lock time.
+                }
+
+                if (wasKeeping && !app.keeping) {
+                    // This app is no longer something we want to keep.  Note
+                    // its current wake lock time to later know to kill it if
+                    // it is not behaving well.
                     BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
                     synchronized (stats) {
                         app.lastWakeTime = stats.getProcessWakeTime(app.info.uid,
                                 app.pid, SystemClock.elapsedRealtime());
                     }
+                    app.lastCpuTime = app.curCpuTime;
                 }
+
                 app.setRawAdj = app.curRawAdj;
             }
             if (adj != app.setAdj) {
diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java
index 67df707..404c6be 100644
--- a/services/java/com/android/server/am/ProcessRecord.java
+++ b/services/java/com/android/server/am/ProcessRecord.java
@@ -60,6 +60,7 @@
     int setAdj;                 // Last set OOM adjustment for this process
     int curSchedGroup;          // Currently desired scheduling class
     int setSchedGroup;          // Last set to background scheduling class
+    boolean keeping;            // Actively running code so don't kill due to that?
     boolean setIsForeground;    // Running foreground UI when last set?
     boolean foregroundServices; // Running any services that are foreground?
     boolean bad;                // True if disabled in the bad process list
@@ -75,6 +76,8 @@
     ComponentName instrumentationResultClass;// copy of instrumentationClass
     BroadcastRecord curReceiver;// receiver currently running in the app
     long lastWakeTime;          // How long proc held wake lock at last check
+    long lastCpuTime;           // How long proc has run CPU at last check
+    long curCpuTime;            // How long proc has run CPU most recently
     long lastRequestedGc;       // When we last asked the app to do a gc
     long lastLowMemory;         // When we last told the app that memory is low
     boolean reportLowMemory;    // Set to true when waiting to report low mem
@@ -131,13 +134,6 @@
     void dump(PrintWriter pw, String prefix) {
         final long now = SystemClock.uptimeMillis();
 
-        long wtime;
-        synchronized (batteryStats.getBatteryStats()) {
-            wtime = batteryStats.getBatteryStats().getProcessWakeTime(info.uid,
-                    pid, SystemClock.elapsedRealtime());
-        }
-        long timeUsed = wtime - lastWakeTime;
-
         if (info.className != null) {
             pw.print(prefix); pw.print("class="); pw.println(info.className);
         }
@@ -170,6 +166,7 @@
         pw.print(prefix); pw.print("lastActivityTime=");
                 TimeUtils.formatDuration(lastActivityTime, now, pw);
                 pw.print(" lruWeight="); pw.print(lruWeight);
+                pw.print(" keeping="); pw.print(keeping);
                 pw.print(" hidden="); pw.print(hidden);
                 pw.print(" empty="); pw.println(empty);
         pw.print(prefix); pw.print("oom: max="); pw.print(maxAdj);
@@ -188,9 +185,20 @@
                 pw.print(" persistentActivities="); pw.println(persistentActivities);
         pw.print(prefix); pw.print("adjSeq="); pw.print(adjSeq);
                 pw.print(" lruSeq="); pw.println(lruSeq);
-        pw.print(prefix); pw.print("lastWakeTime="); pw.print(lastWakeTime);
-                pw.print(" time used=");
-                TimeUtils.formatDuration(timeUsed, pw); pw.println("");
+        if (!keeping) {
+            long wtime;
+            synchronized (batteryStats.getBatteryStats()) {
+                wtime = batteryStats.getBatteryStats().getProcessWakeTime(info.uid,
+                        pid, SystemClock.elapsedRealtime());
+            }
+            long timeUsed = wtime - lastWakeTime;
+            pw.print(prefix); pw.print("lastWakeTime="); pw.print(lastWakeTime);
+                    pw.print(" time used=");
+                    TimeUtils.formatDuration(timeUsed, pw); pw.println("");
+            pw.print(prefix); pw.print("lastCpuTime="); pw.print(lastCpuTime);
+                    pw.print(" time used=");
+                    TimeUtils.formatDuration(curCpuTime-lastCpuTime, pw); pw.println("");
+        }
         pw.print(prefix); pw.print("lastRequestedGc=");
                 TimeUtils.formatDuration(lastRequestedGc, now, pw);
                 pw.print(" lastLowMemory=");
diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java
index f35a68e..e5aceb4 100644
--- a/services/java/com/android/server/am/ServiceRecord.java
+++ b/services/java/com/android/server/am/ServiceRecord.java
@@ -347,7 +347,9 @@
                         // If it gave us a garbage notification, it doesn't
                         // get to be foreground.
                         ams.setServiceForeground(name, ServiceRecord.this,
-                                localForegroundId, null, true);
+                                0, null, true);
+                        ams.crashApplication(appUid, appPid, localPackageName,
+                                "Bad notification for startForeground: " + e);
                     }
                 }
             });
diff --git a/services/java/com/android/server/sip/SipService.java b/services/java/com/android/server/sip/SipService.java
index f1dcd5a..3f43e1c 100644
--- a/services/java/com/android/server/sip/SipService.java
+++ b/services/java/com/android/server/sip/SipService.java
@@ -30,8 +30,8 @@
 import android.net.sip.SipErrorCode;
 import android.net.sip.SipManager;
 import android.net.sip.SipProfile;
+import android.net.sip.SipSession;
 import android.net.sip.SipSessionAdapter;
-import android.net.sip.SipSessionState;
 import android.net.wifi.WifiManager;
 import android.os.Binder;
 import android.os.Bundle;
@@ -143,7 +143,7 @@
     }
 
     private void openToReceiveCalls(SipProfile localProfile) {
-        open3(localProfile, SipManager.SIP_INCOMING_CALL_ACTION, null);
+        open3(localProfile, SipManager.ACTION_SIP_INCOMING_CALL, null);
     }
 
     public synchronized void open3(SipProfile localProfile,
@@ -255,15 +255,15 @@
 
     private void notifyProfileAdded(SipProfile localProfile) {
         if (DEBUG) Log.d(TAG, "notify: profile added: " + localProfile);
-        Intent intent = new Intent(SipManager.SIP_ADD_PHONE_ACTION);
-        intent.putExtra(SipManager.LOCAL_URI_KEY, localProfile.getUriString());
+        Intent intent = new Intent(SipManager.ACTION_SIP_ADD_PHONE);
+        intent.putExtra(SipManager.EXTRA_LOCAL_URI, localProfile.getUriString());
         mContext.sendBroadcast(intent);
     }
 
     private void notifyProfileRemoved(SipProfile localProfile) {
         if (DEBUG) Log.d(TAG, "notify: profile removed: " + localProfile);
-        Intent intent = new Intent(SipManager.SIP_REMOVE_PHONE_ACTION);
-        intent.putExtra(SipManager.LOCAL_URI_KEY, localProfile.getUriString());
+        Intent intent = new Intent(SipManager.ACTION_SIP_REMOVE_PHONE);
+        intent.putExtra(SipManager.EXTRA_LOCAL_URI, localProfile.getUriString());
         mContext.sendBroadcast(intent);
     }
 
@@ -474,8 +474,8 @@
                     // send out incoming call broadcast
                     addPendingSession(session);
                     Intent intent = SipManager.createIncomingCallBroadcast(
-                            mIncomingCallBroadcastAction, session.getCallId(),
-                            sessionDescription);
+                            session.getCallId(), sessionDescription)
+                            .setAction(mIncomingCallBroadcastAction);
                     if (DEBUG) Log.d(TAG, " ringing~~ " + getUri() + ": "
                             + caller.getUri() + ": " + session.getCallId()
                             + " " + mIncomingCallBroadcastAction);
@@ -613,10 +613,10 @@
 
                 try {
                     int state = (mSession == null)
-                            ? SipSessionState.READY_TO_CALL
+                            ? SipSession.State.READY_TO_CALL
                             : mSession.getState();
-                    if ((state == SipSessionState.REGISTERING)
-                            || (state == SipSessionState.DEREGISTERING)) {
+                    if ((state == SipSession.State.REGISTERING)
+                            || (state == SipSession.State.DEREGISTERING)) {
                         mProxy.onRegistering(mSession);
                     } else if (mRegistered) {
                         int duration = (int)
@@ -1138,7 +1138,8 @@
                 event.mTriggerTime += event.mPeriod;
 
                 // run the callback in a new thread to prevent deadlock
-                new Thread(event.mCallback).start();
+                new Thread(event.mCallback, "SipServiceTimerCallbackThread")
+                        .start();
             }
             if (DEBUG_TIMER) {
                 Log.d(TAG, "after timeout execution");
diff --git a/services/java/com/android/server/sip/SipSessionGroup.java b/services/java/com/android/server/sip/SipSessionGroup.java
index b4c2241..66a2c05 100644
--- a/services/java/com/android/server/sip/SipSessionGroup.java
+++ b/services/java/com/android/server/sip/SipSessionGroup.java
@@ -25,11 +25,10 @@
 
 import android.net.sip.ISipSession;
 import android.net.sip.ISipSessionListener;
-import android.net.sip.SessionDescription;
 import android.net.sip.SipErrorCode;
 import android.net.sip.SipProfile;
+import android.net.sip.SipSession;
 import android.net.sip.SipSessionAdapter;
-import android.net.sip.SipSessionState;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -121,7 +120,7 @@
         reset(localIp);
     }
 
-    void reset(String localIp) throws SipException, IOException {
+    synchronized void reset(String localIp) throws SipException, IOException {
         mLocalIp = localIp;
         if (localIp == null) return;
 
@@ -301,7 +300,7 @@
             boolean processed = (session != null) && session.process(event);
             if (isLoggable && processed) {
                 Log.d(TAG, "new state after: "
-                        + SipSessionState.toString(session.mState));
+                        + SipSession.State.toString(session.mState));
             }
         } catch (Throwable e) {
             Log.w(TAG, "event process error: " + event, e);
@@ -332,7 +331,7 @@
 
         public boolean process(EventObject evt) throws SipException {
             if (isLoggable(this, evt)) Log.d(TAG, " ~~~~~   " + this + ": "
-                    + SipSessionState.toString(mState) + ": processing "
+                    + SipSession.State.toString(mState) + ": processing "
                     + log(evt));
             if (isRequestEvent(Request.INVITE, evt)) {
                 RequestEvent event = (RequestEvent) evt;
@@ -342,13 +341,16 @@
                 newSession.mDialog = newSession.mServerTransaction.getDialog();
                 newSession.mInviteReceived = event;
                 newSession.mPeerProfile = createPeerProfile(event.getRequest());
-                newSession.mState = SipSessionState.INCOMING_CALL;
+                newSession.mState = SipSession.State.INCOMING_CALL;
                 newSession.mPeerSessionDescription =
                         extractContent(event.getRequest());
                 addSipSession(newSession);
                 mProxy.onRinging(newSession, newSession.mPeerProfile,
                         newSession.mPeerSessionDescription);
                 return true;
+            } else if (isRequestEvent(Request.OPTIONS, evt)) {
+                mSipHelper.sendResponse((RequestEvent) evt, Response.OK);
+                return true;
             } else {
                 return false;
             }
@@ -358,7 +360,7 @@
     class SipSessionImpl extends ISipSession.Stub {
         SipProfile mPeerProfile;
         SipSessionListenerProxy mProxy = new SipSessionListenerProxy();
-        int mState = SipSessionState.READY_TO_CALL;
+        int mState = SipSession.State.READY_TO_CALL;
         RequestEvent mInviteReceived;
         Dialog mDialog;
         ServerTransaction mServerTransaction;
@@ -378,7 +380,7 @@
                         sleep(timeout);
                         if (mRunning) timeout();
                     }
-                }).start();
+                }, "SipSessionTimerThread").start();
             }
 
             synchronized void cancel() {
@@ -413,7 +415,7 @@
             mInCall = false;
             removeSipSession(this);
             mPeerProfile = null;
-            mState = SipSessionState.READY_TO_CALL;
+            mState = SipSession.State.READY_TO_CALL;
             mInviteReceived = null;
             mDialog = null;
             mServerTransaction = null;
@@ -470,7 +472,7 @@
                             onError(e);
                         }
                     }
-            }).start();
+            }, "SipSessionAsyncCmdThread").start();
         }
 
         public void makeCall(SipProfile peerProfile, String sessionDescription,
@@ -520,10 +522,10 @@
         }
 
         public void sendKeepAlive() {
-            mState = SipSessionState.PINGING;
+            mState = SipSession.State.PINGING;
             try {
                 processCommand(new OptionsCommand());
-                while (SipSessionState.PINGING == mState) {
+                while (SipSession.State.PINGING == mState) {
                     Thread.sleep(1000);
                 }
             } catch (SipException e) {
@@ -550,7 +552,7 @@
             try {
                 String s = super.toString();
                 return s.substring(s.indexOf("@")) + ":"
-                        + SipSessionState.toString(mState);
+                        + SipSession.State.toString(mState);
             } catch (Throwable e) {
                 return super.toString();
             }
@@ -558,7 +560,7 @@
 
         public boolean process(EventObject evt) throws SipException {
             if (isLoggable(this, evt)) Log.d(TAG, " ~~~~~   " + this + ": "
-                    + SipSessionState.toString(mState) + ": processing "
+                    + SipSession.State.toString(mState) + ": processing "
                     + log(evt));
             synchronized (SipSessionGroup.this) {
                 if (isClosed()) return false;
@@ -574,30 +576,30 @@
                 boolean processed;
 
                 switch (mState) {
-                case SipSessionState.REGISTERING:
-                case SipSessionState.DEREGISTERING:
+                case SipSession.State.REGISTERING:
+                case SipSession.State.DEREGISTERING:
                     processed = registeringToReady(evt);
                     break;
-                case SipSessionState.PINGING:
+                case SipSession.State.PINGING:
                     processed = keepAliveProcess(evt);
                     break;
-                case SipSessionState.READY_TO_CALL:
+                case SipSession.State.READY_TO_CALL:
                     processed = readyForCall(evt);
                     break;
-                case SipSessionState.INCOMING_CALL:
+                case SipSession.State.INCOMING_CALL:
                     processed = incomingCall(evt);
                     break;
-                case SipSessionState.INCOMING_CALL_ANSWERING:
+                case SipSession.State.INCOMING_CALL_ANSWERING:
                     processed = incomingCallToInCall(evt);
                     break;
-                case SipSessionState.OUTGOING_CALL:
-                case SipSessionState.OUTGOING_CALL_RING_BACK:
+                case SipSession.State.OUTGOING_CALL:
+                case SipSession.State.OUTGOING_CALL_RING_BACK:
                     processed = outgoingCall(evt);
                     break;
-                case SipSessionState.OUTGOING_CALL_CANCELING:
+                case SipSession.State.OUTGOING_CALL_CANCELING:
                     processed = outgoingCallToReady(evt);
                     break;
-                case SipSessionState.IN_CALL:
+                case SipSession.State.IN_CALL:
                     processed = inCall(evt);
                     break;
                 default:
@@ -625,6 +627,9 @@
                             (TransactionTerminatedEvent) evt);
                 }
                 return true;
+            } else if (isRequestEvent(Request.OPTIONS, evt)) {
+                mSipHelper.sendResponse((RequestEvent) evt, Response.OK);
+                return true;
             } else if (evt instanceof DialogTerminatedEvent) {
                 processDialogTerminated((DialogTerminatedEvent) evt);
                 return true;
@@ -644,8 +649,8 @@
         private void processTransactionTerminated(
                 TransactionTerminatedEvent event) {
             switch (mState) {
-                case SipSessionState.IN_CALL:
-                case SipSessionState.READY_TO_CALL:
+                case SipSession.State.IN_CALL:
+                case SipSession.State.READY_TO_CALL:
                     Log.d(TAG, "Transaction terminated; do nothing");
                     break;
                 default:
@@ -664,27 +669,27 @@
                     ? event.getServerTransaction()
                     : event.getClientTransaction();
 
-            if ((current != target) && (mState != SipSessionState.PINGING)) {
+            if ((current != target) && (mState != SipSession.State.PINGING)) {
                 Log.d(TAG, "not the current transaction; current=" + current
                         + ", timed out=" + target);
                 return;
             }
             switch (mState) {
-                case SipSessionState.REGISTERING:
-                case SipSessionState.DEREGISTERING:
+                case SipSession.State.REGISTERING:
+                case SipSession.State.DEREGISTERING:
                     reset();
                     mProxy.onRegistrationTimeout(this);
                     break;
-                case SipSessionState.INCOMING_CALL:
-                case SipSessionState.INCOMING_CALL_ANSWERING:
-                case SipSessionState.OUTGOING_CALL:
-                case SipSessionState.OUTGOING_CALL_CANCELING:
+                case SipSession.State.INCOMING_CALL:
+                case SipSession.State.INCOMING_CALL_ANSWERING:
+                case SipSession.State.OUTGOING_CALL:
+                case SipSession.State.OUTGOING_CALL_CANCELING:
                     onError(SipErrorCode.TIME_OUT, event.toString());
                     break;
-                case SipSessionState.PINGING:
+                case SipSession.State.PINGING:
                     reset();
                     mReRegisterFlag = true;
-                    mState = SipSessionState.READY_TO_CALL;
+                    mState = SipSession.State.READY_TO_CALL;
                     break;
 
                 default:
@@ -758,7 +763,7 @@
                 switch (statusCode) {
                 case Response.OK:
                     int state = mState;
-                    onRegistrationDone((state == SipSessionState.REGISTERING)
+                    onRegistrationDone((state == SipSession.State.REGISTERING)
                             ? getExpiryTime(((ResponseEvent) evt).getResponse())
                             : -1);
                     mLastNonce = null;
@@ -845,7 +850,7 @@
                         generateTag());
                 mDialog = mClientTransaction.getDialog();
                 addSipSession(this);
-                mState = SipSessionState.OUTGOING_CALL;
+                mState = SipSession.State.OUTGOING_CALL;
                 mProxy.onCalling(this);
                 startSessionTimer(cmd.getTimeout());
                 return true;
@@ -855,7 +860,7 @@
                         generateTag(), duration);
                 mDialog = mClientTransaction.getDialog();
                 addSipSession(this);
-                mState = SipSessionState.REGISTERING;
+                mState = SipSession.State.REGISTERING;
                 mProxy.onRegistering(this);
                 return true;
             } else if (DEREGISTER == evt) {
@@ -863,7 +868,7 @@
                         generateTag(), 0);
                 mDialog = mClientTransaction.getDialog();
                 addSipSession(this);
-                mState = SipSessionState.DEREGISTERING;
+                mState = SipSession.State.DEREGISTERING;
                 mProxy.onRegistering(this);
                 return true;
             }
@@ -878,7 +883,7 @@
                         mLocalProfile,
                         ((MakeCallCommand) evt).getSessionDescription(),
                         mServerTransaction);
-                mState = SipSessionState.INCOMING_CALL_ANSWERING;
+                mState = SipSession.State.INCOMING_CALL_ANSWERING;
                 startSessionTimer(((MakeCallCommand) evt).getTimeout());
                 return true;
             } else if (END_CALL == evt) {
@@ -919,8 +924,8 @@
                 int statusCode = response.getStatusCode();
                 switch (statusCode) {
                 case Response.RINGING:
-                    if (mState == SipSessionState.OUTGOING_CALL) {
-                        mState = SipSessionState.OUTGOING_CALL_RING_BACK;
+                    if (mState == SipSession.State.OUTGOING_CALL) {
+                        mState = SipSession.State.OUTGOING_CALL_RING_BACK;
                         mProxy.onRingingBack(this);
                         cancelSessionTimer();
                     }
@@ -963,7 +968,7 @@
                 // response comes back yet. We are cheating for not checking
                 // response.
                 mSipHelper.sendCancel(mClientTransaction);
-                mState = SipSessionState.OUTGOING_CALL_CANCELING;
+                mState = SipSession.State.OUTGOING_CALL_CANCELING;
                 startSessionTimer(CANCEL_CALL_TIMER);
                 return true;
             }
@@ -1019,7 +1024,7 @@
             } else if (isRequestEvent(Request.INVITE, evt)) {
                 // got Re-INVITE
                 RequestEvent event = mInviteReceived = (RequestEvent) evt;
-                mState = SipSessionState.INCOMING_CALL;
+                mState = SipSession.State.INCOMING_CALL;
                 mPeerSessionDescription = extractContent(event.getRequest());
                 mServerTransaction = null;
                 mProxy.onRinging(this, mPeerProfile, mPeerSessionDescription);
@@ -1032,7 +1037,7 @@
                 // to change call
                 mClientTransaction = mSipHelper.sendReinvite(mDialog,
                         ((MakeCallCommand) evt).getSessionDescription());
-                mState = SipSessionState.OUTGOING_CALL;
+                mState = SipSession.State.OUTGOING_CALL;
                 startSessionTimer(((MakeCallCommand) evt).getTimeout());
                 return true;
             }
@@ -1060,14 +1065,14 @@
         }
 
         private void establishCall() {
-            mState = SipSessionState.IN_CALL;
+            mState = SipSession.State.IN_CALL;
             mInCall = true;
             cancelSessionTimer();
             mProxy.onCallEstablished(this, mPeerSessionDescription);
         }
 
         private void fallbackToPreviousInCall(int errorCode, String message) {
-            mState = SipSessionState.IN_CALL;
+            mState = SipSession.State.IN_CALL;
             mProxy.onCallChangeFailed(this, errorCode, message);
         }
 
@@ -1089,8 +1094,8 @@
         private void onError(int errorCode, String message) {
             cancelSessionTimer();
             switch (mState) {
-                case SipSessionState.REGISTERING:
-                case SipSessionState.DEREGISTERING:
+                case SipSession.State.REGISTERING:
+                case SipSession.State.DEREGISTERING:
                     onRegistrationFailed(errorCode, message);
                     break;
                 default:
@@ -1264,7 +1269,7 @@
     private static boolean isLoggable(SipSessionImpl s) {
         if (s != null) {
             switch (s.mState) {
-                case SipSessionState.PINGING:
+                case SipSession.State.PINGING:
                     return DEBUG_PING;
             }
         }
diff --git a/services/java/com/android/server/sip/SipSessionListenerProxy.java b/services/java/com/android/server/sip/SipSessionListenerProxy.java
index a4cd102..f8be0a8 100644
--- a/services/java/com/android/server/sip/SipSessionListenerProxy.java
+++ b/services/java/com/android/server/sip/SipSessionListenerProxy.java
@@ -40,7 +40,7 @@
         // One thread for each calling back.
         // Note: Guarantee ordering if the issue becomes important. Currently,
         // the chance of handling two callback events at a time is none.
-        new Thread(runnable).start();
+        new Thread(runnable, "SipSessionCallbackThread").start();
     }
 
     public void onCalling(final ISipSession session) {
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 129be4e..ff887e4 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -21,6 +21,7 @@
 #include <sys/types.h>
 
 #include <utils/Errors.h>
+#include <utils/String8.h>
 
 #include <hardware/hardware.h>
 
@@ -100,5 +101,25 @@
     return mList ? mList->hwLayers : 0;
 }
 
+void HWComposer::dump(String8& result, char* buffer, size_t SIZE) const {
+    if (mHwc && mList) {
+        result.append("Hardware Composer state:\n");
+
+        snprintf(buffer, SIZE, "  numHwLayers=%u, flags=%08x\n",
+                mList->numHwLayers, mList->flags);
+        result.append(buffer);
+
+        for (size_t i=0 ; i<mList->numHwLayers ; i++) {
+            const hwc_layer_t& l(mList->hwLayers[i]);
+            snprintf(buffer, SIZE, "  %8s | %08x | %08x | %02x | %04x | [%5d,%5d,%5d,%5d] |  [%5d,%5d,%5d,%5d]\n",
+                    l.compositionType ? "OVERLAY" : "FB",
+                    l.hints, l.flags, l.transform, l.blending,
+                    l.sourceCrop.left, l.sourceCrop.top, l.sourceCrop.right, l.sourceCrop.bottom,
+                    l.displayFrame.left, l.displayFrame.top, l.displayFrame.right, l.displayFrame.bottom);
+            result.append(buffer);
+        }
+    }
+}
+
 // ---------------------------------------------------------------------------
 }; // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 22ff10c..5a9e9eb 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -27,6 +27,8 @@
 namespace android {
 // ---------------------------------------------------------------------------
 
+class String8;
+
 class HWComposer
 {
 public:
@@ -54,6 +56,9 @@
     size_t getNumLayers() const;
     hwc_layer_t* getLayers() const;
 
+    // for debugging
+    void dump(String8& out, char* scratch, size_t SIZE) const;
+
 private:
     hw_module_t const*      mModule;
     hwc_composer_device_t*  mHwc;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index b45f6fe..b353bff 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -85,6 +85,7 @@
         mFreezeDisplayTime(0),
         mDebugRegion(0),
         mDebugBackground(0),
+        mDebugDisableHWC(0),
         mDebugInSwapBuffers(0),
         mLastSwapBufferTime(0),
         mDebugInTransaction(0),
@@ -768,6 +769,10 @@
         hwc_layer_t* const cur(hwc.getLayers());
         for (size_t i=0 ; cur && i<count ; i++) {
             currentLayers[i]->setGeometry(&cur[i]);
+            if (mDebugDisableHWC) {
+                cur[i].compositionType = HWC_FRAMEBUFFER;
+                cur[i].flags |= HWC_SKIP_LAYER;
+            }
         }
     }
 }
@@ -901,6 +906,7 @@
                 continue;
             }
         }
+
         const sp<LayerBase>& layer(layers[i]);
         const Region clip(dirty.intersect(layer->visibleRegionScreen));
         if (!clip.isEmpty()) {
@@ -1522,6 +1528,13 @@
             result.append(buffer);
         }
 
+        HWComposer& hwc(hw.getHwComposer());
+        snprintf(buffer, SIZE, "  h/w composer %s and %s\n",
+                hwc.initCheck()==NO_ERROR ? "present" : "not present",
+                mDebugDisableHWC ? "disabled" : "enabled");
+        result.append(buffer);
+        hwc.dump(result, buffer, SIZE);
+
         const GraphicBufferAllocator& alloc(GraphicBufferAllocator::get());
         alloc.dump(result);
 
@@ -1580,6 +1593,11 @@
                 n = data.readInt32();
                 mDebugBackground = n ? 1 : 0;
                 return NO_ERROR;
+            case 1008:  // toggle use of hw composer
+                n = data.readInt32();
+                mDebugDisableHWC = n ? 1 : 0;
+                mHwWorkListDirty = true;
+                // fall-through...
             case 1004:{ // repaint everything
                 Mutex::Autolock _l(mStateLock);
                 const DisplayHardware& hw(graphicPlane(0).displayHardware());
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 8e286e5..551e8e7 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -382,6 +382,7 @@
                 // don't use a lock for these, we don't care
                 int                         mDebugRegion;
                 int                         mDebugBackground;
+                int                         mDebugDisableHWC;
                 volatile nsecs_t            mDebugInSwapBuffers;
                 nsecs_t                     mLastSwapBufferTime;
                 volatile nsecs_t            mDebugInTransaction;
diff --git a/telephony/java/com/android/internal/telephony/sip/SipPhone.java b/telephony/java/com/android/internal/telephony/sip/SipPhone.java
index 9fcf12d..4791fbd 100755
--- a/telephony/java/com/android/internal/telephony/sip/SipPhone.java
+++ b/telephony/java/com/android/internal/telephony/sip/SipPhone.java
@@ -27,7 +27,7 @@
 import android.net.sip.SipException;
 import android.net.sip.SipManager;
 import android.net.sip.SipProfile;
-import android.net.sip.SipSessionState;
+import android.net.sip.SipSession;
 import android.os.AsyncResult;
 import android.os.Handler;
 import android.os.Looper;
@@ -91,7 +91,7 @@
         foregroundCall = new SipCall();
         backgroundCall = new SipCall();
         mProfile = profile;
-        mSipManager = SipManager.getInstance(context);
+        mSipManager = SipManager.newInstance(context);
 
         // FIXME: what's this for SIP?
         //Change the system property
@@ -710,8 +710,8 @@
 
         void dial() throws SipException {
             setState(Call.State.DIALING);
-            mSipAudioCall = mSipManager.makeAudioCall(mContext, mProfile,
-                    mPeer, null, SESSION_TIMEOUT);
+            mSipAudioCall = mSipManager.makeAudioCall(mProfile, mPeer, null,
+                    SESSION_TIMEOUT);
             mSipAudioCall.setRingbackToneEnabled(false);
             mSipAudioCall.setListener(mAdapter);
         }
@@ -807,20 +807,20 @@
         if (sipAudioCall.isOnHold()) return Call.State.HOLDING;
         int sessionState = sipAudioCall.getState();
         switch (sessionState) {
-            case SipSessionState.READY_TO_CALL:            return Call.State.IDLE;
-            case SipSessionState.INCOMING_CALL:
-            case SipSessionState.INCOMING_CALL_ANSWERING:  return Call.State.INCOMING;
-            case SipSessionState.OUTGOING_CALL:            return Call.State.DIALING;
-            case SipSessionState.OUTGOING_CALL_RING_BACK:  return Call.State.ALERTING;
-            case SipSessionState.OUTGOING_CALL_CANCELING:  return Call.State.DISCONNECTING;
-            case SipSessionState.IN_CALL:                  return Call.State.ACTIVE;
+            case SipSession.State.READY_TO_CALL:            return Call.State.IDLE;
+            case SipSession.State.INCOMING_CALL:
+            case SipSession.State.INCOMING_CALL_ANSWERING:  return Call.State.INCOMING;
+            case SipSession.State.OUTGOING_CALL:            return Call.State.DIALING;
+            case SipSession.State.OUTGOING_CALL_RING_BACK:  return Call.State.ALERTING;
+            case SipSession.State.OUTGOING_CALL_CANCELING:  return Call.State.DISCONNECTING;
+            case SipSession.State.IN_CALL:                  return Call.State.ACTIVE;
             default:
                 Log.w(LOG_TAG, "illegal connection state: " + sessionState);
                 return Call.State.DISCONNECTED;
         }
     }
 
-    private abstract class SipAudioCallAdapter extends SipAudioCall.Adapter {
+    private abstract class SipAudioCallAdapter extends SipAudioCall.Listener {
         protected abstract void onCallEnded(Connection.DisconnectCause cause);
         protected abstract void onError(Connection.DisconnectCause cause);
 
diff --git a/tests/BatteryWaster/src/com/android/batterywaster/BatteryWaster.java b/tests/BatteryWaster/src/com/android/batterywaster/BatteryWaster.java
index 48c4520..5fb09a7 100644
--- a/tests/BatteryWaster/src/com/android/batterywaster/BatteryWaster.java
+++ b/tests/BatteryWaster/src/com/android/batterywaster/BatteryWaster.java
@@ -25,6 +25,7 @@
 import android.os.Bundle;
 import android.os.PowerManager;
 import android.view.View;
+import android.view.WindowManager;
 import android.widget.CheckBox;
 import android.widget.TextView;
 
@@ -38,7 +39,6 @@
     TextView mLog;
     DateFormat mDateFormat;
     IntentFilter mFilter;
-    PowerManager.WakeLock mWakeLock;
     PowerManager.WakeLock mPartialWakeLock;
     SpinThread mThread;
 
@@ -65,24 +65,26 @@
         mFilter.addAction(Intent.ACTION_POWER_CONNECTED);
 
         PowerManager pm = (PowerManager)getSystemService(POWER_SERVICE);
-        mWakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK, "BatteryWaster");
-        mWakeLock.setReferenceCounted(false);
         mPartialWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "BatteryWaster");
         mPartialWakeLock.setReferenceCounted(false);
     }
 
     @Override
-    public void onPause() {
-        super.onPause();
-        stopRunning();
+    public void onResume() {
+        super.onResume();
+        if (((CheckBox)findViewById(R.id.checkbox)).isChecked()) {
+            startRunning();
+        }
+        if (((CheckBox)findViewById(R.id.checkbox_wake)).isChecked()) {
+            mWaking = true;
+            updateWakeLock();
+        }
     }
 
     @Override
     public void onDestroy() {
         super.onDestroy();
-        if (mWakeLock.isHeld()) {
-            mWakeLock.release();
-        }
+        stopRunning();
         if (mPartialWakeLock.isHeld()) {
             mPartialWakeLock.release();
         }
@@ -140,13 +142,9 @@
 
     void updateWakeLock() {
         if (mWasting) {
-            if (!mWakeLock.isHeld()) {
-                mWakeLock.acquire();
-            }
+            getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
         } else {
-            if (mWakeLock.isHeld()) {
-                mWakeLock.release();
-            }
+            getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
         }
         if (mWaking) {
             if (!mPartialWakeLock.isHeld()) {
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java
index c0ba9e5..30d255a 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java
@@ -43,9 +43,10 @@
 import android.webkit.SslErrorHandler;
 import android.webkit.WebChromeClient;
 import android.webkit.WebSettings;
+import android.webkit.WebStorage;
+import android.webkit.WebStorage.QuotaUpdater;
 import android.webkit.WebView;
 import android.webkit.WebViewClient;
-import android.webkit.WebStorage.QuotaUpdater;
 
 import java.io.File;
 import java.lang.Thread.UncaughtExceptionHandler;
@@ -327,6 +328,13 @@
         mCurrentAdditionalTextOutput = null;
 
         mCurrentWebView = createWebViewWithJavascriptInterfaces();
+        // When we create the first WebView, we need to pause to wait for the WebView thread to spin
+        // and up and for it to register its message handlers.
+        if (previousWebView == null) {
+            try {
+                Thread.currentThread().sleep(1000);
+            } catch (Exception e) {}
+        }
         setupWebView(mCurrentWebView);
 
         mEventSender.reset(mCurrentWebView);
@@ -386,6 +394,9 @@
 
         // This is asynchronous, but it gets processed by WebCore before it starts loading pages.
         mCurrentWebView.useMockDeviceOrientation();
+
+        // Must do this after setting the AppCache path.
+        WebStorage.getInstance().deleteAllData();
     }
 
     private void startTests() {
diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h
index a1bc241..c5aa573 100644
--- a/tools/aapt/Bundle.h
+++ b/tools/aapt/Bundle.h
@@ -45,7 +45,7 @@
           mRClassDir(NULL), mResourceIntermediatesDir(NULL), mManifestMinSdkVersion(NULL),
           mMinSdkVersion(NULL), mTargetSdkVersion(NULL), mMaxSdkVersion(NULL),
           mVersionCode(NULL), mVersionName(NULL), mCustomPackage(NULL),
-          mMaxResVersion(NULL), mDebugMode(false),
+          mMaxResVersion(NULL), mDebugMode(false), mProduct(NULL),
           mArgc(0), mArgv(NULL)
         {}
     ~Bundle(void) {}
@@ -139,6 +139,8 @@
     void setMaxResVersion(const char * val) { mMaxResVersion = val; }
     bool getDebugMode() { return mDebugMode; }
     void setDebugMode(bool val) { mDebugMode = val; }
+    const char* getProduct() const { return mProduct; }
+    void setProduct(const char * val) { mProduct = val; }
 
     /*
      * Set and get the file specification.
@@ -237,6 +239,7 @@
     const char* mCustomPackage;
     const char* mMaxResVersion;
     bool        mDebugMode;
+    const char* mProduct;
 
     /* file specification */
     int         mArgc;
diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp
index 71c023d..739b01f 100644
--- a/tools/aapt/Main.cpp
+++ b/tools/aapt/Main.cpp
@@ -67,6 +67,7 @@
         "        [-A asset-source-dir]  [-G class-list-file] [-P public-definitions-file] \\\n"
         "        [-S resource-sources [-S resource-sources ...]] "
         "        [-F apk-file] [-J R-file-dir] \\\n"
+        "        [--product product1,product2,...] \\\n"
         "        [raw-files-dir [raw-files-dir] ...]\n"
         "\n"
         "   Package the android resources.  It will read assets and resources that are\n"
@@ -154,6 +155,9 @@
         "       components target the given package.  Useful when used in\n"
         "       conjunction with --rename-manifest-package to fix tests against\n"
         "       a package that has been renamed.\n"
+        "   --product\n"
+        "       Specifies which variant to choose for strings that have\n"
+        "       product variants\n"
         "   --utf16\n"
         "       changes default encoding for resources to UTF-16.  Only useful when API\n"
         "       level is set to 7 or higher where the default encoding is UTF-8.\n");
@@ -484,6 +488,15 @@
                     bundle.setInstrumentationPackageNameOverride(argv[0]);
                 } else if (strcmp(cp, "-auto-add-overlay") == 0) {
                     bundle.setAutoAddOverlay(true);
+                } else if (strcmp(cp, "-product") == 0) {
+                    argc--;
+                    argv++;
+                    if (!argc) {
+                        fprintf(stderr, "ERROR: No argument supplied for '--product' option\n");
+                        wantUsage = true;
+                        goto bail;
+                    }
+                    bundle.setProduct(argv[0]);
                 } else {
                     fprintf(stderr, "ERROR: Unknown option '-%s'\n", cp);
                     wantUsage = true;
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index f40a877..90a6256 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -574,6 +574,7 @@
                         const String16& itemIdent,
                         int32_t curFormat,
                         bool isFormatted,
+                        const String16& product,
                         bool pseudolocalize,
                         const bool overwrite,
                         ResourceTable* outTable)
@@ -606,6 +607,32 @@
     return err;
 }
 
+/*
+ * Returns true if needle is one of the elements in the comma-separated list
+ * haystack, false otherwise.
+ */
+bool isInProductList(const String16& needle, const String16& haystack) {
+    const char16_t *needle2 = needle.string();
+    const char16_t *haystack2 = haystack.string();
+    size_t needlesize = needle.size();
+
+    while (*haystack2 != '\0') {
+        if (strncmp16(haystack2, needle2, needlesize) == 0) {
+            if (haystack2[needlesize] == '\0' || haystack2[needlesize] == ',') {
+                return true;
+            }
+        }
+
+        while (*haystack2 != '\0' && *haystack2 != ',') {
+            haystack2++;
+        }
+        if (*haystack2 == ',') {
+            haystack2++;
+        }
+    }
+
+    return false;
+}
 
 status_t parseAndAddEntry(Bundle* bundle,
                         const sp<AaptFile>& in,
@@ -618,6 +645,7 @@
                         bool curIsStyled,
                         int32_t curFormat,
                         bool isFormatted,
+                        const String16& product,
                         bool pseudolocalize,
                         const bool overwrite,
                         ResourceTable* outTable)
@@ -634,6 +662,47 @@
         return err;
     }
 
+    /*
+     * If a product type was specified on the command line
+     * and also in the string, and the two are not the same,
+     * return without adding the string.
+     */
+
+    const char *bundleProduct = bundle->getProduct();
+    if (bundleProduct == NULL) {
+        bundleProduct = "";
+    }
+
+    if (product.size() != 0) {
+        /*
+         * If the command-line-specified product is empty, only "default"
+         * matches.  Other variants are skipped.  This is so generation
+         * of the R.java file when the product is not known is predictable.
+         */
+
+        if (bundleProduct[0] == '\0') {
+            if (strcmp16(String16("default").string(), product.string()) != 0) {
+                return NO_ERROR;
+            }
+        } else {
+            /*
+             * The command-line product is not empty.
+             * If the product for this string is on the command-line list,
+             * it matches.  "default" also matches, but only if nothing
+             * else has matched already.
+             */
+
+            if (isInProductList(product, String16(bundleProduct))) {
+                ;
+            } else if (strcmp16(String16("default").string(), product.string()) == 0 &&
+                       !outTable->hasBagOrEntry(myPackage, curType, ident)) {
+                ;
+            } else {
+                return NO_ERROR;
+            }
+        }
+    }
+
     NOISY(printf("Adding resource entry l=%c%c c=%c%c orien=%d d=%d id=%s: %s\n",
                  config.language[0], config.language[1],
                  config.country[0], config.country[1],
@@ -713,6 +782,7 @@
     const String16 translatable16("translatable");
     const String16 formatted16("formatted");
     const String16 false16("false");
+    const String16 product16("product");
 
     const String16 myPackage(assets->getPackage());
 
@@ -760,6 +830,7 @@
             bool curIsStyled = false;
             bool curIsPseudolocalizable = false;
             bool curIsFormatted = fileIsTranslatable;
+            String16 curProduct;
             bool localHasErrors = false;
 
             if (strcmp16(block.getElementName(&len), skip16.string()) == 0) {
@@ -1157,6 +1228,8 @@
                         translatable.setTo(block.getAttributeStringValue(i, &length));
                     } else if (strcmp16(attr, formatted16.string()) == 0) {
                         formatted.setTo(block.getAttributeStringValue(i, &length));
+                    } else if (strcmp16(attr, product16.string()) == 0) {
+                        curProduct.setTo(block.getAttributeStringValue(i, &length));
                     }
                 }
                 
@@ -1374,7 +1447,7 @@
 
                         err = parseAndAddBag(bundle, in, &block, curParams, myPackage, curType,
                                 ident, parentIdent, itemIdent, curFormat, curIsFormatted,
-                                false, overwrite, outTable);
+                                curProduct, false, overwrite, outTable);
                         if (err == NO_ERROR) {
                             if (curIsPseudolocalizable && localeIsDefined(curParams)
                                     && bundle->getPseudolocalize()) {
@@ -1383,7 +1456,7 @@
                                 block.setPosition(parserPosition);
                                 err = parseAndAddBag(bundle, in, &block, pseudoParams, myPackage,
                                         curType, ident, parentIdent, itemIdent, curFormat,
-                                        curIsFormatted, true, overwrite, outTable);
+                                        curIsFormatted, curProduct, true, overwrite, outTable);
 #endif
                             }
                         } 
@@ -1407,7 +1480,7 @@
 
                 err = parseAndAddEntry(bundle, in, &block, curParams, myPackage, curType, ident,
                         *curTag, curIsStyled, curFormat, curIsFormatted,
-                        false, overwrite, outTable);
+                        curProduct, false, overwrite, outTable);
 
                 if (err < NO_ERROR) { // Why err < NO_ERROR instead of err != NO_ERROR?
                     hasErrors = localHasErrors = true;
@@ -1419,7 +1492,8 @@
                         block.setPosition(parserPosition);
                         err = parseAndAddEntry(bundle, in, &block, pseudoParams, myPackage, curType,
                                 ident, *curTag, curIsStyled, curFormat,
-                                curIsFormatted, true, overwrite, outTable);
+                                curIsFormatted, curProduct,
+                                true, overwrite, outTable);
                         if (err != NO_ERROR) {
                             hasErrors = localHasErrors = true;
                         }
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/SdpSessionDescription.java b/voip/java/android/net/sip/SdpSessionDescription.java
deleted file mode 100644
index f6ae837..0000000
--- a/voip/java/android/net/sip/SdpSessionDescription.java
+++ /dev/null
@@ -1,428 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.sip;
-
-import gov.nist.javax.sdp.SessionDescriptionImpl;
-import gov.nist.javax.sdp.fields.AttributeField;
-import gov.nist.javax.sdp.fields.ConnectionField;
-import gov.nist.javax.sdp.fields.MediaField;
-import gov.nist.javax.sdp.fields.OriginField;
-import gov.nist.javax.sdp.fields.ProtoVersionField;
-import gov.nist.javax.sdp.fields.SessionNameField;
-import gov.nist.javax.sdp.fields.TimeField;
-import gov.nist.javax.sdp.parser.SDPAnnounceParser;
-
-import android.util.Log;
-
-import java.text.ParseException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Vector;
-import javax.sdp.Connection;
-import javax.sdp.MediaDescription;
-import javax.sdp.SdpException;
-
-/**
- * A session description that follows SDP (Session Description Protocol).
- * Refer to <a href="http://tools.ietf.org/html/rfc4566">RFC 4566</a>.
- * @hide
- */
-public class SdpSessionDescription extends SessionDescription {
-    private static final String TAG = "SDP";
-    private static final String AUDIO = "audio";
-    private static final String RTPMAP = "rtpmap";
-    private static final String PTIME = "ptime";
-    private static final String SENDONLY = "sendonly";
-    private static final String RECVONLY = "recvonly";
-    private static final String INACTIVE = "inactive";
-
-    private SessionDescriptionImpl mSessionDescription;
-
-    /**
-     * The audio codec information parsed from "rtpmap".
-     */
-    public static class AudioCodec {
-        public final int payloadType;
-        public final String name;
-        public final int sampleRate;
-        public final int sampleCount;
-
-        public AudioCodec(int payloadType, String name, int sampleRate,
-                int sampleCount) {
-            this.payloadType = payloadType;
-            this.name = name;
-            this.sampleRate = sampleRate;
-            this.sampleCount = sampleCount;
-        }
-    }
-
-    /**
-     * The builder class used to create an {@link SdpSessionDescription} object.
-     */
-    public static class Builder {
-        private SdpSessionDescription mSdp = new SdpSessionDescription();
-        private SessionDescriptionImpl mSessionDescription;
-
-        public Builder(String sessionName) throws SdpException {
-            mSessionDescription = new SessionDescriptionImpl();
-            mSdp.mSessionDescription = mSessionDescription;
-            try {
-                ProtoVersionField proto = new ProtoVersionField();
-                proto.setVersion(0);
-                mSessionDescription.addField(proto);
-
-                TimeField time = new TimeField();
-                time.setZero();
-                mSessionDescription.addField(time);
-
-                SessionNameField session = new SessionNameField();
-                session.setValue(sessionName);
-                mSessionDescription.addField(session);
-            } catch (Exception e) {
-                throwSdpException(e);
-            }
-        }
-
-        public Builder setConnectionInfo(String networkType, String addressType,
-                String addr) throws SdpException {
-            try {
-                ConnectionField connection = new ConnectionField();
-                connection.setNetworkType(networkType);
-                connection.setAddressType(addressType);
-                connection.setAddress(addr);
-                mSessionDescription.addField(connection);
-            } catch (Exception e) {
-                throwSdpException(e);
-            }
-            return this;
-        }
-
-        public Builder setOrigin(SipProfile user, long sessionId,
-                long sessionVersion, String networkType, String addressType,
-                String address) throws SdpException {
-            try {
-                OriginField origin = new OriginField();
-                origin.setUsername(user.getUserName());
-                origin.setSessionId(sessionId);
-                origin.setSessionVersion(sessionVersion);
-                origin.setAddressType(addressType);
-                origin.setNetworkType(networkType);
-                origin.setAddress(address);
-                mSessionDescription.addField(origin);
-            } catch (Exception e) {
-                throwSdpException(e);
-            }
-            return this;
-        }
-
-        public Builder addMedia(String media, int port, int numPorts,
-                String transport, Integer... types) throws SdpException {
-            MediaField field = new MediaField();
-            Vector<Integer> typeVector = new Vector<Integer>();
-            Collections.addAll(typeVector, types);
-            try {
-                field.setMediaType(media);
-                field.setMediaPort(port);
-                field.setPortCount(numPorts);
-                field.setProtocol(transport);
-                field.setMediaFormats(typeVector);
-                mSessionDescription.addField(field);
-            } catch (Exception e) {
-                throwSdpException(e);
-            }
-           return this;
-        }
-
-        public Builder addMediaAttribute(String type, String name, String value)
-                throws SdpException {
-            try {
-                MediaDescription md = mSdp.getMediaDescription(type);
-                if (md == null) {
-                    throw new SdpException("Should add media first!");
-                }
-                AttributeField attribute = new AttributeField();
-                attribute.setName(name);
-                attribute.setValueAllowNull(value);
-                mSessionDescription.addField(attribute);
-            } catch (Exception e) {
-                throwSdpException(e);
-            }
-            return this;
-        }
-
-        public Builder addSessionAttribute(String name, String value)
-                throws SdpException {
-            try {
-                AttributeField attribute = new AttributeField();
-                attribute.setName(name);
-                attribute.setValueAllowNull(value);
-                mSessionDescription.addField(attribute);
-            } catch (Exception e) {
-                throwSdpException(e);
-            }
-            return this;
-        }
-
-        private void throwSdpException(Exception e) throws SdpException {
-            if (e instanceof SdpException) {
-                throw (SdpException) e;
-            } else {
-                throw new SdpException(e.toString(), e);
-            }
-        }
-
-        public String build() {
-            return mSdp.toString();
-        }
-    }
-
-    private SdpSessionDescription() {
-    }
-
-    /**
-     * Constructor.
-     *
-     * @param sdpString an SDP session description to parse
-     */
-    public SdpSessionDescription(String sdpString) throws SdpException {
-        try {
-            mSessionDescription = new SDPAnnounceParser(sdpString).parse();
-        } catch (ParseException e) {
-            throw new SdpException(e.toString(), e);
-        }
-        verify();
-    }
-
-    /**
-     * Constructor.
-     *
-     * @param content a raw SDP session description to parse
-     */
-    public SdpSessionDescription(byte[] content) throws SdpException {
-        this(new String(content));
-    }
-
-    private void verify() throws SdpException {
-        // make sure the syntax is correct over the fields we're interested in
-        Vector<MediaDescription> descriptions = (Vector<MediaDescription>)
-                mSessionDescription.getMediaDescriptions(false);
-        for (MediaDescription md : descriptions) {
-            md.getMedia().getMediaPort();
-            Connection connection = md.getConnection();
-            if (connection != null) connection.getAddress();
-            md.getMedia().getFormats();
-        }
-        Connection connection = mSessionDescription.getConnection();
-        if (connection != null) connection.getAddress();
-    }
-
-    /**
-     * Gets the connection address of the media.
-     *
-     * @param type the media type; e.g., "AUDIO"
-     * @return the media connection address of the peer
-     */
-    public String getPeerMediaAddress(String type) {
-        try {
-            MediaDescription md = getMediaDescription(type);
-            Connection connection = md.getConnection();
-            if (connection == null) {
-                connection = mSessionDescription.getConnection();
-            }
-            return ((connection == null) ? null : connection.getAddress());
-        } catch (SdpException e) {
-            // should not occur
-            return null;
-        }
-    }
-
-    /**
-     * Gets the connection port number of the media.
-     *
-     * @param type the media type; e.g., "AUDIO"
-     * @return the media connection port number of the peer
-     */
-    public int getPeerMediaPort(String type) {
-        try {
-            MediaDescription md = getMediaDescription(type);
-            return md.getMedia().getMediaPort();
-        } catch (SdpException e) {
-            // should not occur
-            return -1;
-        }
-    }
-
-    private boolean containsAttribute(String type, String name) {
-        if (name == null) return false;
-        MediaDescription md = getMediaDescription(type);
-        Vector<AttributeField> v = (Vector<AttributeField>)
-                md.getAttributeFields();
-        for (AttributeField field : v) {
-            if (name.equals(field.getAttribute().getName())) return true;
-        }
-        return false;
-    }
-
-    /**
-     * Checks if the media is "sendonly".
-     *
-     * @param type the media type; e.g., "AUDIO"
-     * @return true if the media is "sendonly"
-     */
-    public boolean isSendOnly(String type) {
-        boolean answer = containsAttribute(type, SENDONLY);
-        Log.d(TAG, "   sendonly? " + answer);
-        return answer;
-    }
-
-    /**
-     * Checks if the media is "recvonly".
-     *
-     * @param type the media type; e.g., "AUDIO"
-     * @return true if the media is "recvonly"
-     */
-    public boolean isReceiveOnly(String type) {
-        boolean answer = containsAttribute(type, RECVONLY);
-        Log.d(TAG, "   recvonly? " + answer);
-        return answer;
-    }
-
-    /**
-     * Checks if the media is in sending; i.e., not "recvonly" and not
-     * "inactive".
-     *
-     * @param type the media type; e.g., "AUDIO"
-     * @return true if the media is sending
-     */
-    public boolean isSending(String type) {
-        boolean answer = !containsAttribute(type, RECVONLY)
-                && !containsAttribute(type, INACTIVE);
-
-        Log.d(TAG, "   sending? " + answer);
-        return answer;
-    }
-
-    /**
-     * Checks if the media is in receiving; i.e., not "sendonly" and not
-     * "inactive".
-     *
-     * @param type the media type; e.g., "AUDIO"
-     * @return true if the media is receiving
-     */
-    public boolean isReceiving(String type) {
-        boolean answer = !containsAttribute(type, SENDONLY)
-                && !containsAttribute(type, INACTIVE);
-        Log.d(TAG, "   receiving? " + answer);
-        return answer;
-    }
-
-    private AudioCodec parseAudioCodec(String rtpmap, int ptime) {
-        String[] ss = rtpmap.split(" ");
-        int payloadType = Integer.parseInt(ss[0]);
-
-        ss = ss[1].split("/");
-        String name = ss[0];
-        int sampleRate = Integer.parseInt(ss[1]);
-        int channelCount = 1;
-        if (ss.length > 2) channelCount = Integer.parseInt(ss[2]);
-        int sampleCount = sampleRate / (1000 / ptime) * channelCount;
-        return new AudioCodec(payloadType, name, sampleRate, sampleCount);
-    }
-
-    /**
-     * Gets the list of audio codecs in this session description.
-     *
-     * @return the list of audio codecs in this session description
-     */
-    public List<AudioCodec> getAudioCodecs() {
-        MediaDescription md = getMediaDescription(AUDIO);
-        if (md == null) return new ArrayList<AudioCodec>();
-
-        // FIXME: what happens if ptime is missing
-        int ptime = 20;
-        try {
-            String value = md.getAttribute(PTIME);
-            if (value != null) ptime = Integer.parseInt(value);
-        } catch (Throwable t) {
-            Log.w(TAG, "getCodecs(): ignored: " + t);
-        }
-
-        List<AudioCodec> codecs = new ArrayList<AudioCodec>();
-        Vector<AttributeField> v = (Vector<AttributeField>)
-                md.getAttributeFields();
-        for (AttributeField field : v) {
-            try {
-                if (RTPMAP.equals(field.getName())) {
-                    AudioCodec codec = parseAudioCodec(field.getValue(), ptime);
-                    if (codec != null) codecs.add(codec);
-                }
-            } catch (Throwable t) {
-                Log.w(TAG, "getCodecs(): ignored: " + t);
-            }
-        }
-        return codecs;
-    }
-
-    /**
-     * Gets the media description of the specified type.
-     *
-     * @param type the media type; e.g., "AUDIO"
-     * @return the media description of the specified type
-     */
-    public MediaDescription getMediaDescription(String type) {
-        MediaDescription[] all = getMediaDescriptions();
-        if ((all == null) || (all.length == 0)) return null;
-        for (MediaDescription md : all) {
-            String t = md.getMedia().getMedia();
-            if (t.equalsIgnoreCase(type)) return md;
-        }
-        return null;
-    }
-
-    /**
-     * Gets all the media descriptions in this session description.
-     *
-     * @return all the media descriptions in this session description
-     */
-    public MediaDescription[] getMediaDescriptions() {
-        try {
-            Vector<MediaDescription> descriptions = (Vector<MediaDescription>)
-                    mSessionDescription.getMediaDescriptions(false);
-            MediaDescription[] all = new MediaDescription[descriptions.size()];
-            return descriptions.toArray(all);
-        } catch (SdpException e) {
-            Log.e(TAG, "getMediaDescriptions", e);
-        }
-        return null;
-    }
-
-    @Override
-    public String getType() {
-        return "sdp";
-    }
-
-    @Override
-    public byte[] getContent() {
-          return mSessionDescription.toString().getBytes();
-    }
-
-    @Override
-    public String toString() {
-        return mSessionDescription.toString();
-    }
-}
diff --git a/voip/java/android/net/sip/SessionDescription.java b/voip/java/android/net/sip/SessionDescription.java
deleted file mode 100644
index d476f0b..0000000
--- a/voip/java/android/net/sip/SessionDescription.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.sip;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Abstract class of a session description.
- * @hide
- */
-public abstract class SessionDescription implements Parcelable {
-    /** @hide */
-    public static final Parcelable.Creator<SessionDescription> CREATOR =
-            new Parcelable.Creator<SessionDescription>() {
-                public SessionDescription createFromParcel(Parcel in) {
-                    return new SessionDescriptionImpl(in);
-                }
-
-                public SessionDescription[] newArray(int size) {
-                    return new SessionDescriptionImpl[size];
-                }
-            };
-
-    /**
-     * Gets the type of the session description; e.g., "SDP".
-     *
-     * @return the session description type
-     */
-    public abstract String getType();
-
-    /**
-     * Gets the raw content of the session description.
-     *
-     * @return the content of the session description
-     */
-    public abstract byte[] getContent();
-
-    /** @hide */
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeString(getType());
-        out.writeByteArray(getContent());
-    }
-
-    /** @hide */
-    public int describeContents() {
-        return 0;
-    }
-
-    private static class SessionDescriptionImpl extends SessionDescription {
-        private String mType;
-        private byte[] mContent;
-
-        SessionDescriptionImpl(Parcel in) {
-            mType = in.readString();
-            mContent = in.createByteArray();
-        }
-
-        @Override
-        public String getType() {
-            return mType;
-        }
-
-        @Override
-        public byte[] getContent() {
-            return mContent;
-        }
-    }
-}
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/SipAudioCall.java b/voip/java/android/net/sip/SipAudioCall.java
index 0069fe0..2135fcb 100644
--- a/voip/java/android/net/sip/SipAudioCall.java
+++ b/voip/java/android/net/sip/SipAudioCall.java
@@ -16,120 +16,184 @@
 
 package android.net.sip;
 
+import android.content.Context;
+import android.media.AudioManager;
+import android.media.Ringtone;
+import android.media.RingtoneManager;
+import android.media.ToneGenerator;
+import android.net.Uri;
+import android.net.rtp.AudioCodec;
 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;
+import android.os.Vibrator;
+import android.provider.Settings;
+import android.util.Log;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 
 /**
- * Interface for making audio calls over SIP.
- * @hide
+ * Class that handles an audio call over SIP.
  */
-public interface SipAudioCall {
+/** @hide */
+public class SipAudioCall extends SipSessionAdapter {
+    private static final String TAG = SipAudioCall.class.getSimpleName();
+    private static final boolean RELEASE_SOCKET = true;
+    private static final boolean DONT_RELEASE_SOCKET = false;
+    private static final int SESSION_TIMEOUT = 5; // in seconds
+
     /** Listener class for all event callbacks. */
-    public interface Listener {
+    public static class Listener {
         /**
          * Called when the call object is ready to make another call.
+         * The default implementation calls {@link #onChange}.
          *
          * @param call the call object that is ready to make another call
          */
-        void onReadyToCall(SipAudioCall call);
+        public void onReadyToCall(SipAudioCall call) {
+            onChanged(call);
+        }
 
         /**
          * Called when a request is sent out to initiate a new call.
+         * The default implementation calls {@link #onChange}.
          *
          * @param call the call object that carries out the audio call
          */
-        void onCalling(SipAudioCall call);
+        public void onCalling(SipAudioCall call) {
+            onChanged(call);
+        }
 
         /**
          * Called when a new call comes in.
+         * The default implementation calls {@link #onChange}.
          *
          * @param call the call object that carries out the audio call
          * @param caller the SIP profile of the caller
          */
-        void onRinging(SipAudioCall call, SipProfile caller);
+        public void onRinging(SipAudioCall call, SipProfile caller) {
+            onChanged(call);
+        }
 
         /**
-         * Called when a RINGING response is received for the INVITE request sent
+         * Called when a RINGING response is received for the INVITE request
+         * sent. The default implementation calls {@link #onChange}.
          *
          * @param call the call object that carries out the audio call
          */
-        void onRingingBack(SipAudioCall call);
+        public void onRingingBack(SipAudioCall call) {
+            onChanged(call);
+        }
 
         /**
          * Called when the session is established.
+         * The default implementation calls {@link #onChange}.
          *
          * @param call the call object that carries out the audio call
          */
-        void onCallEstablished(SipAudioCall call);
+        public void onCallEstablished(SipAudioCall call) {
+            onChanged(call);
+        }
 
         /**
          * Called when the session is terminated.
+         * The default implementation calls {@link #onChange}.
          *
          * @param call the call object that carries out the audio call
          */
-        void onCallEnded(SipAudioCall call);
+        public void onCallEnded(SipAudioCall call) {
+            onChanged(call);
+        }
 
         /**
          * Called when the peer is busy during session initialization.
+         * The default implementation calls {@link #onChange}.
          *
          * @param call the call object that carries out the audio call
          */
-        void onCallBusy(SipAudioCall call);
+        public void onCallBusy(SipAudioCall call) {
+            onChanged(call);
+        }
 
         /**
          * Called when the call is on hold.
+         * The default implementation calls {@link #onChange}.
          *
          * @param call the call object that carries out the audio call
          */
-        void onCallHeld(SipAudioCall call);
+        public void onCallHeld(SipAudioCall call) {
+            onChanged(call);
+        }
 
         /**
-         * Called when an error occurs.
+         * Called when an error occurs. The default implementation is no op.
          *
          * @param call the call object that carries out the audio call
          * @param errorCode error code of this error
          * @param errorMessage error message
          * @see SipErrorCode
          */
-        void onError(SipAudioCall call, int errorCode, String errorMessage);
-    }
-
-    /**
-     * The adapter class for {@link Listener}. The default implementation of
-     * all callback methods is no-op.
-     */
-    public class Adapter implements Listener {
-        protected void onChanged(SipAudioCall call) {
-        }
-        public void onReadyToCall(SipAudioCall call) {
-            onChanged(call);
-        }
-        public void onCalling(SipAudioCall call) {
-            onChanged(call);
-        }
-        public void onRinging(SipAudioCall call, SipProfile caller) {
-            onChanged(call);
-        }
-        public void onRingingBack(SipAudioCall call) {
-            onChanged(call);
-        }
-        public void onCallEstablished(SipAudioCall call) {
-            onChanged(call);
-        }
-        public void onCallEnded(SipAudioCall call) {
-            onChanged(call);
-        }
-        public void onCallBusy(SipAudioCall call) {
-            onChanged(call);
-        }
-        public void onCallHeld(SipAudioCall call) {
-            onChanged(call);
-        }
         public void onError(SipAudioCall call, int errorCode,
                 String errorMessage) {
-            onChanged(call);
+            // no-op
         }
+
+        /**
+         * Called when an event occurs and the corresponding callback is not
+         * overridden. The default implementation is no op. Error events are
+         * not re-directed to this callback and are handled in {@link #onError}.
+         */
+        public void onChanged(SipAudioCall call) {
+            // no-op
+        }
+    }
+
+    private Context mContext;
+    private SipProfile mLocalProfile;
+    private SipAudioCall.Listener mListener;
+    private SipSession mSipSession;
+
+    private long mSessionId = System.currentTimeMillis();
+    private String mPeerSd;
+
+    private AudioStream mAudioStream;
+    private AudioGroup mAudioGroup;
+
+    private boolean mInCall = false;
+    private boolean mMuted = false;
+    private boolean mHold = false;
+
+    private boolean mRingbackToneEnabled = true;
+    private boolean mRingtoneEnabled = true;
+    private Ringtone mRingtone;
+    private ToneGenerator mRingbackTone;
+
+    private SipProfile mPendingCallRequest;
+    private WifiManager mWm;
+    private WifiManager.WifiLock mWifiHighPerfLock;
+
+    private int mErrorCode = SipErrorCode.NO_ERROR;
+    private String mErrorMessage;
+
+    /**
+     * Creates a call object with the local SIP profile.
+     * @param context the context for accessing system services such as
+     *        ringtone, audio, WIFI etc
+     */
+    public SipAudioCall(Context context, SipProfile localProfile) {
+        mContext = context;
+        mLocalProfile = localProfile;
+        mWm = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
     }
 
     /**
@@ -139,7 +203,9 @@
      * @param listener to listen to the audio call events of this object
      * @see #setListener(Listener, boolean)
      */
-    void setListener(Listener listener);
+    public void setListener(SipAudioCall.Listener listener) {
+        setListener(listener, false);
+    }
 
     /**
      * Sets the listener to listen to the audio call events. A
@@ -150,12 +216,312 @@
      * @param callbackImmediately set to true if the caller wants to be called
      *      back immediately on the current state
      */
-    void setListener(Listener listener, boolean callbackImmediately);
+    public void setListener(SipAudioCall.Listener listener,
+            boolean callbackImmediately) {
+        mListener = listener;
+        try {
+            if ((listener == null) || !callbackImmediately) {
+                // do nothing
+            } else if (mErrorCode != SipErrorCode.NO_ERROR) {
+                listener.onError(this, mErrorCode, mErrorMessage);
+            } else if (mInCall) {
+                if (mHold) {
+                    listener.onCallHeld(this);
+                } else {
+                    listener.onCallEstablished(this);
+                }
+            } else {
+                int state = getState();
+                switch (state) {
+                    case SipSession.State.READY_TO_CALL:
+                        listener.onReadyToCall(this);
+                        break;
+                    case SipSession.State.INCOMING_CALL:
+                        listener.onRinging(this, getPeerProfile());
+                        break;
+                    case SipSession.State.OUTGOING_CALL:
+                        listener.onCalling(this);
+                        break;
+                    case SipSession.State.OUTGOING_CALL_RING_BACK:
+                        listener.onRingingBack(this);
+                        break;
+                }
+            }
+        } catch (Throwable t) {
+            Log.e(TAG, "setListener()", t);
+        }
+    }
+
+    /**
+     * Checks if the call is established.
+     *
+     * @return true if the call is established
+     */
+    public synchronized boolean isInCall() {
+        return mInCall;
+    }
+
+    /**
+     * Checks if the call is on hold.
+     *
+     * @return true if the call is on hold
+     */
+    public synchronized boolean isOnHold() {
+        return mHold;
+    }
 
     /**
      * Closes this object. This object is not usable after being closed.
      */
-    void close();
+    public void close() {
+        close(true);
+    }
+
+    private synchronized void close(boolean closeRtp) {
+        if (closeRtp) stopCall(RELEASE_SOCKET);
+        stopRingbackTone();
+        stopRinging();
+
+        mInCall = false;
+        mHold = false;
+        mSessionId = System.currentTimeMillis();
+        mErrorCode = SipErrorCode.NO_ERROR;
+        mErrorMessage = null;
+
+        if (mSipSession != null) {
+            mSipSession.setListener(null);
+            mSipSession = null;
+        }
+    }
+
+    /**
+     * Gets the local SIP profile.
+     *
+     * @return the local SIP profile
+     */
+    public synchronized SipProfile getLocalProfile() {
+        return mLocalProfile;
+    }
+
+    /**
+     * Gets the peer's SIP profile.
+     *
+     * @return the peer's SIP profile
+     */
+    public synchronized SipProfile getPeerProfile() {
+        return (mSipSession == null) ? null : mSipSession.getPeerProfile();
+    }
+
+    /**
+     * Gets the state of the {@link SipSession} that carries this call.
+     * The value returned must be one of the states in {@link SipSession.State}.
+     *
+     * @return the session state
+     */
+    public synchronized int getState() {
+        if (mSipSession == null) return SipSession.State.READY_TO_CALL;
+        return mSipSession.getState();
+    }
+
+
+    /**
+     * Gets the {@link SipSession} that carries this call.
+     *
+     * @return the session object that carries this call
+     * @hide
+     */
+    public synchronized SipSession getSipSession() {
+        return mSipSession;
+    }
+
+    private SipSession.Listener createListener() {
+        return new SipSession.Listener() {
+            @Override
+            public void onCalling(SipSession session) {
+                Log.d(TAG, "calling... " + session);
+                Listener listener = mListener;
+                if (listener != null) {
+                    try {
+                        listener.onCalling(SipAudioCall.this);
+                    } catch (Throwable t) {
+                        Log.i(TAG, "onCalling(): " + t);
+                    }
+                }
+            }
+
+            @Override
+            public void onRingingBack(SipSession session) {
+                Log.d(TAG, "sip call ringing back: " + session);
+                if (!mInCall) startRingbackTone();
+                Listener listener = mListener;
+                if (listener != null) {
+                    try {
+                        listener.onRingingBack(SipAudioCall.this);
+                    } catch (Throwable t) {
+                        Log.i(TAG, "onRingingBack(): " + t);
+                    }
+                }
+            }
+
+            @Override
+            public synchronized void onRinging(SipSession session,
+                    SipProfile peerProfile, String sessionDescription) {
+                if ((mSipSession == null) || !mInCall
+                        || !session.getCallId().equals(mSipSession.getCallId())) {
+                    // should not happen
+                    session.endCall();
+                    return;
+                }
+
+                // session changing request
+                try {
+                    String answer = createAnswer(sessionDescription).encode();
+                    mSipSession.answerCall(answer, SESSION_TIMEOUT);
+                } catch (Throwable e) {
+                    Log.e(TAG, "onRinging()", e);
+                    session.endCall();
+                }
+            }
+
+            @Override
+            public void onCallEstablished(SipSession session,
+                    String sessionDescription) {
+                stopRingbackTone();
+                stopRinging();
+                mPeerSd = sessionDescription;
+                Log.v(TAG, "onCallEstablished()" + mPeerSd);
+
+                Listener listener = mListener;
+                if (listener != null) {
+                    try {
+                        if (mHold) {
+                            listener.onCallHeld(SipAudioCall.this);
+                        } else {
+                            listener.onCallEstablished(SipAudioCall.this);
+                        }
+                    } catch (Throwable t) {
+                        Log.i(TAG, "onCallEstablished(): " + t);
+                    }
+                }
+            }
+
+            @Override
+            public void onCallEnded(SipSession session) {
+                Log.d(TAG, "sip call ended: " + session);
+                Listener listener = mListener;
+                if (listener != null) {
+                    try {
+                        listener.onCallEnded(SipAudioCall.this);
+                    } catch (Throwable t) {
+                        Log.i(TAG, "onCallEnded(): " + t);
+                    }
+                }
+                close();
+            }
+
+            @Override
+            public void onCallBusy(SipSession session) {
+                Log.d(TAG, "sip call busy: " + session);
+                Listener listener = mListener;
+                if (listener != null) {
+                    try {
+                        listener.onCallBusy(SipAudioCall.this);
+                    } catch (Throwable t) {
+                        Log.i(TAG, "onCallBusy(): " + t);
+                    }
+                }
+                close(false);
+            }
+
+            @Override
+            public void onCallChangeFailed(SipSession session, int errorCode,
+                    String message) {
+                Log.d(TAG, "sip call change failed: " + message);
+                mErrorCode = errorCode;
+                mErrorMessage = message;
+                Listener listener = mListener;
+                if (listener != null) {
+                    try {
+                        listener.onError(SipAudioCall.this, mErrorCode,
+                                message);
+                    } catch (Throwable t) {
+                        Log.i(TAG, "onCallBusy(): " + t);
+                    }
+                }
+            }
+
+            @Override
+            public void onError(SipSession session, int errorCode,
+                    String message) {
+                SipAudioCall.this.onError(errorCode, message);
+            }
+
+            @Override
+            public void onRegistering(SipSession session) {
+                // irrelevant
+            }
+
+            @Override
+            public void onRegistrationTimeout(SipSession session) {
+                // irrelevant
+            }
+
+            @Override
+            public void onRegistrationFailed(SipSession session, int errorCode,
+                    String message) {
+                // irrelevant
+            }
+
+            @Override
+            public void onRegistrationDone(SipSession session, int duration) {
+                // irrelevant
+            }
+        };
+    }
+
+    private void onError(int errorCode, String message) {
+        Log.d(TAG, "sip session error: "
+                + SipErrorCode.toString(errorCode) + ": " + message);
+        mErrorCode = errorCode;
+        mErrorMessage = message;
+        Listener listener = mListener;
+        if (listener != null) {
+            try {
+                listener.onError(this, errorCode, message);
+            } catch (Throwable t) {
+                Log.i(TAG, "onError(): " + t);
+            }
+        }
+        synchronized (this) {
+            if ((errorCode == SipErrorCode.DATA_CONNECTION_LOST)
+                    || !isInCall()) {
+                close(true);
+            }
+        }
+    }
+
+    /**
+     * Attaches an incoming call to this call object.
+     *
+     * @param session the session that receives the incoming call
+     * @param sessionDescription the session description of the incoming call
+     * @throws SipException if the SIP service fails to attach this object to
+     *        the session
+     */
+    public synchronized void attachCall(SipSession session,
+            String sessionDescription) throws SipException {
+        mSipSession = session;
+        mPeerSd = sessionDescription;
+        Log.v(TAG, "attachCall()" + mPeerSd);
+        try {
+            session.setListener(createListener());
+
+            if (getState() == SipSession.State.INCOMING_CALL) startRinging();
+        } catch (Throwable e) {
+            Log.e(TAG, "attachCall()", e);
+            throwSipException(e);
+        }
+    }
 
     /**
      * Initiates an audio call to the specified profile. The attempt will be
@@ -165,29 +531,40 @@
      *
      * @param callee the SIP profile to make the call to
      * @param sipManager the {@link SipManager} object to help make call with
-     * @param timeout the timeout value in seconds
+     * @param timeout the timeout value in seconds. Default value (defined by
+     *        SIP protocol) is used if {@code timeout} is zero or negative.
      * @see Listener.onError
+     * @throws SipException if the SIP service fails to create a session for the
+     *        call
      */
-    void makeCall(SipProfile callee, SipManager sipManager, int timeout)
-            throws SipException;
+    public synchronized void makeCall(SipProfile peerProfile,
+        SipManager sipManager, int timeout) throws SipException {
+        SipSession s = mSipSession = sipManager.createSipSession(
+                mLocalProfile, createListener());
+        if (s == null) {
+            throw new SipException(
+                    "Failed to create SipSession; network available?");
+        }
+        try {
+            mAudioStream = new AudioStream(InetAddress.getByName(getLocalIp()));
+            s.makeCall(peerProfile, createOffer().encode(), timeout);
+        } catch (IOException e) {
+            throw new SipException("makeCall()", e);
+        }
+    }
 
     /**
-     * Starts the audio for the established call. This method should be called
-     * after {@link Listener#onCallEstablished} is called.
+     * Ends a call.
+     * @throws SipException if the SIP service fails to end the call
      */
-    void startAudio();
+    public synchronized void endCall() throws SipException {
+        stopRinging();
+        stopCall(RELEASE_SOCKET);
+        mInCall = false;
 
-    /**
-     * Attaches an incoming call to this call object.
-     *
-     * @param session the session that receives the incoming call
-     * @param sessionDescription the session description of the incoming call
-     */
-    void attachCall(ISipSession session, String sessionDescription)
-            throws SipException;
-
-    /** Ends a call. */
-    void endCall() throws SipException;
+        // perform the above local ops first and then network op
+        if (mSipSession != null) mSipSession.endCall();
+    }
 
     /**
      * Puts a call on hold.  When succeeds, {@link Listener#onCallHeld} is
@@ -196,10 +573,19 @@
      * {@code Listener.onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
      * will be called.
      *
-     * @param timeout the timeout value in seconds
+     * @param timeout the timeout value in seconds. Default value (defined by
+     *        SIP protocol) is used if {@code timeout} is zero or negative.
      * @see Listener.onError
+     * @throws SipException if the SIP service fails to hold the call
      */
-    void holdCall(int timeout) throws SipException;
+    public synchronized void holdCall(int timeout) throws SipException {
+        if (mHold) return;
+        mSipSession.changeCall(createHoldOffer().encode(), timeout);
+        mHold = true;
+
+        AudioGroup audioGroup = getAudioGroup();
+        if (audioGroup != null) audioGroup.setMode(AudioGroup.MODE_ON_HOLD);
+    }
 
     /**
      * Answers a call. The attempt will be timed out if the call is not
@@ -207,10 +593,20 @@
      * {@code Listener.onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
      * will be called.
      *
-     * @param timeout the timeout value in seconds
+     * @param timeout the timeout value in seconds. Default value (defined by
+     *        SIP protocol) is used if {@code timeout} is zero or negative.
      * @see Listener.onError
+     * @throws SipException if the SIP service fails to answer the call
      */
-    void answerCall(int timeout) throws SipException;
+    public synchronized void answerCall(int timeout) throws SipException {
+        stopRinging();
+        try {
+            mAudioStream = new AudioStream(InetAddress.getByName(getLocalIp()));
+            mSipSession.answerCall(createAnswer(mPeerSd).encode(), timeout);
+        } catch (IOException e) {
+            throw new SipException("answerCall()", e);
+        }
+    }
 
     /**
      * Continues a call that's on hold. When succeeds,
@@ -219,45 +615,191 @@
      * {@code Listener.onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
      * will be called.
      *
-     * @param timeout the timeout value in seconds
+     * @param timeout the timeout value in seconds. Default value (defined by
+     *        SIP protocol) is used if {@code timeout} is zero or negative.
      * @see Listener.onError
+     * @throws SipException if the SIP service fails to unhold the call
      */
-    void continueCall(int timeout) throws SipException;
+    public synchronized void continueCall(int timeout) throws SipException {
+        if (!mHold) return;
+        mSipSession.changeCall(createContinueOffer().encode(), timeout);
+        mHold = false;
+        AudioGroup audioGroup = getAudioGroup();
+        if (audioGroup != null) audioGroup.setMode(AudioGroup.MODE_NORMAL);
+    }
 
-    /** Puts the device to speaker mode. */
-    void setSpeakerMode(boolean speakerMode);
+    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 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);
+
+                    // 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));
+                        }
+                    }
+
+                    // 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;
+                }
+            }
+            // Reject the media.
+            Media reply = answer.newMedia(
+                    media.getType(), 0, 1, media.getProtocol());
+            for (String format : media.getFormats()) {
+                reply.setFormat(format, null);
+            }
+        }
+        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;
+    }
+
+    private void grabWifiHighPerfLock() {
+        /* not available in master yet
+        if (mWifiHighPerfLock == null) {
+            Log.v(TAG, "acquire wifi high perf lock");
+            mWifiHighPerfLock = ((WifiManager)
+                    mContext.getSystemService(Context.WIFI_SERVICE))
+                    .createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, TAG);
+            mWifiHighPerfLock.acquire();
+        }
+        */
+    }
+
+    private void releaseWifiHighPerfLock() {
+        if (mWifiHighPerfLock != null) {
+            Log.v(TAG, "release wifi high perf lock");
+            mWifiHighPerfLock.release();
+            mWifiHighPerfLock = null;
+        }
+    }
+
+    private boolean isWifiOn() {
+        return (mWm.getConnectionInfo().getBSSID() == null) ? false : true;
+    }
 
     /** Toggles mute. */
-    void toggleMute();
-
-    /**
-     * Checks if the call is on hold.
-     *
-     * @return true if the call is on hold
-     */
-    boolean isOnHold();
+    public synchronized void toggleMute() {
+        AudioGroup audioGroup = getAudioGroup();
+        if (audioGroup != null) {
+            audioGroup.setMode(
+                    mMuted ? AudioGroup.MODE_NORMAL : AudioGroup.MODE_MUTED);
+            mMuted = !mMuted;
+        }
+    }
 
     /**
      * Checks if the call is muted.
      *
      * @return true if the call is muted
      */
-    boolean isMuted();
+    public synchronized boolean isMuted() {
+        return mMuted;
+    }
+
+    /** Puts the device to speaker mode. */
+    public synchronized void setSpeakerMode(boolean speakerMode) {
+        ((AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE))
+                .setSpeakerphoneOn(speakerMode);
+    }
 
     /**
-     * Sends a DTMF code.
+     * Sends a DTMF code. According to RFC2833, event 0--9 maps to decimal
+     * value 0--9, '*' to 10, '#' to 11, event 'A'--'D' to 12--15, and event
+     * flash to 16. Currently, event flash is not supported.
      *
-     * @param code the DTMF code to send
+     * @param code the DTMF code to send. Value 0 to 15 (inclusive) are valid
+     *        inputs.
+     * @see http://tools.ietf.org/html/rfc2833
      */
-    void sendDtmf(int code);
+    public void sendDtmf(int code) {
+        sendDtmf(code, null);
+    }
 
     /**
-     * Sends a DTMF code.
+     * Sends a DTMF code. According to RFC2833, event 0--9 maps to decimal
+     * value 0--9, '*' to 10, '#' to 11, event 'A'--'D' to 12--15, and event
+     * flash to 16. Currently, event flash is not supported.
      *
-     * @param code the DTMF code to send
+     * @param code the DTMF code to send. Value 0 to 15 (inclusive) are valid
+     *        inputs.
      * @param result the result message to send when done
      */
-    void sendDtmf(int code, Message result);
+    public synchronized void sendDtmf(int code, Message result) {
+        AudioGroup audioGroup = getAudioGroup();
+        if ((audioGroup != null) && (mSipSession != null)
+                && (SipSession.State.IN_CALL == getState())) {
+            Log.v(TAG, "send DTMF: " + code);
+            audioGroup.sendDtmf(code);
+        }
+        if (result != null) result.sendToTarget();
+    }
 
     /**
      * Gets the {@link AudioStream} object used in this call. The object
@@ -268,8 +810,11 @@
      *
      * @return the {@link AudioStream} object or null if the RTP stream has not
      *      yet been set up
+     * @hide
      */
-    AudioStream getAudioStream();
+    public synchronized AudioStream getAudioStream() {
+        return mAudioStream;
+    }
 
     /**
      * Gets the {@link AudioGroup} object which the {@link AudioStream} object
@@ -283,8 +828,12 @@
      * @return the {@link AudioGroup} object or null if the RTP stream has not
      *      yet been set up
      * @see #getAudioStream
+     * @hide
      */
-    AudioGroup getAudioGroup();
+    public synchronized AudioGroup getAudioGroup() {
+        if (mAudioGroup != null) return mAudioGroup;
+        return ((mAudioStream == null) ? null : mAudioStream.getGroup());
+    }
 
     /**
      * Sets the {@link AudioGroup} object which the {@link AudioStream} object
@@ -292,56 +841,214 @@
      * will be dynamically created when needed.
      *
      * @see #getAudioStream
+     * @hide
      */
-    void setAudioGroup(AudioGroup audioGroup);
+    public synchronized void setAudioGroup(AudioGroup group) {
+        if ((mAudioStream != null) && (mAudioStream.getGroup() != null)) {
+            mAudioStream.join(group);
+        }
+        mAudioGroup = group;
+    }
 
     /**
-     * Checks if the call is established.
-     *
-     * @return true if the call is established
+     * Starts the audio for the established call. This method should be called
+     * after {@link Listener#onCallEstablished} is called.
      */
-    boolean isInCall();
+    public void startAudio() {
+        try {
+            startAudioInternal();
+        } catch (UnknownHostException e) {
+            onError(SipErrorCode.PEER_NOT_REACHABLE, e.getMessage());
+        } catch (Throwable e) {
+            onError(SipErrorCode.CLIENT_ERROR, e.getMessage());
+        }
+    }
 
-    /**
-     * Gets the local SIP profile.
-     *
-     * @return the local SIP profile
-     */
-    SipProfile getLocalProfile();
+    private synchronized void startAudioInternal() throws UnknownHostException {
+        if (mPeerSd == null) {
+            Log.v(TAG, "startAudioInternal() mPeerSd = null");
+            throw new IllegalStateException("mPeerSd = null");
+        }
 
-    /**
-     * Gets the peer's SIP profile.
-     *
-     * @return the peer's SIP profile
-     */
-    SipProfile getPeerProfile();
+        stopCall(DONT_RELEASE_SOCKET);
+        mInCall = true;
 
-    /**
-     * Gets the state of the {@link ISipSession} that carries this call.
-     * The value returned must be one of the states in {@link SipSessionState}.
-     *
-     * @return the session state
-     */
-    int getState();
+        // 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;
+                    }
+                }
 
-    /**
-     * Gets the {@link ISipSession} that carries this call.
-     *
-     * @return the session object that carries this call
-     */
-    ISipSession getSipSession();
+                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());
+
+                    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);
+                        }
+                    }
+
+                    // 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 (isWifiOn()) grabWifiHighPerfLock();
+
+        if (!mHold) {
+            /* 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.
+             */
+            ((AudioManager) mContext.getSystemService
+                    (Context.AUDIO_SERVICE))
+                    .setMode(AudioManager.MODE_NORMAL);
+        }
+
+        // AudioGroup logic:
+        AudioGroup audioGroup = getAudioGroup();
+        if (mHold) {
+            if (audioGroup != null) {
+                audioGroup.setMode(AudioGroup.MODE_ON_HOLD);
+            }
+            // don't create an AudioGroup here; doing so will fail if
+            // there's another AudioGroup out there that's active
+        } else {
+            if (audioGroup == null) audioGroup = new AudioGroup();
+            stream.join(audioGroup);
+            if (mMuted) {
+                audioGroup.setMode(AudioGroup.MODE_MUTED);
+            } else {
+                audioGroup.setMode(AudioGroup.MODE_NORMAL);
+            }
+        }
+    }
+
+    private void stopCall(boolean releaseSocket) {
+        Log.d(TAG, "stop audiocall");
+        releaseWifiHighPerfLock();
+        if (mAudioStream != null) {
+            mAudioStream.join(null);
+
+            if (releaseSocket) {
+                mAudioStream.release();
+                mAudioStream = null;
+            }
+        }
+    }
+
+    private String getLocalIp() {
+        return mSipSession.getLocalIp();
+    }
+
 
     /**
      * Enables/disables the ring-back tone.
      *
      * @param enabled true to enable; false to disable
      */
-    void setRingbackToneEnabled(boolean enabled);
+    public synchronized void setRingbackToneEnabled(boolean enabled) {
+        mRingbackToneEnabled = enabled;
+    }
 
     /**
      * Enables/disables the ring tone.
      *
      * @param enabled true to enable; false to disable
      */
-    void setRingtoneEnabled(boolean enabled);
+    public synchronized void setRingtoneEnabled(boolean enabled) {
+        mRingtoneEnabled = enabled;
+    }
+
+    private void startRingbackTone() {
+        if (!mRingbackToneEnabled) return;
+        if (mRingbackTone == null) {
+            // The volume relative to other sounds in the stream
+            int toneVolume = 80;
+            mRingbackTone = new ToneGenerator(
+                    AudioManager.STREAM_VOICE_CALL, toneVolume);
+        }
+        mRingbackTone.startTone(ToneGenerator.TONE_CDMA_LOW_PBX_L);
+    }
+
+    private void stopRingbackTone() {
+        if (mRingbackTone != null) {
+            mRingbackTone.stopTone();
+            mRingbackTone.release();
+            mRingbackTone = null;
+        }
+    }
+
+    private void startRinging() {
+        if (!mRingtoneEnabled) return;
+        ((Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE))
+                .vibrate(new long[] {0, 1000, 1000}, 1);
+        AudioManager am = (AudioManager)
+                mContext.getSystemService(Context.AUDIO_SERVICE);
+        if (am.getStreamVolume(AudioManager.STREAM_RING) > 0) {
+            String ringtoneUri =
+                    Settings.System.DEFAULT_RINGTONE_URI.toString();
+            mRingtone = RingtoneManager.getRingtone(mContext,
+                    Uri.parse(ringtoneUri));
+            mRingtone.play();
+        }
+    }
+
+    private void stopRinging() {
+        ((Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE))
+                .cancel();
+        if (mRingtone != null) mRingtone.stop();
+    }
+
+    private void throwSipException(Throwable throwable) throws SipException {
+        if (throwable instanceof SipException) {
+            throw (SipException) throwable;
+        } else {
+            throw new SipException("", throwable);
+        }
+    }
+
+    private SipProfile getPeerProfile(SipSession session) {
+        return session.getPeerProfile();
+    }
 }
diff --git a/voip/java/android/net/sip/SipAudioCallImpl.java b/voip/java/android/net/sip/SipAudioCallImpl.java
deleted file mode 100644
index 8cd41db..0000000
--- a/voip/java/android/net/sip/SipAudioCallImpl.java
+++ /dev/null
@@ -1,750 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.sip;
-
-import gov.nist.javax.sdp.fields.SDPKeywords;
-
-import android.content.Context;
-import android.media.AudioManager;
-import android.media.Ringtone;
-import android.media.RingtoneManager;
-import android.media.ToneGenerator;
-import android.net.Uri;
-import android.net.rtp.AudioCodec;
-import android.net.rtp.AudioGroup;
-import android.net.rtp.AudioStream;
-import android.net.rtp.RtpStream;
-import android.net.wifi.WifiManager;
-import android.os.Message;
-import android.os.RemoteException;
-import android.os.Vibrator;
-import android.provider.Settings;
-import android.util.Log;
-
-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. 
- */
-/** @hide */
-public class SipAudioCallImpl extends SipSessionAdapter
-        implements SipAudioCall {
-    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 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;
-
-    private boolean mRingbackToneEnabled = true;
-    private boolean mRingtoneEnabled = true;
-    private Ringtone mRingtone;
-    private ToneGenerator mRingbackTone;
-
-    private SipProfile mPendingCallRequest;
-
-    private int mErrorCode = SipErrorCode.NO_ERROR;
-    private String mErrorMessage;
-
-    public SipAudioCallImpl(Context context, SipProfile localProfile) {
-        mContext = context;
-        mLocalProfile = localProfile;
-    }
-
-    public void setListener(SipAudioCall.Listener listener) {
-        setListener(listener, false);
-    }
-
-    public void setListener(SipAudioCall.Listener listener,
-            boolean callbackImmediately) {
-        mListener = listener;
-        try {
-            if ((listener == null) || !callbackImmediately) {
-                // do nothing
-            } else if (mErrorCode != SipErrorCode.NO_ERROR) {
-                listener.onError(this, mErrorCode, mErrorMessage);
-            } else if (mInCall) {
-                if (mHold) {
-                    listener.onCallHeld(this);
-                } else {
-                    listener.onCallEstablished(this);
-                }
-            } else {
-                int state = getState();
-                switch (state) {
-                    case SipSessionState.READY_TO_CALL:
-                        listener.onReadyToCall(this);
-                        break;
-                    case SipSessionState.INCOMING_CALL:
-                        listener.onRinging(this, getPeerProfile(mSipSession));
-                        break;
-                    case SipSessionState.OUTGOING_CALL:
-                        listener.onCalling(this);
-                        break;
-                    case SipSessionState.OUTGOING_CALL_RING_BACK:
-                        listener.onRingingBack(this);
-                        break;
-                }
-            }
-        } catch (Throwable t) {
-            Log.e(TAG, "setListener()", t);
-        }
-    }
-
-    public synchronized boolean isInCall() {
-        return mInCall;
-    }
-
-    public synchronized boolean isOnHold() {
-        return mHold;
-    }
-
-    public void close() {
-        close(true);
-    }
-
-    private synchronized void close(boolean closeRtp) {
-        if (closeRtp) stopCall(RELEASE_SOCKET);
-        stopRingbackTone();
-        stopRinging();
-
-        mInCall = false;
-        mHold = false;
-        mSessionId = -1L;
-        mErrorCode = SipErrorCode.NO_ERROR;
-        mErrorMessage = null;
-
-        if (mSipSession != null) {
-            try {
-                mSipSession.setListener(null);
-            } catch (RemoteException e) {
-                // don't care
-            }
-            mSipSession = null;
-        }
-    }
-
-    public synchronized SipProfile getLocalProfile() {
-        return mLocalProfile;
-    }
-
-    public synchronized SipProfile getPeerProfile() {
-        try {
-            return (mSipSession == null) ? null : mSipSession.getPeerProfile();
-        } catch (RemoteException e) {
-            return null;
-        }
-    }
-
-    public synchronized int getState() {
-        if (mSipSession == null) return SipSessionState.READY_TO_CALL;
-        try {
-            return mSipSession.getState();
-        } catch (RemoteException e) {
-            return SipSessionState.REMOTE_ERROR;
-        }
-    }
-
-
-    public synchronized ISipSession getSipSession() {
-        return mSipSession;
-    }
-
-    @Override
-    public void onCalling(ISipSession session) {
-        Log.d(TAG, "calling... " + session);
-        Listener listener = mListener;
-        if (listener != null) {
-            try {
-                listener.onCalling(this);
-            } catch (Throwable t) {
-                Log.e(TAG, "onCalling()", t);
-            }
-        }
-    }
-
-    @Override
-    public void onRingingBack(ISipSession session) {
-        Log.d(TAG, "sip call ringing back: " + session);
-        if (!mInCall) startRingbackTone();
-        Listener listener = mListener;
-        if (listener != null) {
-            try {
-                listener.onRingingBack(this);
-            } catch (Throwable t) {
-                Log.e(TAG, "onRingingBack()", t);
-            }
-        }
-    }
-
-    @Override
-    public synchronized void onRinging(ISipSession session,
-            SipProfile peerProfile, String sessionDescription) {
-        try {
-            if ((mSipSession == null) || !mInCall
-                    || !session.getCallId().equals(mSipSession.getCallId())) {
-                // should not happen
-                session.endCall();
-                return;
-            }
-
-            // session changing request
-            try {
-                mPeerSd = new SdpSessionDescription(sessionDescription);
-                answerCall(SESSION_TIMEOUT);
-            } catch (Throwable e) {
-                Log.e(TAG, "onRinging()", e);
-                session.endCall();
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "onRinging()", e);
-        }
-    }
-
-    @Override
-    public void onCallEstablished(ISipSession session,
-            String sessionDescription) {
-        stopRingbackTone();
-        stopRinging();
-        try {
-            mPeerSd = new SdpSessionDescription(sessionDescription);
-            Log.d(TAG, "sip call established: " + mPeerSd);
-        } catch (SdpException e) {
-            Log.e(TAG, "createSessionDescription()", e);
-        }
-
-        Listener listener = mListener;
-        if (listener != null) {
-            try {
-                if (mHold) {
-                    listener.onCallHeld(this);
-                } else {
-                    listener.onCallEstablished(this);
-                }
-            } catch (Throwable t) {
-                Log.e(TAG, "onCallEstablished()", t);
-            }
-        }
-    }
-
-    @Override
-    public void onCallEnded(ISipSession session) {
-        Log.d(TAG, "sip call ended: " + session);
-        Listener listener = mListener;
-        if (listener != null) {
-            try {
-                listener.onCallEnded(this);
-            } catch (Throwable t) {
-                Log.e(TAG, "onCallEnded()", t);
-            }
-        }
-        close();
-    }
-
-    @Override
-    public void onCallBusy(ISipSession session) {
-        Log.d(TAG, "sip call busy: " + session);
-        Listener listener = mListener;
-        if (listener != null) {
-            try {
-                listener.onCallBusy(this);
-            } catch (Throwable t) {
-                Log.e(TAG, "onCallBusy()", t);
-            }
-        }
-        close(false);
-    }
-
-    @Override
-    public void onCallChangeFailed(ISipSession session, int errorCode,
-            String message) {
-        Log.d(TAG, "sip call change failed: " + message);
-        mErrorCode = errorCode;
-        mErrorMessage = message;
-        Listener listener = mListener;
-        if (listener != null) {
-            try {
-                listener.onError(this, mErrorCode, message);
-            } catch (Throwable t) {
-                Log.e(TAG, "onCallBusy()", t);
-            }
-        }
-    }
-
-    @Override
-    public void onError(ISipSession session, int errorCode, String message) {
-        Log.d(TAG, "sip session error: " + SipErrorCode.toString(errorCode)
-                + ": " + message);
-        mErrorCode = errorCode;
-        mErrorMessage = message;
-        Listener listener = mListener;
-        if (listener != null) {
-            try {
-                listener.onError(this, errorCode, message);
-            } catch (Throwable t) {
-                Log.e(TAG, "onError()", t);
-            }
-        }
-        synchronized (this) {
-            if ((errorCode == SipErrorCode.DATA_CONNECTION_LOST)
-                    || !isInCall()) {
-                close(true);
-            }
-        }
-    }
-
-    public synchronized void attachCall(ISipSession session,
-            String sessionDescription) throws SipException {
-        mSipSession = session;
-        try {
-            mPeerSd = new SdpSessionDescription(sessionDescription);
-            session.setListener(this);
-
-            if (getState() == SipSessionState.INCOMING_CALL) startRinging();
-        } catch (Throwable e) {
-            Log.e(TAG, "attachCall()", e);
-            throwSipException(e);
-        }
-    }
-
-    public synchronized void makeCall(SipProfile peerProfile,
-            SipManager sipManager, int timeout) throws SipException {
-        try {
-            mSipSession = sipManager.createSipSession(mLocalProfile, this);
-            if (mSipSession == null) {
-                throw new SipException(
-                        "Failed to create SipSession; network available?");
-            }
-            mSipSession.makeCall(peerProfile, createOfferSessionDescription(),
-                    timeout);
-        } catch (Throwable e) {
-            if (e instanceof SipException) {
-                throw (SipException) e;
-            } else {
-                throwSipException(e);
-            }
-        }
-    }
-
-    public synchronized void endCall() throws SipException {
-        try {
-            stopRinging();
-            stopCall(true);
-            mInCall = false;
-
-            // perform the above local ops first and then network op
-            if (mSipSession != null) mSipSession.endCall();
-        } catch (Throwable e) {
-            throwSipException(e);
-        }
-    }
-
-    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);
-        } catch (Throwable e) {
-            Log.e(TAG, "answerCall()", e);
-            throwSipException(e);
-        }
-    }
-
-    public synchronized void continueCall(int timeout) throws SipException {
-        if (!mHold) return;
-        try {
-            mHold = false;
-            mSipSession.changeCall(createContinueSessionDescription(), timeout);
-        } catch (Throwable e) {
-            throwSipException(e);
-        }
-
-        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 String createHoldSessionDescription() {
-        try {
-            return createSdpBuilder(false, mCodec)
-                    .addMediaAttribute(AUDIO, "sendonly", (String) null)
-                    .build();
-        } catch (SdpException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    private String createContinueSessionDescription() {
-        return createSdpBuilder(true, mCodec).build();
-    }
-
-    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);
-            }
-            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));
-            }
-            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;
-    }
-
-    public synchronized void toggleMute() {
-        AudioGroup audioGroup = getAudioGroup();
-        if (audioGroup != null) {
-            audioGroup.setMode(
-                    mMuted ? AudioGroup.MODE_NORMAL : AudioGroup.MODE_MUTED);
-            mMuted = !mMuted;
-        }
-    }
-
-    public synchronized boolean isMuted() {
-        return mMuted;
-    }
-
-    public synchronized void setSpeakerMode(boolean speakerMode) {
-        ((AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE))
-                .setSpeakerphoneOn(speakerMode);
-    }
-
-    public void sendDtmf(int code) {
-        sendDtmf(code, null);
-    }
-
-    public synchronized void sendDtmf(int code, Message result) {
-        AudioGroup audioGroup = getAudioGroup();
-        if ((audioGroup != null) && (mSipSession != null)
-                && (SipSessionState.IN_CALL == getState())) {
-            Log.v(TAG, "send DTMF: " + code);
-            audioGroup.sendDtmf(code);
-        }
-        if (result != null) result.sendToTarget();
-    }
-
-    public synchronized AudioStream getAudioStream() {
-        return mAudioStream;
-    }
-
-    public synchronized AudioGroup getAudioGroup() {
-        if (mAudioGroup != null) return mAudioGroup;
-        return ((mAudioStream == null) ? null : mAudioStream.getAudioGroup());
-    }
-
-    public synchronized void setAudioGroup(AudioGroup group) {
-        if ((mAudioStream != null) && (mAudioStream.getAudioGroup() != 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();
-        } catch (UnknownHostException e) {
-            onError(mSipSession, SipErrorCode.PEER_NOT_REACHABLE,
-                    e.getMessage());
-        } catch (Throwable e) {
-            onError(mSipSession, SipErrorCode.CLIENT_ERROR,
-                    e.getMessage());
-        }
-    }
-
-    private synchronized void startAudioInternal() throws UnknownHostException {
-        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
-
-        // TODO: get sample rate from sdp
-        mCodec = getCodec(peerSd);
-
-        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);
-
-        audioStream.setMode(RtpStream.MODE_NORMAL);
-        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.
-             */
-            ((AudioManager) mContext.getSystemService
-                    (Context.AUDIO_SERVICE))
-                    .setMode(AudioManager.MODE_NORMAL);
-        }
-
-        // AudioGroup logic:
-        AudioGroup audioGroup = getAudioGroup();
-        if (mHold) {
-            if (audioGroup != null) {
-                audioGroup.setMode(AudioGroup.MODE_ON_HOLD);
-            }
-            // don't create an AudioGroup here; doing so will fail if
-            // there's another AudioGroup out there that's active
-        } else {
-            if (audioGroup == null) audioGroup = new AudioGroup();
-            audioStream.join(audioGroup);
-            if (mMuted) {
-                audioGroup.setMode(AudioGroup.MODE_MUTED);
-            } else {
-                audioGroup.setMode(AudioGroup.MODE_NORMAL);
-            }
-        }
-    }
-
-    private void stopCall(boolean releaseSocket) {
-        Log.d(TAG, "stop audiocall");
-        if (mAudioStream != null) {
-            mAudioStream.join(null);
-
-            if (releaseSocket) {
-                mAudioStream.release();
-                mAudioStream = null;
-            }
-        }
-    }
-
-    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";
-        }
-    }
-
-    public synchronized void setRingbackToneEnabled(boolean enabled) {
-        mRingbackToneEnabled = enabled;
-    }
-
-    public synchronized void setRingtoneEnabled(boolean enabled) {
-        mRingtoneEnabled = enabled;
-    }
-
-    private void startRingbackTone() {
-        if (!mRingbackToneEnabled) return;
-        if (mRingbackTone == null) {
-            // The volume relative to other sounds in the stream
-            int toneVolume = 80;
-            mRingbackTone = new ToneGenerator(
-                    AudioManager.STREAM_VOICE_CALL, toneVolume);
-        }
-        mRingbackTone.startTone(ToneGenerator.TONE_CDMA_LOW_PBX_L);
-    }
-
-    private void stopRingbackTone() {
-        if (mRingbackTone != null) {
-            mRingbackTone.stopTone();
-            mRingbackTone.release();
-            mRingbackTone = null;
-        }
-    }
-
-    private void startRinging() {
-        if (!mRingtoneEnabled) return;
-        ((Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE))
-                .vibrate(new long[] {0, 1000, 1000}, 1);
-        AudioManager am = (AudioManager)
-                mContext.getSystemService(Context.AUDIO_SERVICE);
-        if (am.getStreamVolume(AudioManager.STREAM_RING) > 0) {
-            String ringtoneUri =
-                    Settings.System.DEFAULT_RINGTONE_URI.toString();
-            mRingtone = RingtoneManager.getRingtone(mContext,
-                    Uri.parse(ringtoneUri));
-            mRingtone.play();
-        }
-    }
-
-    private void stopRinging() {
-        ((Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE))
-                .cancel();
-        if (mRingtone != null) mRingtone.stop();
-    }
-
-    private void throwSipException(Throwable throwable) throws SipException {
-        if (throwable instanceof SipException) {
-            throw (SipException) throwable;
-        } else {
-            throw new SipException("", throwable);
-        }
-    }
-
-    private SipProfile getPeerProfile(ISipSession session) {
-        try {
-            return session.getPeerProfile();
-        } catch (RemoteException e) {
-            return null;
-        }
-    }
-}
diff --git a/voip/java/android/net/sip/SipManager.java b/voip/java/android/net/sip/SipManager.java
index 31768d7..5976a04 100644
--- a/voip/java/android/net/sip/SipManager.java
+++ b/voip/java/android/net/sip/SipManager.java
@@ -30,8 +30,9 @@
  * The class provides API for various SIP related tasks. Specifically, the API
  * allows an application to:
  * <ul>
- * <li>register a {@link SipProfile} to have the background SIP service listen
- *      to incoming calls and broadcast them with registered command string. See
+ * <li>open a {@link SipProfile} to get ready for making outbound calls or have
+ *      the background SIP service listen to incoming calls and broadcast them
+ *      with registered command string. See
  *      {@link #open(SipProfile, String, SipRegistrationListener)},
  *      {@link #open(SipProfile)}, {@link #close}, {@link #isOpened} and
  *      {@link #isRegistered}. It also facilitates handling of the incoming call
@@ -40,39 +41,59 @@
  *      {@link #getOfferSessionDescription} and {@link #takeAudioCall}.</li>
  * <li>make/take SIP-based audio calls. See
  *      {@link #makeAudioCall} and {@link #takeAudioCall}.</li>
- * <li>register/unregister with a SIP service provider. See
+ * <li>register/unregister with a SIP service provider manually. See
  *      {@link #register} and {@link #unregister}.</li>
- * <li>process SIP events directly with a {@link ISipSession} created by
+ * <li>process SIP events directly with a {@link SipSession} created by
  *      {@link #createSipSession}.</li>
  * </ul>
  * @hide
  */
 public class SipManager {
-    /** @hide */
-    public static final String SIP_INCOMING_CALL_ACTION =
+    /**
+     * Action string for the incoming call intent for the Phone app.
+     * Internal use only.
+     * @hide
+     */
+    public static final String ACTION_SIP_INCOMING_CALL =
             "com.android.phone.SIP_INCOMING_CALL";
-    /** @hide */
-    public static final String SIP_ADD_PHONE_ACTION =
+    /**
+     * Action string for the add-phone intent.
+     * Internal use only.
+     * @hide
+     */
+    public static final String ACTION_SIP_ADD_PHONE =
             "com.android.phone.SIP_ADD_PHONE";
-    /** @hide */
-    public static final String SIP_REMOVE_PHONE_ACTION =
+    /**
+     * Action string for the remove-phone intent.
+     * Internal use only.
+     * @hide
+     */
+    public static final String ACTION_SIP_REMOVE_PHONE =
             "com.android.phone.SIP_REMOVE_PHONE";
-    /** @hide */
-    public static final String LOCAL_URI_KEY = "LOCAL SIPURI";
+    /**
+     * Part of the ACTION_SIP_ADD_PHONE and ACTION_SIP_REMOVE_PHONE intents.
+     * Internal use only.
+     * @hide
+     */
+    public static final String EXTRA_LOCAL_URI = "android:localSipUri";
 
-    private static final String CALL_ID_KEY = "CallID";
-    private static final String OFFER_SD_KEY = "OfferSD";
+    /** Part of the incoming call intent. */
+    public static final String EXTRA_CALL_ID = "android:sipCallID";
+
+    /** Part of the incoming call intent. */
+    public static final String EXTRA_OFFER_SD = "android:sipOfferSD";
 
     private ISipService mSipService;
+    private Context mContext;
 
     /**
-     * Gets a manager instance. Returns null if SIP API is not supported.
+     * Creates a manager instance. Returns null if SIP API is not supported.
      *
-     * @param context application context for checking if SIP API is supported
+     * @param context application context for creating the manager object
      * @return the manager instance or null if SIP API is not supported
      */
-    public static SipManager getInstance(Context context) {
-        return (isApiSupported(context) ? new SipManager() : null);
+    public static SipManager newInstance(Context context) {
+        return (isApiSupported(context) ? new SipManager(context) : null);
     }
 
     /**
@@ -80,7 +101,7 @@
      */
     public static boolean isApiSupported(Context context) {
         return true;
-        /* 
+        /* TODO: uncomment this before ship
         return context.getPackageManager().hasSystemFeature(
                 PackageManager.FEATURE_SIP);
          */
@@ -91,7 +112,7 @@
      */
     public static boolean isVoipSupported(Context context) {
         return true;
-        /* 
+        /* TODO: uncomment this before ship
         return context.getPackageManager().hasSystemFeature(
                 PackageManager.FEATURE_SIP_VOIP) && isApiSupported(context);
          */
@@ -105,23 +126,21 @@
                 com.android.internal.R.bool.config_sip_wifi_only);
     }
 
-    private SipManager() {
+    private SipManager(Context context) {
+        mContext = context;
         createSipService();
     }
 
     private void createSipService() {
-        if (mSipService != null) return;
         IBinder b = ServiceManager.getService(Context.SIP_SERVICE);
         mSipService = ISipService.Stub.asInterface(b);
     }
 
     /**
-     * Opens the profile for making calls and/or receiving calls. Subsequent
-     * SIP calls can be made through the default phone UI. The caller may also
-     * make subsequent calls through {@link #makeAudioCall}.
-     * If the receiving-call option is enabled in the profile, the SIP service
-     * will register the profile to the corresponding server periodically in
-     * order to receive calls from the server.
+     * Opens the profile for making calls. The caller may make subsequent calls
+     * through {@link #makeAudioCall}. If one also wants to receive calls on the
+     * profile, use {@link #open(SipProfile, String, SipRegistrationListener)}
+     * instead.
      *
      * @param localProfile the SIP profile to make calls from
      * @throws SipException if the profile contains incorrect settings or
@@ -136,12 +155,11 @@
     }
 
     /**
-     * Opens the profile for making calls and/or receiving calls. Subsequent
-     * SIP calls can be made through the default phone UI. The caller may also
-     * make subsequent calls through {@link #makeAudioCall}.
-     * If the receiving-call option is enabled in the profile, the SIP service
-     * will register the profile to the corresponding server periodically in
-     * order to receive calls from the server.
+     * Opens the profile for making calls and/or receiving calls. The caller may
+     * make subsequent calls through {@link #makeAudioCall}. If the
+     * auto-registration option is enabled in the profile, the SIP service
+     * will register the profile to the corresponding SIP provider periodically
+     * in order to receive calls from the provider.
      *
      * @param localProfile the SIP profile to receive incoming calls for
      * @param incomingCallBroadcastAction the action to be broadcast when an
@@ -195,7 +213,8 @@
     }
 
     /**
-     * Checks if the specified profile is enabled to receive calls.
+     * Checks if the specified profile is opened in the SIP service for
+     * making and/or receiving calls.
      *
      * @param localProfileUri the URI of the profile in question
      * @return true if the profile is enabled to receive calls
@@ -210,11 +229,16 @@
     }
 
     /**
-     * Checks if the specified profile is registered to the server for
-     * receiving calls.
+     * Checks if the SIP service has successfully registered the profile to the
+     * SIP provider (specified in the profile) for receiving calls. Returning
+     * true from this method also implies the profile is opened
+     * ({@link #isOpened}).
      *
      * @param localProfileUri the URI of the profile in question
-     * @return true if the profile is registered to the server
+     * @return true if the profile is registered to the SIP provider; false if
+     *        the profile has not been opened in the SIP service or the SIP
+     *        service has not yet successfully registered the profile to the SIP
+     *        provider
      * @throws SipException if calling the SIP service results in an error
      */
     public boolean isRegistered(String localProfileUri) throws SipException {
@@ -231,7 +255,6 @@
      * {@code SipAudioCall.Listener.onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
      * will be called.
      *
-     * @param context context to create a {@link SipAudioCall} object
      * @param localProfile the SIP profile to make the call from
      * @param peerProfile the SIP profile to make the call to
      * @param listener to listen to the call events from {@link SipAudioCall};
@@ -241,10 +264,10 @@
      * @throws SipException if calling the SIP service results in an error
      * @see SipAudioCall.Listener.onError
      */
-    public SipAudioCall makeAudioCall(Context context, SipProfile localProfile,
+    public SipAudioCall makeAudioCall(SipProfile localProfile,
             SipProfile peerProfile, SipAudioCall.Listener listener, int timeout)
             throws SipException {
-        SipAudioCall call = new SipAudioCallImpl(context, localProfile);
+        SipAudioCall call = new SipAudioCall(mContext, localProfile);
         call.setListener(listener);
         call.makeCall(peerProfile, this, timeout);
         return call;
@@ -257,7 +280,6 @@
      * {@code SipAudioCall.Listener.onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
      * will be called.
      *
-     * @param context context to create a {@link SipAudioCall} object
      * @param localProfileUri URI of the SIP profile to make the call from
      * @param peerProfileUri URI of the SIP profile to make the call to
      * @param listener to listen to the call events from {@link SipAudioCall};
@@ -267,11 +289,11 @@
      * @throws SipException if calling the SIP service results in an error
      * @see SipAudioCall.Listener.onError
      */
-    public SipAudioCall makeAudioCall(Context context, String localProfileUri,
+    public SipAudioCall makeAudioCall(String localProfileUri,
             String peerProfileUri, SipAudioCall.Listener listener, int timeout)
             throws SipException {
         try {
-            return makeAudioCall(context,
+            return makeAudioCall(
                     new SipProfile.Builder(localProfileUri).build(),
                     new SipProfile.Builder(peerProfileUri).build(), listener,
                     timeout);
@@ -281,15 +303,14 @@
     }
 
     /**
-     * The method calls {@code takeAudioCall(context, incomingCallIntent,
+     * The method calls {@code takeAudioCall(incomingCallIntent,
      * listener, true}.
      *
-     * @see #takeAudioCall(Context, Intent, SipAudioCall.Listener, boolean)
+     * @see #takeAudioCall(Intent, SipAudioCall.Listener, boolean)
      */
-    public SipAudioCall takeAudioCall(Context context,
-            Intent incomingCallIntent, SipAudioCall.Listener listener)
-            throws SipException {
-        return takeAudioCall(context, incomingCallIntent, listener, true);
+    public SipAudioCall takeAudioCall(Intent incomingCallIntent,
+            SipAudioCall.Listener listener) throws SipException {
+        return takeAudioCall(incomingCallIntent, listener, true);
     }
 
     /**
@@ -298,16 +319,15 @@
      * {@link SipAudioCall.Listener#onRinging}
      * callback.
      *
-     * @param context context to create a {@link SipAudioCall} object
      * @param incomingCallIntent the incoming call broadcast intent
      * @param listener to listen to the call events from {@link SipAudioCall};
      *      can be null
      * @return a {@link SipAudioCall} object
      * @throws SipException if calling the SIP service results in an error
      */
-    public SipAudioCall takeAudioCall(Context context,
-            Intent incomingCallIntent, SipAudioCall.Listener listener,
-            boolean ringtoneEnabled) throws SipException {
+    public SipAudioCall takeAudioCall(Intent incomingCallIntent,
+            SipAudioCall.Listener listener, boolean ringtoneEnabled)
+            throws SipException {
         if (incomingCallIntent == null) return null;
 
         String callId = getCallId(incomingCallIntent);
@@ -324,10 +344,10 @@
         try {
             ISipSession session = mSipService.getPendingSession(callId);
             if (session == null) return null;
-            SipAudioCall call = new SipAudioCallImpl(
-                    context, session.getLocalProfile());
+            SipAudioCall call = new SipAudioCall(
+                    mContext, session.getLocalProfile());
             call.setRingtoneEnabled(ringtoneEnabled);
-            call.attachCall(session, offerSd);
+            call.attachCall(new SipSession(session), offerSd);
             call.setListener(listener);
             return call;
         } catch (Throwable t) {
@@ -355,7 +375,7 @@
      * @return the call ID or null if the intent does not contain it
      */
     public static String getCallId(Intent incomingCallIntent) {
-        return incomingCallIntent.getStringExtra(CALL_ID_KEY);
+        return incomingCallIntent.getStringExtra(EXTRA_CALL_ID);
     }
 
     /**
@@ -367,30 +387,30 @@
      *      have it
      */
     public static String getOfferSessionDescription(Intent incomingCallIntent) {
-        return incomingCallIntent.getStringExtra(OFFER_SD_KEY);
+        return incomingCallIntent.getStringExtra(EXTRA_OFFER_SD);
     }
 
     /**
      * Creates an incoming call broadcast intent.
      *
-     * @param action the action string to broadcast
      * @param callId the call ID of the incoming call
      * @param sessionDescription the session description of the incoming call
      * @return the incoming call intent
      * @hide
      */
-    public static Intent createIncomingCallBroadcast(String action,
-            String callId, String sessionDescription) {
-        Intent intent = new Intent(action);
-        intent.putExtra(CALL_ID_KEY, callId);
-        intent.putExtra(OFFER_SD_KEY, sessionDescription);
+    public static Intent createIncomingCallBroadcast(String callId,
+            String sessionDescription) {
+        Intent intent = new Intent();
+        intent.putExtra(EXTRA_CALL_ID, callId);
+        intent.putExtra(EXTRA_OFFER_SD, sessionDescription);
         return intent;
     }
 
     /**
-     * Registers the profile to the corresponding server for receiving calls.
-     * {@link #open} is still needed to be called at least once in order for
-     * the SIP service to broadcast an intent when an incoming call is received.
+     * Manually registers the profile to the corresponding SIP provider for
+     * receiving calls. {@link #open(SipProfile, String, SipRegistrationListener)}
+     * is still needed to be called at least once in order for the SIP service
+     * to broadcast an intent when an incoming call is received.
      *
      * @param localProfile the SIP profile to register with
      * @param expiryTime registration expiration time (in seconds)
@@ -409,8 +429,10 @@
     }
 
     /**
-     * Unregisters the profile from the corresponding server for not receiving
-     * further calls.
+     * Manually unregisters the profile from the corresponding SIP provider for
+     * stop receiving further calls. This may interference with the auto
+     * registration process in the SIP service if the auto-registration option
+     * in the profile is enabled.
      *
      * @param localProfile the SIP profile to register with
      * @param listener to listen to the registration events
@@ -460,10 +482,11 @@
      * @param localProfile the SIP profile the session is associated with
      * @param listener to listen to SIP session events
      */
-    public ISipSession createSipSession(SipProfile localProfile,
-            ISipSessionListener listener) throws SipException {
+    public SipSession createSipSession(SipProfile localProfile,
+            SipSession.Listener listener) throws SipException {
         try {
-            return mSipService.createSession(localProfile, listener);
+            ISipSession s = mSipService.createSession(localProfile, null);
+            return new SipSession(s, listener);
         } catch (RemoteException e) {
             throw new SipException("createSipSession()", e);
         }
diff --git a/voip/java/android/net/sip/SipProfile.java b/voip/java/android/net/sip/SipProfile.java
index 88bfba9..6d5cb3c 100644
--- a/voip/java/android/net/sip/SipProfile.java
+++ b/voip/java/android/net/sip/SipProfile.java
@@ -48,7 +48,6 @@
     private boolean mAutoRegistration = true;
     private transient int mCallingUid = 0;
 
-    /** @hide */
     public static final Parcelable.Creator<SipProfile> CREATOR =
             new Parcelable.Creator<SipProfile>() {
                 public SipProfile createFromParcel(Parcel in) {
@@ -287,7 +286,7 @@
         mCallingUid = in.readInt();
     }
 
-    /** @hide */
+    @Override
     public void writeToParcel(Parcel out, int flags) {
         out.writeSerializable(mAddress);
         out.writeString(mProxyAddress);
@@ -300,7 +299,7 @@
         out.writeInt(mCallingUid);
     }
 
-    /** @hide */
+    @Override
     public int describeContents() {
         return 0;
     }
diff --git a/voip/java/android/net/sip/SipSession.java b/voip/java/android/net/sip/SipSession.java
new file mode 100644
index 0000000..0cc7206
--- /dev/null
+++ b/voip/java/android/net/sip/SipSession.java
@@ -0,0 +1,531 @@
+/*
+ * 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 android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * A SIP session that is associated with a SIP dialog or a standalone
+ * transaction not within a dialog.
+ * @hide
+ */
+public final class SipSession {
+    private static final String TAG = "SipSession";
+
+    /**
+     * Defines {@link SipSession} states.
+     * @hide
+     */
+    public static class State {
+        /** When session is ready to initiate a call or transaction. */
+        public static final int READY_TO_CALL = 0;
+
+        /** When the registration request is sent out. */
+        public static final int REGISTERING = 1;
+
+        /** When the unregistration request is sent out. */
+        public static final int DEREGISTERING = 2;
+
+        /** When an INVITE request is received. */
+        public static final int INCOMING_CALL = 3;
+
+        /** When an OK response is sent for the INVITE request received. */
+        public static final int INCOMING_CALL_ANSWERING = 4;
+
+        /** When an INVITE request is sent. */
+        public static final int OUTGOING_CALL = 5;
+
+        /** When a RINGING response is received for the INVITE request sent. */
+        public static final int OUTGOING_CALL_RING_BACK = 6;
+
+        /** When a CANCEL request is sent for the INVITE request sent. */
+        public static final int OUTGOING_CALL_CANCELING = 7;
+
+        /** When a call is established. */
+        public static final int IN_CALL = 8;
+
+        /** When an OPTIONS request is sent. */
+        public static final int PINGING = 9;
+
+        /** Not defined. */
+        public static final int NOT_DEFINED = 101;
+
+        /**
+         * Converts the state to string.
+         */
+        public static String toString(int state) {
+            switch (state) {
+                case READY_TO_CALL:
+                    return "READY_TO_CALL";
+                case REGISTERING:
+                    return "REGISTERING";
+                case DEREGISTERING:
+                    return "DEREGISTERING";
+                case INCOMING_CALL:
+                    return "INCOMING_CALL";
+                case INCOMING_CALL_ANSWERING:
+                    return "INCOMING_CALL_ANSWERING";
+                case OUTGOING_CALL:
+                    return "OUTGOING_CALL";
+                case OUTGOING_CALL_RING_BACK:
+                    return "OUTGOING_CALL_RING_BACK";
+                case OUTGOING_CALL_CANCELING:
+                    return "OUTGOING_CALL_CANCELING";
+                case IN_CALL:
+                    return "IN_CALL";
+                case PINGING:
+                    return "PINGING";
+                default:
+                    return "NOT_DEFINED";
+            }
+        }
+
+        private State() {
+        }
+    }
+
+    /**
+     * Listener class that listens to {@link SipSession} events.
+     * @hide
+     */
+    public static class Listener {
+        /**
+         * Called when an INVITE request is sent to initiate a new call.
+         *
+         * @param session the session object that carries out the transaction
+         */
+        public void onCalling(SipSession session) {
+        }
+
+        /**
+         * Called when an INVITE request is received.
+         *
+         * @param session the session object that carries out the transaction
+         * @param caller the SIP profile of the caller
+         * @param sessionDescription the caller's session description
+         */
+        public void onRinging(SipSession session, SipProfile caller,
+                String sessionDescription) {
+        }
+
+        /**
+         * Called when a RINGING response is received for the INVITE request sent
+         *
+         * @param session the session object that carries out the transaction
+         */
+        public void onRingingBack(SipSession session) {
+        }
+
+        /**
+         * Called when the session is established.
+         *
+         * @param session the session object that is associated with the dialog
+         * @param sessionDescription the peer's session description
+         */
+        public void onCallEstablished(SipSession session,
+                String sessionDescription) {
+        }
+
+        /**
+         * Called when the session is terminated.
+         *
+         * @param session the session object that is associated with the dialog
+         */
+        public void onCallEnded(SipSession session) {
+        }
+
+        /**
+         * Called when the peer is busy during session initialization.
+         *
+         * @param session the session object that carries out the transaction
+         */
+        public void onCallBusy(SipSession session) {
+        }
+
+        /**
+         * Called when an error occurs during session initialization and
+         * termination.
+         *
+         * @param session the session object that carries out the transaction
+         * @param errorCode error code defined in {@link SipErrorCode}
+         * @param errorMessage error message
+         */
+        public void onError(SipSession session, int errorCode,
+                String errorMessage) {
+        }
+
+        /**
+         * Called when an error occurs during session modification negotiation.
+         *
+         * @param session the session object that carries out the transaction
+         * @param errorCode error code defined in {@link SipErrorCode}
+         * @param errorMessage error message
+         */
+        public void onCallChangeFailed(SipSession session, int errorCode,
+                String errorMessage) {
+        }
+
+        /**
+         * Called when a registration request is sent.
+         *
+         * @param session the session object that carries out the transaction
+         */
+        public void onRegistering(SipSession session) {
+        }
+
+        /**
+         * Called when registration is successfully done.
+         *
+         * @param session the session object that carries out the transaction
+         * @param duration duration in second before the registration expires
+         */
+        public void onRegistrationDone(SipSession session, int duration) {
+        }
+
+        /**
+         * Called when the registration fails.
+         *
+         * @param session the session object that carries out the transaction
+         * @param errorCode error code defined in {@link SipErrorCode}
+         * @param errorMessage error message
+         */
+        public void onRegistrationFailed(SipSession session, int errorCode,
+                String errorMessage) {
+        }
+
+        /**
+         * Called when the registration gets timed out.
+         *
+         * @param session the session object that carries out the transaction
+         */
+        public void onRegistrationTimeout(SipSession session) {
+        }
+    }
+
+    private final ISipSession mSession;
+    private Listener mListener;
+
+    SipSession(ISipSession realSession) {
+        mSession = realSession;
+        if (realSession != null) {
+            try {
+                realSession.setListener(createListener());
+            } catch (RemoteException e) {
+                Log.e(TAG, "SipSession.setListener(): " + e);
+            }
+        }
+    }
+
+    SipSession(ISipSession realSession, Listener listener) {
+        this(realSession);
+        setListener(listener);
+    }
+
+    /**
+     * Gets the IP address of the local host on which this SIP session runs.
+     *
+     * @return the IP address of the local host
+     */
+    public String getLocalIp() {
+        try {
+            return mSession.getLocalIp();
+        } catch (RemoteException e) {
+            Log.e(TAG, "getLocalIp(): " + e);
+            return "127.0.0.1";
+        }
+    }
+
+    /**
+     * Gets the SIP profile that this session is associated with.
+     *
+     * @return the SIP profile that this session is associated with
+     */
+    public SipProfile getLocalProfile() {
+        try {
+            return mSession.getLocalProfile();
+        } catch (RemoteException e) {
+            Log.e(TAG, "getLocalProfile(): " + e);
+            return null;
+        }
+    }
+
+    /**
+     * Gets the SIP profile that this session is connected to. Only available
+     * when the session is associated with a SIP dialog.
+     *
+     * @return the SIP profile that this session is connected to
+     */
+    public SipProfile getPeerProfile() {
+        try {
+            return mSession.getPeerProfile();
+        } catch (RemoteException e) {
+            Log.e(TAG, "getPeerProfile(): " + e);
+            return null;
+        }
+    }
+
+    /**
+     * Gets the session state. The value returned must be one of the states in
+     * {@link SipSessionState}.
+     *
+     * @return the session state
+     */
+    public int getState() {
+        try {
+            return mSession.getState();
+        } catch (RemoteException e) {
+            Log.e(TAG, "getState(): " + e);
+            return State.NOT_DEFINED;
+        }
+    }
+
+    /**
+     * Checks if the session is in a call.
+     *
+     * @return true if the session is in a call
+     */
+    public boolean isInCall() {
+        try {
+            return mSession.isInCall();
+        } catch (RemoteException e) {
+            Log.e(TAG, "isInCall(): " + e);
+            return false;
+        }
+    }
+
+    /**
+     * Gets the call ID of the session.
+     *
+     * @return the call ID
+     */
+    public String getCallId() {
+        try {
+            return mSession.getCallId();
+        } catch (RemoteException e) {
+            Log.e(TAG, "getCallId(): " + e);
+            return null;
+        }
+    }
+
+
+    /**
+     * Sets the listener to listen to the session events. A {@code SipSession}
+     * can only hold one listener at a time. Subsequent calls to this method
+     * override the previous listener.
+     *
+     * @param listener to listen to the session events of this object
+     */
+    public void setListener(Listener listener) {
+        mListener = listener;
+    }
+
+
+    /**
+     * Performs registration to the server specified by the associated local
+     * profile. The session listener is called back upon success or failure of
+     * registration. The method is only valid to call when the session state is
+     * in {@link SipSessionState#READY_TO_CALL}.
+     *
+     * @param duration duration in second before the registration expires
+     * @see Listener
+     */
+    public void register(int duration) {
+        try {
+            mSession.register(duration);
+        } catch (RemoteException e) {
+            Log.e(TAG, "register(): " + e);
+        }
+    }
+
+    /**
+     * Performs unregistration to the server specified by the associated local
+     * profile. Unregistration is technically the same as registration with zero
+     * expiration duration. The session listener is called back upon success or
+     * failure of unregistration. The method is only valid to call when the
+     * session state is in {@link SipSessionState#READY_TO_CALL}.
+     *
+     * @see Listener
+     */
+    public void unregister() {
+        try {
+            mSession.unregister();
+        } catch (RemoteException e) {
+            Log.e(TAG, "unregister(): " + e);
+        }
+    }
+
+    /**
+     * Initiates a call to the specified profile. The session listener is called
+     * back upon defined session events. The method is only valid to call when
+     * the session state is in {@link SipSessionState#READY_TO_CALL}.
+     *
+     * @param callee the SIP profile to make the call to
+     * @param sessionDescription the session description of this call
+     * @param timeout the session will be timed out if the call is not
+     *        established within {@code timeout} seconds. Default value (defined
+     *        by SIP protocol) is used if {@code timeout} is zero or negative.
+     * @see Listener
+     */
+    public void makeCall(SipProfile callee, String sessionDescription,
+            int timeout) {
+        try {
+            mSession.makeCall(callee, sessionDescription, timeout);
+        } catch (RemoteException e) {
+            Log.e(TAG, "makeCall(): " + e);
+        }
+    }
+
+    /**
+     * Answers an incoming call with the specified session description. The
+     * method is only valid to call when the session state is in
+     * {@link SipSessionState#INCOMING_CALL}.
+     *
+     * @param sessionDescription the session description to answer this call
+     * @param timeout the session will be timed out if the call is not
+     *        established within {@code timeout} seconds. Default value (defined
+     *        by SIP protocol) is used if {@code timeout} is zero or negative.
+     */
+    public void answerCall(String sessionDescription, int timeout) {
+        try {
+            mSession.answerCall(sessionDescription, timeout);
+        } catch (RemoteException e) {
+            Log.e(TAG, "answerCall(): " + e);
+        }
+    }
+
+    /**
+     * Ends an established call, terminates an outgoing call or rejects an
+     * incoming call. The method is only valid to call when the session state is
+     * in {@link SipSessionState#IN_CALL},
+     * {@link SipSessionState#INCOMING_CALL},
+     * {@link SipSessionState#OUTGOING_CALL} or
+     * {@link SipSessionState#OUTGOING_CALL_RING_BACK}.
+     */
+    public void endCall() {
+        try {
+            mSession.endCall();
+        } catch (RemoteException e) {
+            Log.e(TAG, "endCall(): " + e);
+        }
+    }
+
+    /**
+     * Changes the session description during a call. The method is only valid
+     * to call when the session state is in {@link SipSessionState#IN_CALL}.
+     *
+     * @param sessionDescription the new session description
+     * @param timeout the session will be timed out if the call is not
+     *        established within {@code timeout} seconds. Default value (defined
+     *        by SIP protocol) is used if {@code timeout} is zero or negative.
+     */
+    public void changeCall(String sessionDescription, int timeout) {
+        try {
+            mSession.changeCall(sessionDescription, timeout);
+        } catch (RemoteException e) {
+            Log.e(TAG, "changeCall(): " + e);
+        }
+    }
+
+    ISipSession getRealSession() {
+        return mSession;
+    }
+
+    private ISipSessionListener createListener() {
+        return new ISipSessionListener.Stub() {
+            public void onCalling(ISipSession session) {
+                if (mListener != null) {
+                    mListener.onCalling(SipSession.this);
+                }
+            }
+
+            public void onRinging(ISipSession session, SipProfile caller,
+                    String sessionDescription) {
+                if (mListener != null) {
+                    mListener.onRinging(SipSession.this, caller,
+                            sessionDescription);
+                }
+            }
+
+            public void onRingingBack(ISipSession session) {
+                if (mListener != null) {
+                    mListener.onRingingBack(SipSession.this);
+                }
+            }
+
+            public void onCallEstablished(ISipSession session,
+                    String sessionDescription) {
+                if (mListener != null) {
+                    mListener.onCallEstablished(SipSession.this,
+                            sessionDescription);
+                }
+            }
+
+            public void onCallEnded(ISipSession session) {
+                if (mListener != null) {
+                    mListener.onCallEnded(SipSession.this);
+                }
+            }
+
+            public void onCallBusy(ISipSession session) {
+                if (mListener != null) {
+                    mListener.onCallBusy(SipSession.this);
+                }
+            }
+
+            public void onCallChangeFailed(ISipSession session, int errorCode,
+                    String message) {
+                if (mListener != null) {
+                    mListener.onCallChangeFailed(SipSession.this, errorCode,
+                            message);
+                }
+            }
+
+            public void onError(ISipSession session, int errorCode, String message) {
+                if (mListener != null) {
+                    mListener.onError(SipSession.this, errorCode, message);
+                }
+            }
+
+            public void onRegistering(ISipSession session) {
+                if (mListener != null) {
+                    mListener.onRegistering(SipSession.this);
+                }
+            }
+
+            public void onRegistrationDone(ISipSession session, int duration) {
+                if (mListener != null) {
+                    mListener.onRegistrationDone(SipSession.this, duration);
+                }
+            }
+
+            public void onRegistrationFailed(ISipSession session, int errorCode,
+                    String message) {
+                if (mListener != null) {
+                    mListener.onRegistrationFailed(SipSession.this, errorCode,
+                            message);
+                }
+            }
+
+            public void onRegistrationTimeout(ISipSession session) {
+                if (mListener != null) {
+                    mListener.onRegistrationTimeout(SipSession.this);
+                }
+            }
+        };
+    }
+}
diff --git a/voip/java/android/net/sip/SipSessionState.java b/voip/java/android/net/sip/SipSessionState.java
deleted file mode 100644
index 31e9d3f..0000000
--- a/voip/java/android/net/sip/SipSessionState.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.sip;
-
-/**
- * Defines {@link ISipSession} states.
- * @hide
- */
-public class SipSessionState {
-    /** When session is ready to initiate a call or transaction. */
-    public static final int READY_TO_CALL = 0;
-
-    /** When the registration request is sent out. */
-    public static final int REGISTERING = 1;
-
-    /** When the unregistration request is sent out. */
-    public static final int DEREGISTERING = 2;
-
-    /** When an INVITE request is received. */
-    public static final int INCOMING_CALL = 3;
-
-    /** When an OK response is sent for the INVITE request received. */
-    public static final int INCOMING_CALL_ANSWERING = 4;
-
-    /** When an INVITE request is sent. */
-    public static final int OUTGOING_CALL = 5;
-
-    /** When a RINGING response is received for the INVITE request sent. */
-    public static final int OUTGOING_CALL_RING_BACK = 6;
-
-    /** When a CANCEL request is sent for the INVITE request sent. */
-    public static final int OUTGOING_CALL_CANCELING = 7;
-
-    /** When a call is established. */
-    public static final int IN_CALL = 8;
-
-    /** Some error occurs when making a remote call to {@link ISipSession}. */
-    public static final int REMOTE_ERROR = 9;
-
-    /** When an OPTIONS request is sent. */
-    public static final int PINGING = 10;
-
-    /** Not defined. */
-    public static final int NOT_DEFINED = 101;
-
-    /**
-     * Converts the state to string.
-     */
-    public static String toString(int state) {
-        switch (state) {
-            case READY_TO_CALL:
-                return "READY_TO_CALL";
-            case REGISTERING:
-                return "REGISTERING";
-            case DEREGISTERING:
-                return "DEREGISTERING";
-            case INCOMING_CALL:
-                return "INCOMING_CALL";
-            case INCOMING_CALL_ANSWERING:
-                return "INCOMING_CALL_ANSWERING";
-            case OUTGOING_CALL:
-                return "OUTGOING_CALL";
-            case OUTGOING_CALL_RING_BACK:
-                return "OUTGOING_CALL_RING_BACK";
-            case OUTGOING_CALL_CANCELING:
-                return "OUTGOING_CALL_CANCELING";
-            case IN_CALL:
-                return "IN_CALL";
-            case REMOTE_ERROR:
-                return "REMOTE_ERROR";
-            case PINGING:
-                return "PINGING";
-            default:
-                return "NOT_DEFINED";
-        }
-    }
-
-    private SipSessionState() {
-    }
-}
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},
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index e73bca0..f760d27 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -105,5 +105,9 @@
     void saveNetwork(in WifiConfiguration wifiConfig);
 
     void forgetNetwork(int networkId);
+
+    void startWpsPbc(String bssid);
+
+    void startWpsPin(String bssid, int apPin);
 }
 
diff --git a/wifi/java/android/net/wifi/WifiConfigStore.java b/wifi/java/android/net/wifi/WifiConfigStore.java
index dfa9f75..7ea4872 100644
--- a/wifi/java/android/net/wifi/WifiConfigStore.java
+++ b/wifi/java/android/net/wifi/WifiConfigStore.java
@@ -23,6 +23,7 @@
 import android.net.wifi.WifiConfiguration.IpAssignment;
 import android.net.wifi.WifiConfiguration.KeyMgmt;
 import android.net.wifi.WifiConfiguration.Status;
+import static android.net.wifi.WifiConfiguration.INVALID_NETWORK_ID;
 import android.os.Environment;
 import android.text.TextUtils;
 import android.util.Log;
@@ -95,9 +96,9 @@
      * and enable all stored networks in supplicant.
      */
     static void initialize(Context context) {
-        Log.d(TAG, "Updating config and enabling all networks");
+        Log.d(TAG, "Loading config and enabling all networks");
         sContext = context;
-        updateConfiguredNetworks();
+        loadConfiguredNetworks();
         enableAllNetworks();
     }
 
@@ -150,7 +151,7 @@
     static void selectNetwork(WifiConfiguration config) {
         if (config != null) {
             int netId = addOrUpdateNetworkNative(config);
-            if (netId != -1) {
+            if (netId != INVALID_NETWORK_ID) {
                 selectNetwork(netId);
             } else {
                 Log.e(TAG, "Failed to update network " + config);
@@ -174,7 +175,7 @@
         if (sLastPriority == -1 || sLastPriority > 1000000) {
             synchronized (sConfiguredNetworks) {
                 for(WifiConfiguration config : sConfiguredNetworks.values()) {
-                    if (config.networkId != -1) {
+                    if (config.networkId != INVALID_NETWORK_ID) {
                         config.priority = 0;
                         addOrUpdateNetworkNative(config);
                     }
@@ -204,10 +205,10 @@
      * @param config WifiConfiguration to be saved
      */
     static void saveNetwork(WifiConfiguration config) {
-        boolean newNetwork = (config.networkId == -1);
+        boolean newNetwork = (config.networkId == INVALID_NETWORK_ID);
         int netId = addOrUpdateNetworkNative(config);
         /* enable a new network */
-        if (newNetwork && netId >= 0) {
+        if (newNetwork && netId != INVALID_NETWORK_ID) {
             WifiNative.enableNetworkCommand(netId, false);
             synchronized (sConfiguredNetworks) {
                 sConfiguredNetworks.get(netId).status = Status.ENABLED;
@@ -288,13 +289,7 @@
         }
 
         if (disableOthers) {
-            synchronized (sConfiguredNetworks) {
-                for(WifiConfiguration config : sConfiguredNetworks.values()) {
-                    if(config != null && config.networkId != netId) {
-                        config.status = Status.DISABLED;
-                    }
-                }
-            }
+            markAllNetworksDisabledExcept(netId);
         }
         return ret;
     }
@@ -321,6 +316,32 @@
     }
 
     /**
+     * Start WPS pin method configuration
+     */
+    static boolean startWpsPin(String bssid, int apPin) {
+        if (WifiNative.startWpsPinCommand(bssid, apPin)) {
+            /* WPS leaves all networks disabled */
+            markAllNetworksDisabled();
+            return true;
+        }
+        Log.e(TAG, "Failed to start WPS pin method configuration");
+        return false;
+    }
+
+    /**
+     * Start WPS push button configuration
+     */
+    static boolean startWpsPbc(String bssid) {
+        if (WifiNative.startWpsPbcCommand(bssid)) {
+            /* WPS leaves all networks disabled */
+            markAllNetworksDisabled();
+            return true;
+        }
+        Log.e(TAG, "Failed to start WPS push button configuration");
+        return false;
+    }
+
+    /**
      * Fetch the IP configuration for a given network id
      */
     static DhcpInfo getIpConfiguration(int netId) {
@@ -350,7 +371,7 @@
         sContext.sendBroadcast(intent);
     }
 
-    private static void updateConfiguredNetworks() {
+    static void loadConfiguredNetworks() {
         String listStr = WifiNative.listNetworksCommand();
         sLastPriority = 0;
 
@@ -391,6 +412,22 @@
             }
         }
         readIpConfigurations();
+        sendConfigChangeBroadcast();
+    }
+
+    /* Mark all networks except specified netId as disabled */
+    private static void markAllNetworksDisabledExcept(int netId) {
+        synchronized (sConfiguredNetworks) {
+            for(WifiConfiguration config : sConfiguredNetworks.values()) {
+                if(config != null && config.networkId != netId) {
+                    config.status = Status.DISABLED;
+                }
+            }
+        }
+    }
+
+    private static void markAllNetworksDisabled() {
+        markAllNetworksDisabledExcept(INVALID_NETWORK_ID);
     }
 
     private static void writeIpConfigurations() {
@@ -513,20 +550,20 @@
 
     private static int addOrUpdateNetworkNative(WifiConfiguration config) {
         /*
-         * If the supplied networkId is -1, we create a new empty
+         * If the supplied networkId is INVALID_NETWORK_ID, we create a new empty
          * network configuration. Otherwise, the networkId should
          * refer to an existing configuration.
          */
         int netId = config.networkId;
         boolean updateFailed = true;
-        boolean newNetwork = netId == -1;
-        // networkId of -1 means we want to create a new network
+        boolean newNetwork = (netId == INVALID_NETWORK_ID);
+        // networkId of INVALID_NETWORK_ID means we want to create a new network
 
         if (newNetwork) {
             netId = WifiNative.addNetworkCommand();
             if (netId < 0) {
                 Log.e(TAG, "Failed to add a network!");
-                return -1;
+                return INVALID_NETWORK_ID;
           }
         }
 
@@ -700,7 +737,7 @@
                         "Failed to set a network variable, removed network: "
                         + netId);
             }
-            return -1;
+            return INVALID_NETWORK_ID;
         }
 
         /* An update of the network variables requires reading them
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 8971bdd..57e9bad 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -43,6 +43,8 @@
     public static final String priorityVarName = "priority";
     /** {@hide} */
     public static final String hiddenSSIDVarName = "scan_ssid";
+    /** {@hide} */
+    public static final int INVALID_NETWORK_ID = -1;
 
     /** {@hide} */
     public class EnterpriseField {
@@ -313,7 +315,7 @@
     public DhcpInfo ipConfig;
 
     public WifiConfiguration() {
-        networkId = -1;
+        networkId = INVALID_NETWORK_ID;
         SSID = null;
         BSSID = null;
         priority = 0;
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 4435110..0b3a782 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1037,6 +1037,32 @@
     }
 
     /**
+     * Start Wi-fi protected setup push button Configuration
+     *
+     * @param bssid BSSID of the access point
+     * @hide
+     */
+    public void startWpsPbc(String bssid) {
+        try {
+            mService.startWpsPbc(bssid);
+        } catch (RemoteException e) { }
+    }
+
+    /**
+     * Start Wi-fi Protected Setup pin method configuration
+     *
+     * @param bssid BSSID of the access point
+     * @param apPin PIN issued by the access point
+     *
+     * @hide
+     */
+    public void startWpsPin(String bssid, int apPin) {
+        try {
+            mService.startWpsPin(bssid, apPin);
+        } catch (RemoteException e) { }
+    }
+
+    /**
      * Allows an application to keep the Wi-Fi radio awake.
      * Normally the Wi-Fi radio may turn off when the user has not used the device in a while.
      * Acquiring a WifiLock will keep the radio on until the lock is released.  Multiple 
diff --git a/wifi/java/android/net/wifi/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java
index 862a61b..1251a25 100644
--- a/wifi/java/android/net/wifi/WifiNative.java
+++ b/wifi/java/android/net/wifi/WifiNative.java
@@ -151,6 +151,10 @@
 
     public native static boolean clearBlacklistCommand();
 
+    public native static boolean startWpsPbcCommand(String bssid);
+
+    public native static boolean startWpsPinCommand(String bssid, int apPin);
+
     public native static boolean doDhcpRequest(DhcpInfo results);
 
     public native static String getDhcpError();
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index 6fe7529..7e26028 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -151,7 +151,9 @@
     /* Connection to a specific network involves disabling all networks,
      * this flag tracks if networks need to be re-enabled */
     private boolean mEnableAllNetworks = false;
-
+    /* Track if WPS was started since we need to re-enable networks
+     * and load configuration afterwards */
+    private boolean mWpsStarted = false;
 
     // Event log tags (must be in sync with event-log-tags)
     private static final int EVENTLOG_WIFI_STATE_CHANGED        = 50021;
@@ -299,7 +301,8 @@
      * supplicant config.
      */
     private static final int CMD_FORGET_NETWORK                   = 92;
-
+    /* Start Wi-Fi protected setup */
+    private static final int CMD_START_WPS                        = 93;
 
 
     /**
@@ -406,7 +409,7 @@
      * Keep track of whether WIFI is running.
      */
     private boolean mIsRunning = false;
-    
+
     /**
      * Keep track of whether we last told the battery stats we had started.
      */
@@ -765,6 +768,14 @@
         sendMessage(obtainMessage(CMD_FORGET_NETWORK, netId, 0));
     }
 
+    public void startWpsPbc(String bssid) {
+        sendMessage(obtainMessage(CMD_START_WPS, bssid));
+    }
+
+    public void startWpsPin(String bssid, int apPin) {
+        sendMessage(obtainMessage(CMD_START_WPS, apPin, 0, bssid));
+    }
+
     public void enableRssiPolling(boolean enabled) {
        sendMessage(obtainMessage(CMD_ENABLE_RSSI_POLL, enabled ? 1 : 0, 0));
     }
@@ -1597,6 +1608,7 @@
                 case CMD_CONNECT_NETWORK:
                 case CMD_SAVE_NETWORK:
                 case CMD_FORGET_NETWORK:
+                case CMD_START_WPS:
                     break;
                 default:
                     Log.e(TAG, "Error! unhandled message" + message);
@@ -2355,6 +2367,33 @@
                     /* Expect a disconnection from the old connection */
                     transitionTo(mDisconnectingState);
                     break;
+                case CMD_START_WPS:
+                    String bssid = (String) message.obj;
+                    int apPin = message.arg1;
+                    boolean success;
+                    if (apPin != 0) {
+                        /* WPS pin method configuration */
+                        success = WifiConfigStore.startWpsPin(bssid, apPin);
+                    } else {
+                        /* WPS push button configuration */
+                        success = WifiConfigStore.startWpsPbc(bssid);
+                    }
+                    /* During WPS setup, all other networks are disabled. After
+                     * a successful connect a new config is created in the supplicant.
+                     *
+                     * We need to enable all networks after a successful connection
+                     * or when supplicant goes inactive due to failure. Enabling all
+                     * networks after a disconnect is observed as done with connectNetwork
+                     * does not lead to a successful WPS setup.
+                     *
+                     * Upon success, the configuration list needs to be reloaded
+                     */
+                    if (success) {
+                        mWpsStarted = true;
+                        /* Expect a disconnection from the old connection */
+                        transitionTo(mDisconnectingState);
+                    }
+                    break;
                 case SCAN_RESULTS_EVENT:
                     /* Set the scan setting back to "connect" mode */
                     WifiNative.setScanResultHandlingCommand(CONNECT_MODE);
@@ -2581,6 +2620,12 @@
         @Override
         public void enter() {
             if (DBG) Log.d(TAG, getName() + "\n");
+            /* A successful WPS connection */
+            if (mWpsStarted) {
+                WifiConfigStore.enableAllNetworks();
+                WifiConfigStore.loadConfiguredNetworks();
+                mWpsStarted = false;
+            }
             EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
         }
         @Override
@@ -2911,6 +2956,14 @@
             @Override
              public void enter() {
                  if (DBG) Log.d(TAG, getName() + "\n");
+
+                 /* A failed WPS connection */
+                 if (mWpsStarted) {
+                     Log.e(TAG, "WPS set up failed, enabling other networks");
+                     WifiConfigStore.enableAllNetworks();
+                     mWpsStarted = false;
+                 }
+
                  Message message = getCurrentMessage();
                  StateChangeResult stateChangeResult = (StateChangeResult) message.obj;