Merge change 6713 into donut

* changes:
  fix for [1969185] valgrind errors in new gl stuff
diff --git a/api/current.xml b/api/current.xml
index d97a0f4..7df4115 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -496,6 +496,17 @@
  visibility="public"
 >
 </field>
+<field name="GLOBAL_SEARCH"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.permission.GLOBAL_SEARCH&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="HARDWARE_TEST"
  type="java.lang.String"
  transient="false"
@@ -25945,6 +25956,17 @@
  visibility="public"
 >
 </method>
+<method name="getPathPermissions"
+ return="android.content.pm.PathPermission[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getReadPermission"
  return="java.lang.String"
  abstract="false"
@@ -26113,6 +26135,19 @@
 <parameter name="sortOrder" type="java.lang.String">
 </parameter>
 </method>
+<method name="setPathPermissions"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+<parameter name="permissions" type="android.content.pm.PathPermission[]">
+</parameter>
+</method>
 <method name="setReadPermission"
  return="void"
  abstract="false"
@@ -37714,6 +37749,73 @@
 >
 </field>
 </class>
+<class name="PathPermission"
+ extends="android.os.PatternMatcher"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="PathPermission"
+ type="android.content.pm.PathPermission"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="pattern" type="java.lang.String">
+</parameter>
+<parameter name="type" type="int">
+</parameter>
+<parameter name="readPermission" type="java.lang.String">
+</parameter>
+<parameter name="writePermission" type="java.lang.String">
+</parameter>
+</constructor>
+<constructor name="PathPermission"
+ type="android.content.pm.PathPermission"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="src" type="android.os.Parcel">
+</parameter>
+</constructor>
+<method name="getReadPermission"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getWritePermission"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</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="PermissionGroupInfo"
  extends="android.content.pm.PackageItemInfo"
  abstract="false"
@@ -38043,6 +38145,17 @@
  visibility="public"
 >
 </field>
+<field name="pathPermissions"
+ type="android.content.pm.PathPermission[]"
+ transient="false"
+ volatile="false"
+ value="null"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="readPermission"
  type="java.lang.String"
  transient="false"
@@ -46736,6 +46849,1742 @@
 </method>
 </class>
 </package>
+<package name="android.gesture"
+>
+<class name="Gesture"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.os.Parcelable">
+</implements>
+<constructor name="Gesture"
+ type="android.gesture.Gesture"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="addStroke"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="stroke" type="android.gesture.GestureStroke">
+</parameter>
+</method>
+<method name="describeContents"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getBoundingBox"
+ return="android.graphics.RectF"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getID"
+ return="long"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getLength"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getStrokes"
+ return="java.util.ArrayList&lt;android.gesture.GestureStroke&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getStrokesCount"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="toBitmap"
+ return="android.graphics.Bitmap"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="width" type="int">
+</parameter>
+<parameter name="height" type="int">
+</parameter>
+<parameter name="edge" type="int">
+</parameter>
+<parameter name="numSample" type="int">
+</parameter>
+<parameter name="color" type="int">
+</parameter>
+</method>
+<method name="toBitmap"
+ return="android.graphics.Bitmap"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="width" type="int">
+</parameter>
+<parameter name="height" type="int">
+</parameter>
+<parameter name="inset" type="int">
+</parameter>
+<parameter name="color" type="int">
+</parameter>
+</method>
+<method name="toPath"
+ return="android.graphics.Path"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="toPath"
+ return="android.graphics.Path"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="path" type="android.graphics.Path">
+</parameter>
+</method>
+<method name="toPath"
+ return="android.graphics.Path"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="width" type="int">
+</parameter>
+<parameter name="height" type="int">
+</parameter>
+<parameter name="edge" type="int">
+</parameter>
+<parameter name="numSample" type="int">
+</parameter>
+</method>
+<method name="toPath"
+ return="android.graphics.Path"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="path" type="android.graphics.Path">
+</parameter>
+<parameter name="width" type="int">
+</parameter>
+<parameter name="height" type="int">
+</parameter>
+<parameter name="edge" type="int">
+</parameter>
+<parameter name="numSample" type="int">
+</parameter>
+</method>
+<method name="writeToParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="out" type="android.os.Parcel">
+</parameter>
+<parameter name="flags" 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="GestureLibraries"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="fromFile"
+ return="android.gesture.GestureLibrary"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="path" type="java.lang.String">
+</parameter>
+</method>
+<method name="fromFile"
+ return="android.gesture.GestureLibrary"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="path" type="java.io.File">
+</parameter>
+</method>
+<method name="fromPrivateFile"
+ return="android.gesture.GestureLibrary"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="name" type="java.lang.String">
+</parameter>
+</method>
+<method name="fromRawResource"
+ return="android.gesture.GestureLibrary"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="resourceId" type="int">
+</parameter>
+</method>
+</class>
+<class name="GestureLibrary"
+ extends="java.lang.Object"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="GestureLibrary"
+ type="android.gesture.GestureLibrary"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+</constructor>
+<method name="addGesture"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="entryName" type="java.lang.String">
+</parameter>
+<parameter name="gesture" type="android.gesture.Gesture">
+</parameter>
+</method>
+<method name="getGestureEntries"
+ return="java.util.Set&lt;java.lang.String&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getGestures"
+ return="java.util.ArrayList&lt;android.gesture.Gesture&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="entryName" type="java.lang.String">
+</parameter>
+</method>
+<method name="getLearner"
+ return="android.gesture.Learner"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getOrientationStyle"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getSequenceType"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isReadOnly"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="load"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="recognize"
+ return="java.util.ArrayList&lt;android.gesture.Prediction&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="gesture" type="android.gesture.Gesture">
+</parameter>
+</method>
+<method name="removeEntry"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="entryName" type="java.lang.String">
+</parameter>
+</method>
+<method name="removeGesture"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="entryName" type="java.lang.String">
+</parameter>
+<parameter name="gesture" type="android.gesture.Gesture">
+</parameter>
+</method>
+<method name="save"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="setOrientationStyle"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="style" type="int">
+</parameter>
+</method>
+<method name="setSequenceType"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="type" type="int">
+</parameter>
+</method>
+<field name="mStore"
+ type="android.gesture.GestureStore"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+</field>
+</class>
+<class name="GestureOverlayView"
+ extends="android.widget.FrameLayout"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="GestureOverlayView"
+ type="android.gesture.GestureOverlayView"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+</constructor>
+<constructor name="GestureOverlayView"
+ type="android.gesture.GestureOverlayView"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="attrs" type="android.util.AttributeSet">
+</parameter>
+</constructor>
+<constructor name="GestureOverlayView"
+ type="android.gesture.GestureOverlayView"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="attrs" type="android.util.AttributeSet">
+</parameter>
+<parameter name="defStyle" type="int">
+</parameter>
+</constructor>
+<method name="addOnGestureListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.gesture.GestureOverlayView.OnGestureListener">
+</parameter>
+</method>
+<method name="addOnGesturePerformedListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.gesture.GestureOverlayView.OnGesturePerformedListener">
+</parameter>
+</method>
+<method name="addOnGesturingListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.gesture.GestureOverlayView.OnGesturingListener">
+</parameter>
+</method>
+<method name="cancelClearAnimation"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="cancelGesture"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="clear"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="animated" type="boolean">
+</parameter>
+</method>
+<method name="getCurrentStroke"
+ return="java.util.ArrayList&lt;android.gesture.GesturePoint&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getFadeOffset"
+ return="long"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getGesture"
+ return="android.gesture.Gesture"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getGestureColor"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getGesturePath"
+ return="android.graphics.Path"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getGesturePath"
+ return="android.graphics.Path"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="path" type="android.graphics.Path">
+</parameter>
+</method>
+<method name="getGestureStrokeAngleThreshold"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getGestureStrokeLengthThreshold"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getGestureStrokeSquarenessTreshold"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getGestureStrokeType"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getGestureStrokeWidth"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getOrientation"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getUncertainGestureColor"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isEventsInterceptionEnabled"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isFadeEnabled"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isGestureVisible"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isGesturing"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="removeAllOnGestureListeners"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="removeAllOnGesturePerformedListeners"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="removeAllOnGesturingListeners"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="removeOnGestureListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.gesture.GestureOverlayView.OnGestureListener">
+</parameter>
+</method>
+<method name="removeOnGesturePerformedListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.gesture.GestureOverlayView.OnGesturePerformedListener">
+</parameter>
+</method>
+<method name="removeOnGesturingListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.gesture.GestureOverlayView.OnGesturingListener">
+</parameter>
+</method>
+<method name="setEventsInterceptionEnabled"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="enabled" type="boolean">
+</parameter>
+</method>
+<method name="setFadeEnabled"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fadeEnabled" type="boolean">
+</parameter>
+</method>
+<method name="setFadeOffset"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fadeOffset" type="long">
+</parameter>
+</method>
+<method name="setGesture"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="gesture" type="android.gesture.Gesture">
+</parameter>
+</method>
+<method name="setGestureColor"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="color" type="int">
+</parameter>
+</method>
+<method name="setGestureStrokeAngleThreshold"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="gestureStrokeAngleThreshold" type="float">
+</parameter>
+</method>
+<method name="setGestureStrokeLengthThreshold"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="gestureStrokeLengthThreshold" type="float">
+</parameter>
+</method>
+<method name="setGestureStrokeSquarenessTreshold"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="gestureStrokeSquarenessTreshold" type="float">
+</parameter>
+</method>
+<method name="setGestureStrokeType"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="gestureStrokeType" type="int">
+</parameter>
+</method>
+<method name="setGestureStrokeWidth"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="gestureStrokeWidth" type="float">
+</parameter>
+</method>
+<method name="setGestureVisible"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="visible" type="boolean">
+</parameter>
+</method>
+<method name="setOrientation"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="orientation" type="int">
+</parameter>
+</method>
+<method name="setUncertainGestureColor"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="color" type="int">
+</parameter>
+</method>
+<field name="GESTURE_STROKE_TYPE_MULTIPLE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="GESTURE_STROKE_TYPE_SINGLE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ORIENTATION_HORIZONTAL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ORIENTATION_VERTICAL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<interface name="GestureOverlayView.OnGestureListener"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onGesture"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="overlay" type="android.gesture.GestureOverlayView">
+</parameter>
+<parameter name="event" type="android.view.MotionEvent">
+</parameter>
+</method>
+<method name="onGestureCancelled"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="overlay" type="android.gesture.GestureOverlayView">
+</parameter>
+<parameter name="event" type="android.view.MotionEvent">
+</parameter>
+</method>
+<method name="onGestureEnded"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="overlay" type="android.gesture.GestureOverlayView">
+</parameter>
+<parameter name="event" type="android.view.MotionEvent">
+</parameter>
+</method>
+<method name="onGestureStarted"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="overlay" type="android.gesture.GestureOverlayView">
+</parameter>
+<parameter name="event" type="android.view.MotionEvent">
+</parameter>
+</method>
+</interface>
+<interface name="GestureOverlayView.OnGesturePerformedListener"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onGesturePerformed"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="overlay" type="android.gesture.GestureOverlayView">
+</parameter>
+<parameter name="gesture" type="android.gesture.Gesture">
+</parameter>
+</method>
+</interface>
+<interface name="GestureOverlayView.OnGesturingListener"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onGesturingEnded"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="overlay" type="android.gesture.GestureOverlayView">
+</parameter>
+</method>
+<method name="onGesturingStarted"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="overlay" type="android.gesture.GestureOverlayView">
+</parameter>
+</method>
+</interface>
+<class name="GesturePoint"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="GesturePoint"
+ type="android.gesture.GesturePoint"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="x" type="float">
+</parameter>
+<parameter name="y" type="float">
+</parameter>
+<parameter name="t" type="long">
+</parameter>
+</constructor>
+<field name="timestamp"
+ type="long"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="x"
+ type="float"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="y"
+ type="float"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="GestureStore"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="GestureStore"
+ type="android.gesture.GestureStore"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="addGesture"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="entryName" type="java.lang.String">
+</parameter>
+<parameter name="gesture" type="android.gesture.Gesture">
+</parameter>
+</method>
+<method name="getGestureEntries"
+ return="java.util.Set&lt;java.lang.String&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getGestures"
+ return="java.util.ArrayList&lt;android.gesture.Gesture&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="entryName" type="java.lang.String">
+</parameter>
+</method>
+<method name="getOrientationStyle"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getSequenceType"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="hasChanged"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="load"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="stream" type="java.io.InputStream">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="load"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="stream" type="java.io.InputStream">
+</parameter>
+<parameter name="closeStream" type="boolean">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="recognize"
+ return="java.util.ArrayList&lt;android.gesture.Prediction&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="gesture" type="android.gesture.Gesture">
+</parameter>
+</method>
+<method name="removeEntry"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="entryName" type="java.lang.String">
+</parameter>
+</method>
+<method name="removeGesture"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="entryName" type="java.lang.String">
+</parameter>
+<parameter name="gesture" type="android.gesture.Gesture">
+</parameter>
+</method>
+<method name="save"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="stream" type="java.io.OutputStream">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="save"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="stream" type="java.io.OutputStream">
+</parameter>
+<parameter name="closeStream" type="boolean">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="setOrientationStyle"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="style" type="int">
+</parameter>
+</method>
+<method name="setSequenceType"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="type" type="int">
+</parameter>
+</method>
+<field name="ORIENTATION_INVARIANT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ORIENTATION_SENSITIVE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SEQUENCE_INVARIANT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SEQUENCE_SENSITIVE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="GestureStroke"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="GestureStroke"
+ type="android.gesture.GestureStroke"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="points" type="java.util.ArrayList&lt;android.gesture.GesturePoint&gt;">
+</parameter>
+</constructor>
+<method name="clearPath"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="computeOrientedBoundingBox"
+ return="android.gesture.OrientedBoundingBox"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getPath"
+ return="android.graphics.Path"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="toPath"
+ return="android.graphics.Path"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="width" type="float">
+</parameter>
+<parameter name="height" type="float">
+</parameter>
+<parameter name="numSample" type="int">
+</parameter>
+</method>
+<field name="boundingBox"
+ type="android.graphics.RectF"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="length"
+ type="float"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="points"
+ type="float[]"
+ transient="false"
+ volatile="false"
+ value="null"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="Learner"
+ extends="java.lang.Object"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility=""
+>
+</class>
+<class name="OrientedBoundingBox"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<field name="centerX"
+ type="float"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="centerY"
+ type="float"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="height"
+ type="float"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="orientation"
+ type="float"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="squareness"
+ type="float"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="width"
+ type="float"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="Prediction"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<field name="name"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="score"
+ type="double"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+</package>
 <package name="android.graphics"
 >
 <class name="AvoidXfermode"
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 5ee29ac..ec8d56b 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -2545,32 +2545,39 @@
             classname = "android.app.FullBackupAgent";
         }
         try {
-            java.lang.ClassLoader cl = packageInfo.getClassLoader();
-            agent = (BackupAgent) cl.loadClass(data.appInfo.backupAgentName).newInstance();
-        } catch (Exception e) {
-            throw new RuntimeException("Unable to instantiate backup agent "
-                    + data.appInfo.backupAgentName + ": " + e.toString(), e);
-        }
-        
-        // set up the agent's context
-        try {
-            if (DEBUG_BACKUP) Log.v(TAG, "Initializing BackupAgent "
-                    + data.appInfo.backupAgentName);
-            
-            ApplicationContext context = new ApplicationContext();
-            context.init(packageInfo, null, this);
-            context.setOuterContext(agent);
-            agent.attach(context);
-            agent.onCreate();
+            IBinder binder = null;
+            try {
+                java.lang.ClassLoader cl = packageInfo.getClassLoader();
+                agent = (BackupAgent) cl.loadClass(data.appInfo.backupAgentName).newInstance();
+
+                // set up the agent's context
+                if (DEBUG_BACKUP) Log.v(TAG, "Initializing BackupAgent "
+                        + data.appInfo.backupAgentName);
+
+                ApplicationContext context = new ApplicationContext();
+                context.init(packageInfo, null, this);
+                context.setOuterContext(agent);
+                agent.attach(context);
+
+                agent.onCreate();
+                binder = agent.onBind();
+                mBackupAgents.put(packageName, agent);
+            } catch (Exception e) {
+                // If this is during restore, fail silently; otherwise go
+                // ahead and let the user see the crash.
+                Log.e(TAG, "Agent threw during creation: " + e);
+                if (data.backupMode != IApplicationThread.BACKUP_MODE_RESTORE) {
+                    throw e;
+                }
+                // falling through with 'binder' still null
+            }
 
             // tell the OS that we're live now
-            IBinder binder = agent.onBind();
             try {
                 ActivityManagerNative.getDefault().backupAgentCreated(packageName, binder);
             } catch (RemoteException e) {
                 // nothing to do.
             }
-            mBackupAgents.put(packageName, agent);
         } catch (Exception e) {
             throw new RuntimeException("Unable to create BackupAgent "
                     + data.appInfo.backupAgentName + ": " + e.toString(), e);
diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java
index 022a9d9..6d6aca4 100644
--- a/core/java/android/app/SearchDialog.java
+++ b/core/java/android/app/SearchDialog.java
@@ -991,7 +991,7 @@
     };
 
     @Override
-    public void cancel() {
+    public void dismiss() {
         if (!isShowing()) return;
 
         // We made sure the IME was displayed, so also make sure it is closed
@@ -1003,7 +1003,7 @@
                     getWindow().getDecorView().getWindowToken(), 0);
         }
         
-        super.cancel();
+        super.dismiss();
     }
     
     /**
diff --git a/core/java/android/app/SuggestionsAdapter.java b/core/java/android/app/SuggestionsAdapter.java
index 49c94d1..c8e952f 100644
--- a/core/java/android/app/SuggestionsAdapter.java
+++ b/core/java/android/app/SuggestionsAdapter.java
@@ -18,7 +18,8 @@
 
 import android.content.ContentResolver;
 import android.content.Context;
-import android.content.res.Resources.NotFoundException;
+import android.content.res.ColorStateList;
+import android.content.res.Resources;
 import android.database.Cursor;
 import android.graphics.Canvas;
 import android.graphics.drawable.Drawable;
@@ -300,31 +301,19 @@
         ((SuggestionItemView)view).setColor(backgroundColor);
 
         final boolean isHtml = mFormatCol > 0 && "html".equals(cursor.getString(mFormatCol));
-        setViewText(cursor, views.mText1, mText1Col, isHtml);
-        setViewText(cursor, views.mText2, mText2Col, isHtml);
+        String text1 = null;
+        if (mText1Col >= 0) {
+            text1 = cursor.getString(mText1Col);
+        }
+        String text2 = null;
+        if (mText2Col >= 0) {
+            text2 = cursor.getString(mText2Col);
+        }
+        ((SuggestionItemView)view).setTextStrings(text1, text2, isHtml, mProviderContext);
         setViewIcon(cursor, views.mIcon1, mIconName1Col);
         setViewIcon(cursor, views.mIcon2, mIconName2Col);
     }
 
-    private void setViewText(Cursor cursor, TextView v, int textCol, boolean isHtml) {
-        if (v == null) {
-            return;
-        }
-        CharSequence text = null;
-        if (textCol >= 0) {
-            String str = cursor.getString(textCol);
-            text = (str != null && isHtml) ? Html.fromHtml(str) : str;
-        }
-        // Set the text even if it's null, since we need to clear any previous text.
-        v.setText(text);
-
-        if (TextUtils.isEmpty(text)) {
-            v.setVisibility(View.GONE);
-        } else {
-            v.setVisibility(View.VISIBLE);
-        }
-    }
-
     private void setViewIcon(Cursor cursor, ImageView v, int iconNameCol) {
         if (v == null) {
             return;
@@ -476,7 +465,7 @@
             if (drawable != null) {
                 mOutsideDrawablesCache.put(drawableId, drawable);
             }
-        } catch (NotFoundException nfe) {
+        } catch (Resources.NotFoundException nfe) {
             if (DBG) Log.d(LOG_TAG, "Icon resource not found: " + drawableId);
             // drawable = null;
         }
@@ -509,8 +498,82 @@
      * draws on top of the list view selection highlight).
      */
     private class SuggestionItemView extends ViewGroup {
+        /**
+         * Parses a given HTMl string and manages Spannable variants of the string for different
+         * states of the suggestion item (selected, pressed and normal). Colors for these different
+         * states are specified in the html font tag color attribute in the format '@<RESOURCEID>'
+         * where RESOURCEID is the ID of a ColorStateList or Color resource. 
+         */
+        private class MultiStateText {
+            private CharSequence mNormal = null;  // text to display in normal state.
+            private CharSequence mSelected = null;  // text to display in selected state.
+            private CharSequence mPressed = null;  // text to display in pressed state.
+            private String mPlainText = null;  // valid if the text is stateless plain text.
+
+            public MultiStateText(boolean isHtml, String text, Context context) {
+                if (!isHtml || text == null) {
+                    mPlainText = text;
+                    return;
+                }
+
+                String textNormal = text;
+                String textSelected = text;
+                String textPressed = text;
+                int textLength = text.length();
+                int start = text.indexOf("\"@");
+
+                // For each font color attribute which has the value in the form '@<RESOURCEID>',
+                // try to load the resource and create the display strings for the 3 states.
+                while (start >= 0) {
+                    start++;
+                    int end = text.indexOf("\"", start);
+                    if (end == -1) break;
+
+                    String colorIdString = text.substring(start, end);
+                    int colorId = Integer.parseInt(colorIdString.substring(1));
+                    try {
+                        // The following call works both for color lists and colors.
+                        ColorStateList csl = context.getResources().getColorStateList(colorId);
+                        int normalColor = csl.getColorForState(
+                                View.EMPTY_STATE_SET, csl.getDefaultColor());
+                        int selectedColor = csl.getColorForState(
+                                View.SELECTED_STATE_SET, csl.getDefaultColor());
+                        int pressedColor = csl.getColorForState(
+                                View.PRESSED_STATE_SET, csl.getDefaultColor());
+
+                        // Convert the int color values into a hex string, and strip the first 2
+                        // characters which will be the alpha (html doesn't want this).
+                        textNormal = textNormal.replace(colorIdString,
+                                "#" + Integer.toHexString(normalColor).substring(2));
+                        textSelected = textSelected.replace(colorIdString,
+                                "#" + Integer.toHexString(selectedColor).substring(2));
+                        textPressed = textPressed.replace(colorIdString,
+                                "#" + Integer.toHexString(pressedColor).substring(2));
+                    } catch (Resources.NotFoundException e) {
+                        // Nothing to do.
+                    }
+
+                    start = text.indexOf("\"@", end);
+                }
+                mNormal = Html.fromHtml(textNormal);
+                mSelected = Html.fromHtml(textSelected);
+                mPressed = Html.fromHtml(textPressed);
+            }
+            public CharSequence normal() {
+                return (mPlainText != null) ? mPlainText : mNormal;
+            }
+            public CharSequence selected() {
+                return (mPlainText != null) ? mPlainText : mSelected;
+            }
+            public CharSequence pressed() {
+                return (mPlainText != null) ? mPlainText : mPressed;
+            }
+        }
+
         private int mBackgroundColor;  // the background color to draw in normal state.
         private View mView;  // the suggestion item's view.
+        private MultiStateText mText1Strings = null;
+        private MultiStateText mText2Strings = null;
 
         protected SuggestionItemView(Context context, Cursor cursor) {
             // Initialize ourselves
@@ -537,12 +600,48 @@
             }
         }
 
+        private void setInitialTextForView(TextView view, MultiStateText multiState,
+                String plainText) {
+            // Set the text even if it's null, since we need to clear any previous text.
+            CharSequence text = (multiState != null) ? multiState.normal() : plainText;
+            view.setText(text);
+
+            if (TextUtils.isEmpty(text)) {
+                view.setVisibility(View.GONE);
+            } else {
+                view.setVisibility(View.VISIBLE);
+            }
+        }
+
+        public void setTextStrings(String text1, String text2, boolean isHtml, Context context) {
+            mText1Strings = new MultiStateText(isHtml, text1, context);
+            mText2Strings = new MultiStateText(isHtml, text2, context);
+
+            ChildViewCache views = (ChildViewCache) getTag();
+            setInitialTextForView(views.mText1, mText1Strings, text1);
+            setInitialTextForView(views.mText2, mText2Strings, text2);
+        }
+
+        public void updateTextViewContentIfRequired() {
+            // Check if the pressed or selected state has changed since the last call.
+            boolean isPressedNow = isPressed();
+            boolean isSelectedNow = isSelected();
+
+            ChildViewCache views = (ChildViewCache) getTag();
+            views.mText1.setText((isPressedNow ? mText1Strings.pressed() :
+                (isSelectedNow ? mText1Strings.selected() : mText1Strings.normal())));
+            views.mText2.setText((isPressedNow ? mText2Strings.pressed() :
+                (isSelectedNow ? mText2Strings.selected() : mText2Strings.normal())));
+        }
+
         public void setColor(int backgroundColor) {
             mBackgroundColor = backgroundColor;
         }
 
         @Override
         public void dispatchDraw(Canvas canvas) {
+            updateTextViewContentIfRequired();
+
             if (mBackgroundColor != 0 && !isPressed() && !isSelected()) {
                 canvas.drawColor(mBackgroundColor);
             }
diff --git a/core/java/android/backup/BackupManager.java b/core/java/android/backup/BackupManager.java
index 34a1a0c..37a58a9 100644
--- a/core/java/android/backup/BackupManager.java
+++ b/core/java/android/backup/BackupManager.java
@@ -42,6 +42,9 @@
 public class BackupManager {
     private static final String TAG = "BackupManager";
 
+    /** @hide TODO: REMOVE THIS */
+    public static final boolean EVEN_THINK_ABOUT_DOING_RESTORE = false;
+
     private Context mContext;
     private static IBackupManager sService;
 
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 5cc5730..6b50405 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -17,6 +17,7 @@
 package android.content;
 
 import android.content.pm.PackageManager;
+import android.content.pm.PathPermission;
 import android.content.pm.ProviderInfo;
 import android.content.res.AssetFileDescriptor;
 import android.content.res.Configuration;
@@ -29,6 +30,7 @@
 import android.net.Uri;
 import android.os.Binder;
 import android.os.ParcelFileDescriptor;
+import android.os.Process;
 
 import java.io.File;
 import java.io.FileNotFoundException;
@@ -65,8 +67,10 @@
  */
 public abstract class ContentProvider implements ComponentCallbacks {
     private Context mContext = null;
+    private int mMyUid;
     private String mReadPermission;
     private String mWritePermission;
+    private PathPermission[] mPathPermissions;
 
     private Transport mTransport = new Transport();
 
@@ -108,24 +112,20 @@
         public IBulkCursor bulkQuery(Uri uri, String[] projection,
                 String selection, String[] selectionArgs, String sortOrder,
                 IContentObserver observer, CursorWindow window) {
-            checkReadPermission(uri);
+            enforceReadPermission(uri);
             Cursor cursor = ContentProvider.this.query(uri, projection,
                     selection, selectionArgs, sortOrder);
             if (cursor == null) {
                 return null;
             }
-            String wperm = getWritePermission();
             return new CursorToBulkCursorAdaptor(cursor, observer,
                     ContentProvider.this.getClass().getName(),
-                    wperm == null ||
-                    getContext().checkCallingOrSelfPermission(getWritePermission())
-                            == PackageManager.PERMISSION_GRANTED,
-                    window);
+                    hasWritePermission(uri), window);
         }
 
         public Cursor query(Uri uri, String[] projection,
                 String selection, String[] selectionArgs, String sortOrder) {
-            checkReadPermission(uri);
+            enforceReadPermission(uri);
             return ContentProvider.this.query(uri, projection, selection,
                     selectionArgs, sortOrder);
         }
@@ -136,55 +136,84 @@
 
 
         public Uri insert(Uri uri, ContentValues initialValues) {
-            checkWritePermission(uri);
+            enforceWritePermission(uri);
             return ContentProvider.this.insert(uri, initialValues);
         }
 
         public int bulkInsert(Uri uri, ContentValues[] initialValues) {
-            checkWritePermission(uri);
+            enforceWritePermission(uri);
             return ContentProvider.this.bulkInsert(uri, initialValues);
         }
 
         public int delete(Uri uri, String selection, String[] selectionArgs) {
-            checkWritePermission(uri);
+            enforceWritePermission(uri);
             return ContentProvider.this.delete(uri, selection, selectionArgs);
         }
 
         public int update(Uri uri, ContentValues values, String selection,
                 String[] selectionArgs) {
-            checkWritePermission(uri);
+            enforceWritePermission(uri);
             return ContentProvider.this.update(uri, values, selection, selectionArgs);
         }
 
         public ParcelFileDescriptor openFile(Uri uri, String mode)
                 throws FileNotFoundException {
-            if (mode != null && mode.startsWith("rw")) checkWritePermission(uri);
-            else checkReadPermission(uri);
+            if (mode != null && mode.startsWith("rw")) enforceWritePermission(uri);
+            else enforceReadPermission(uri);
             return ContentProvider.this.openFile(uri, mode);
         }
 
         public AssetFileDescriptor openAssetFile(Uri uri, String mode)
                 throws FileNotFoundException {
-            if (mode != null && mode.startsWith("rw")) checkWritePermission(uri);
-            else checkReadPermission(uri);
+            if (mode != null && mode.startsWith("rw")) enforceWritePermission(uri);
+            else enforceReadPermission(uri);
             return ContentProvider.this.openAssetFile(uri, mode);
         }
 
         public ISyncAdapter getSyncAdapter() {
-            checkWritePermission(null);
+            enforceWritePermission(null);
             SyncAdapter sa = ContentProvider.this.getSyncAdapter();
             return sa != null ? sa.getISyncAdapter() : null;
         }
 
-        private void checkReadPermission(Uri uri) {
+        private void enforceReadPermission(Uri uri) {
+            final int uid = Binder.getCallingUid();
+            if (uid == mMyUid) {
+                return;
+            }
+            
+            final Context context = getContext();
             final String rperm = getReadPermission();
             final int pid = Binder.getCallingPid();
-            final int uid = Binder.getCallingUid();
-            if (getContext().checkUriPermission(uri, rperm, null, pid, uid,
+            if (rperm == null
+                    || context.checkPermission(rperm, pid, uid)
+                    == PackageManager.PERMISSION_GRANTED) {
+                return;
+            }
+            
+            PathPermission[] pps = getPathPermissions();
+            if (pps != null) {
+                final String path = uri.getPath();
+                int i = pps.length;
+                while (i > 0) {
+                    i--;
+                    final PathPermission pp = pps[i];
+                    final String pprperm = pp.getReadPermission();
+                    if (pprperm != null && pp.match(path)) {
+                        if (context.checkPermission(pprperm, pid, uid)
+                                == PackageManager.PERMISSION_GRANTED) {
+                            return;
+                        }
+                    }
+                }
+            }
+            
+            if (context.checkUriPermission(uri, pid, uid,
                     Intent.FLAG_GRANT_READ_URI_PERMISSION)
                     == PackageManager.PERMISSION_GRANTED) {
                 return;
             }
+            
             String msg = "Permission Denial: reading "
                     + ContentProvider.this.getClass().getName()
                     + " uri " + uri + " from pid=" + Binder.getCallingPid()
@@ -193,20 +222,57 @@
             throw new SecurityException(msg);
         }
 
-        private void checkWritePermission(Uri uri) {
+        private boolean hasWritePermission(Uri uri) {
+            final int uid = Binder.getCallingUid();
+            if (uid == mMyUid) {
+                return true;
+            }
+            
+            final Context context = getContext();
             final String wperm = getWritePermission();
             final int pid = Binder.getCallingPid();
-            final int uid = Binder.getCallingUid();
-            if (getContext().checkUriPermission(uri, null, wperm, pid, uid,
+            if (wperm == null
+                    || context.checkPermission(wperm, pid, uid)
+                    == PackageManager.PERMISSION_GRANTED) {
+                return true;
+            }
+            
+            PathPermission[] pps = getPathPermissions();
+            if (pps != null) {
+                final String path = uri.getPath();
+                int i = pps.length;
+                while (i > 0) {
+                    i--;
+                    final PathPermission pp = pps[i];
+                    final String ppwperm = pp.getWritePermission();
+                    if (ppwperm != null && pp.match(path)) {
+                        if (context.checkPermission(ppwperm, pid, uid)
+                                == PackageManager.PERMISSION_GRANTED) {
+                            return true;
+                        }
+                    }
+                }
+            }
+            
+            if (context.checkUriPermission(uri, pid, uid,
                     Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
                     == PackageManager.PERMISSION_GRANTED) {
+                return true;
+            }
+            
+            return false;
+        }
+        
+        private void enforceWritePermission(Uri uri) {
+            if (hasWritePermission(uri)) {
                 return;
             }
+            
             String msg = "Permission Denial: writing "
                     + ContentProvider.this.getClass().getName()
                     + " uri " + uri + " from pid=" + Binder.getCallingPid()
                     + ", uid=" + Binder.getCallingUid()
-                    + " requires " + wperm;
+                    + " requires " + getWritePermission();
             throw new SecurityException(msg);
         }
     }
@@ -266,6 +332,28 @@
     }
 
     /**
+     * Change the path-based permission required to read and/or write data in
+     * the content provider.  This is normally set for you from its manifest
+     * information when the provider is first created.
+     *
+     * @param permissions Array of path permission descriptions.
+     */
+    protected final void setPathPermissions(PathPermission[] permissions) {
+        mPathPermissions = permissions;
+    }
+
+    /**
+     * Return the path-based permissions required for read and/or write access to
+     * this content provider.  This method can be called from multiple
+     * threads, as described in
+     * <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals:
+     * Processes and Threads</a>.
+     */
+    public final PathPermission[] getPathPermissions() {
+        return mPathPermissions;
+    }
+
+    /**
      * Called when the provider is being started.
      *
      * @return true if the provider was successfully loaded, false otherwise
@@ -600,9 +688,11 @@
          */
         if (mContext == null) {
             mContext = context;
+            mMyUid = Process.myUid();
             if (info != null) {
                 setReadPermission(info.readPermission);
                 setWritePermission(info.writePermission);
+                setPathPermissions(info.pathPermissions);
             }
             ContentProvider.this.onCreate();
         }
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index b293636..0e2deed 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1918,6 +1918,7 @@
                         outInfo.metaData, outError)) == null) {
                     return false;
                 }
+                
             } else if (parser.getName().equals("grant-uri-permission")) {
                 TypedArray sa = res.obtainAttributes(attrs,
                         com.android.internal.R.styleable.AndroidManifestGrantUriPermission);
@@ -1941,7 +1942,7 @@
                 if (str != null) {
                     pa = new PatternMatcher(str, PatternMatcher.PATTERN_SIMPLE_GLOB);
                 }
-
+                
                 sa.recycle();
 
                 if (pa != null) {
@@ -1956,6 +1957,101 @@
                         outInfo.info.uriPermissionPatterns = newp;
                     }
                     outInfo.info.grantUriPermissions = true;
+                } else {
+                    if (!RIGID_PARSER) {
+                        Log.w(TAG, "Problem in package " + mArchiveSourcePath + ":");
+                        Log.w(TAG, "No path, pathPrefix, or pathPattern for <path-permission>");
+                        XmlUtils.skipCurrentTag(parser);
+                        continue;
+                    }
+                    outError[0] = "No path, pathPrefix, or pathPattern for <path-permission>";
+                    return false;
+                }
+                XmlUtils.skipCurrentTag(parser);
+
+            } else if (parser.getName().equals("path-permission")) {
+                TypedArray sa = res.obtainAttributes(attrs,
+                        com.android.internal.R.styleable.AndroidManifestPathPermission);
+
+                PathPermission pa = null;
+
+                String permission = sa.getNonResourceString(
+                        com.android.internal.R.styleable.AndroidManifestPathPermission_permission);
+                String readPermission = sa.getNonResourceString(
+                        com.android.internal.R.styleable.AndroidManifestPathPermission_readPermission);
+                if (readPermission == null) {
+                    readPermission = permission;
+                }
+                String writePermission = sa.getNonResourceString(
+                        com.android.internal.R.styleable.AndroidManifestPathPermission_writePermission);
+                if (writePermission == null) {
+                    writePermission = permission;
+                }
+                
+                boolean havePerm = false;
+                if (readPermission != null) {
+                    readPermission = readPermission.intern();
+                    havePerm = true;
+                }
+                if (writePermission != null) {
+                    writePermission = readPermission.intern();
+                    havePerm = true;
+                }
+
+                if (!havePerm) {
+                    if (!RIGID_PARSER) {
+                        Log.w(TAG, "Problem in package " + mArchiveSourcePath + ":");
+                        Log.w(TAG, "No readPermission or writePermssion for <path-permission>");
+                        XmlUtils.skipCurrentTag(parser);
+                        continue;
+                    }
+                    outError[0] = "No readPermission or writePermssion for <path-permission>";
+                    return false;
+                }
+                
+                String path = sa.getNonResourceString(
+                        com.android.internal.R.styleable.AndroidManifestPathPermission_path);
+                if (path != null) {
+                    pa = new PathPermission(path,
+                            PatternMatcher.PATTERN_LITERAL, readPermission, writePermission);
+                }
+
+                path = sa.getNonResourceString(
+                        com.android.internal.R.styleable.AndroidManifestPathPermission_pathPrefix);
+                if (path != null) {
+                    pa = new PathPermission(path,
+                            PatternMatcher.PATTERN_PREFIX, readPermission, writePermission);
+                }
+
+                path = sa.getNonResourceString(
+                        com.android.internal.R.styleable.AndroidManifestPathPermission_pathPattern);
+                if (path != null) {
+                    pa = new PathPermission(path,
+                            PatternMatcher.PATTERN_SIMPLE_GLOB, readPermission, writePermission);
+                }
+
+                sa.recycle();
+
+                if (pa != null) {
+                    if (outInfo.info.pathPermissions == null) {
+                        outInfo.info.pathPermissions = new PathPermission[1];
+                        outInfo.info.pathPermissions[0] = pa;
+                    } else {
+                        final int N = outInfo.info.pathPermissions.length;
+                        PathPermission[] newp = new PathPermission[N+1];
+                        System.arraycopy(outInfo.info.pathPermissions, 0, newp, 0, N);
+                        newp[N] = pa;
+                        outInfo.info.pathPermissions = newp;
+                    }
+                } else {
+                    if (!RIGID_PARSER) {
+                        Log.w(TAG, "Problem in package " + mArchiveSourcePath + ":");
+                        Log.w(TAG, "No path, pathPrefix, or pathPattern for <path-permission>");
+                        XmlUtils.skipCurrentTag(parser);
+                        continue;
+                    }
+                    outError[0] = "No path, pathPrefix, or pathPattern for <path-permission>";
+                    return false;
                 }
                 XmlUtils.skipCurrentTag(parser);
 
diff --git a/core/java/android/content/pm/PathPermission.java b/core/java/android/content/pm/PathPermission.java
new file mode 100644
index 0000000..7e49d7d
--- /dev/null
+++ b/core/java/android/content/pm/PathPermission.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.PatternMatcher;
+
+/**
+ * Description of permissions needed to access a particular path
+ * in a {@link ProviderInfo}.
+ */
+public class PathPermission extends PatternMatcher {
+    private final String mReadPermission;
+    private final String mWritePermission;
+    
+    public PathPermission(String pattern, int type, String readPermission,
+            String writePermission) {
+        super(pattern, type);
+        mReadPermission = readPermission;
+        mWritePermission = writePermission;
+    }
+    
+    public String getReadPermission() {
+        return mReadPermission;
+    }
+    
+    public String getWritePermission() {
+        return mWritePermission;
+    }
+    
+    public void writeToParcel(Parcel dest, int flags) {
+        super.writeToParcel(dest, flags);
+        dest.writeString(mReadPermission);
+        dest.writeString(mWritePermission);
+    }
+    
+    public PathPermission(Parcel src) {
+        super(src);
+        mReadPermission = src.readString();
+        mWritePermission = src.readString();
+    }
+    
+    public static final Parcelable.Creator<PathPermission> CREATOR
+            = new Parcelable.Creator<PathPermission>() {
+        public PathPermission createFromParcel(Parcel source) {
+            return new PathPermission(source);
+        }
+
+        public PathPermission[] newArray(int size) {
+            return new PathPermission[size];
+        }
+    };
+}
\ No newline at end of file
diff --git a/core/java/android/content/pm/ProviderInfo.java b/core/java/android/content/pm/ProviderInfo.java
index b67ddf6..d01460e 100644
--- a/core/java/android/content/pm/ProviderInfo.java
+++ b/core/java/android/content/pm/ProviderInfo.java
@@ -28,6 +28,7 @@
  */
 public final class ProviderInfo extends ComponentInfo
         implements Parcelable {
+    
     /** The name provider is published under content:// */
     public String authority = null;
     
@@ -56,6 +57,14 @@
      */
     public PatternMatcher[] uriPermissionPatterns = null;
     
+    /**
+     * If non-null, these are path-specific permissions that are allowed for
+     * accessing the provider.  Any permissions listed here will allow a
+     * holding client to access the provider, and the provider will check
+     * the URI it provides when making calls against the patterns here.
+     */
+    public PathPermission[] pathPermissions = null;
+    
     /** If true, this content provider allows multiple instances of itself
      *  to run in different process.  If false, a single instances is always
      *  run in {@link #processName}. */
@@ -78,6 +87,7 @@
         writePermission = orig.writePermission;
         grantUriPermissions = orig.grantUriPermissions;
         uriPermissionPatterns = orig.uriPermissionPatterns;
+        pathPermissions = orig.pathPermissions;
         multiprocess = orig.multiprocess;
         initOrder = orig.initOrder;
         isSyncable = orig.isSyncable;
@@ -94,6 +104,7 @@
         out.writeString(writePermission);
         out.writeInt(grantUriPermissions ? 1 : 0);
         out.writeTypedArray(uriPermissionPatterns, parcelableFlags);
+        out.writeTypedArray(pathPermissions, parcelableFlags);
         out.writeInt(multiprocess ? 1 : 0);
         out.writeInt(initOrder);
         out.writeInt(isSyncable ? 1 : 0);
@@ -122,6 +133,7 @@
         writePermission = in.readString();
         grantUriPermissions = in.readInt() != 0;
         uriPermissionPatterns = in.createTypedArray(PatternMatcher.CREATOR);
+        pathPermissions = in.createTypedArray(PathPermission.CREATOR);
         multiprocess = in.readInt() != 0;
         initOrder = in.readInt();
         isSyncable = in.readInt() != 0;
diff --git a/core/java/android/gesture/Gesture.java b/core/java/android/gesture/Gesture.java
index 2262477..c92f665 100755
--- a/core/java/android/gesture/Gesture.java
+++ b/core/java/android/gesture/Gesture.java
@@ -57,11 +57,6 @@
         mGestureID = GESTURE_ID_BASE + sGestureCount++;
     }
 
-    void recycle() {
-        mStrokes.clear();
-        mBoundingBox.setEmpty();
-    }
-
     /**
      * @return all the strokes of the gesture
      */
@@ -162,20 +157,6 @@
     }
 
     /**
-     * draw the gesture
-     * 
-     * @param canvas
-     */
-    void draw(Canvas canvas, Paint paint) {
-        final ArrayList<GestureStroke> strokes = mStrokes;
-        final int count = strokes.size();
-
-        for (int i = 0; i < count; i++) {
-            strokes.get(i).draw(canvas, paint);
-        }
-    }
-
-    /**
      * Create a bitmap of the gesture with a transparent background
      * 
      * @param width width of the target bitmap
diff --git a/core/java/android/gesture/package.html b/core/java/android/gesture/package.html
index a54a0171..32c44d2 100644
--- a/core/java/android/gesture/package.html
+++ b/core/java/android/gesture/package.html
@@ -1,5 +1,5 @@
 <HTML>
 <BODY>
-@hide
+Provides classes to create, recognize, load and save gestures.
 </BODY>
 </HTML>
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 1214abc..51e6c1e 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -68,6 +68,12 @@
     public static final int PHONE_UID = 1001;
 
     /**
+     * Defines the UID/GID for the WIFI supplicant process.
+     * @hide
+     */
+    public static final int WIFI_UID = 1010;
+
+    /**
      * Defines the start of a range of UIDs (and GIDs), going from this
      * number to {@link #LAST_APPLICATION_UID} that are reserved for assigning
      * to applications.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index aa583ac..6ed1ac8 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1328,7 +1328,7 @@
          * boolean (1 or 0).
          */
         public static final String HAPTIC_FEEDBACK_ENABLED = "haptic_feedback_enabled";
-        
+
         /**
          * Whether live web suggestions while the user types into search dialogs are
          * enabled. Browsers and other search UIs should respect this, as it allows
@@ -2300,7 +2300,7 @@
          * @hide
          */
         public static final String BACKUP_TRANSPORT = "backup_transport";
-        
+
         /**
          * Version for which the setup wizard was last shown.  Bumped for
          * each release when there is new setup information to show.
@@ -2954,6 +2954,13 @@
                 "vending_pd_resend_frequency_ms";
 
         /**
+         * Frequency in milliseconds at which we should cycle through the promoted applications
+         * on the home screen or the categories page.
+         */
+        public static final String VENDING_PROMO_REFRESH_FREQUENCY_MS =
+                "vending_promo_refresh_freq_ms";
+
+        /**
          * URL that points to the legal terms of service to display in Settings.
          * <p>
          * This should be a https URL. For a pretty user-friendly URL, use
diff --git a/core/java/android/server/search/Searchables.java b/core/java/android/server/search/Searchables.java
index b959907..c615957 100644
--- a/core/java/android/server/search/Searchables.java
+++ b/core/java/android/server/search/Searchables.java
@@ -247,7 +247,12 @@
             for (int i = 0; i < webSearchInfoList.size(); ++i) {
                 ActivityInfo ai = webSearchInfoList.get(i).activityInfo;
                 ComponentName component = new ComponentName(ai.packageName, ai.name);
-                newSearchablesForWebSearchList.add(newSearchablesMap.get(component));
+                SearchableInfo searchable = newSearchablesMap.get(component);
+                if (searchable == null) {
+                    Log.w(LOG_TAG, "did not find component in searchables: " + component);
+                } else {
+                    newSearchablesForWebSearchList.add(searchable);
+                }
             }
         }
 
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index ec671d5..9f01923 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -16,15 +16,27 @@
 
 package android.webkit;
 
+import android.content.ContentValues;
 import android.content.Context;
+import android.content.SharedPreferences;
 import android.os.Build;
 import android.os.Handler;
 import android.os.Message;
+import android.preference.PreferenceManager;
 import android.provider.Checkin;
+import android.provider.Settings;
+import android.util.Log;
 
+import java.io.File;
 import java.lang.SecurityException;
-import android.content.pm.PackageManager;
 
+import android.content.SharedPreferences.Editor;
+import android.content.pm.PackageManager;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteException;
+import android.database.sqlite.SQLiteStatement;
+
+import java.util.HashSet;
 import java.util.Locale;
 
 /**
@@ -176,6 +188,43 @@
     private boolean         mBuiltInZoomControls = false;
     private boolean         mAllowFileAccess = true;
 
+    // Donut-specific hack to keep Gears permissions in sync with the
+    // system location setting.
+    // TODO: Make sure this hack is removed in  Eclair, when Gears
+    // is also removed.
+    // Used to remember if we checked the Gears permissions already.
+    static boolean mCheckedGearsPermissions = false;
+    // The Gears permissions database directory.
+    private final static String GEARS_DATABASE_DIR = "gears";
+    // The Gears permissions database file name.
+    private final static String GEARS_DATABASE_FILE = "permissions.db";
+    // The Gears location permissions table.
+    private final static String GEARS_LOCATION_ACCESS_TABLE_NAME = 
+        "LocationAccess";
+    // The Gears storage access permissions table.
+    private final static String GEARS_STORAGE_ACCESS_TABLE_NAME = "Access";
+    // The Gears permissions db schema version table.
+    private final static String GEARS_SCHEMA_VERSION_TABLE_NAME =
+        "VersionInfo";
+    // The shared pref name.
+    private static final String LAST_KNOWN_LOCATION_SETTING =
+        "lastKnownLocationSystemSetting";
+    // The Browser package name.
+    private static final String BROWSER_PACKAGE_NAME = "com.android.browser";
+    // The Google URLs whitelisted for Gears location access.
+    private static HashSet<String> sGearsWhiteList;
+
+    static {
+        sGearsWhiteList = new HashSet<String>();
+        // NOTE: DO NOT ADD A "/" AT THE END!
+        sGearsWhiteList.add("http://www.google.com");
+        sGearsWhiteList.add("http://www.google.co.uk");
+    }
+
+    private static final String LOGTAG = "webcore";
+    static final boolean DEBUG = false;
+    static final boolean LOGV_ENABLED = DEBUG;
+
     // Class to handle messages before WebCore is ready.
     private class EventHandler {
         // Message id for syncing
@@ -196,6 +245,7 @@
                     switch (msg.what) {
                         case SYNC:
                             synchronized (WebSettings.this) {
+                                checkGearsPermissions();
                                 if (mBrowserFrame.mNativeFrame != 0) {
                                     nativeSync(mBrowserFrame.mNativeFrame);
                                 }
@@ -1163,6 +1213,126 @@
         return size;
     }
 
+    private void checkGearsPermissions() {
+        // Did we already check the permissions?
+        if (mCheckedGearsPermissions) {
+            return;
+        }
+        // Are we running in the browser?
+        if (!BROWSER_PACKAGE_NAME.equals(mContext.getPackageName())) {
+            return;
+        }
+        // Is the pluginsPath sane?
+        if (mPluginsPath == null || mPluginsPath.length() == 0) {
+            // We don't yet have a meaningful plugin path, so
+            // we can't do anything about the Gears permissions.
+            return;
+        }
+        // Remember we checked the Gears permissions.
+        mCheckedGearsPermissions = true;
+        // Get the current system settings.
+        int setting = Settings.Secure.getInt(mContext.getContentResolver(),
+                Settings.Secure.USE_LOCATION_FOR_SERVICES, -1);
+        // Check if we need to set the Gears permissions.
+        if (setting != -1 && locationSystemSettingChanged(setting)) {
+            setGearsPermissionForGoogleDomains(setting);
+        }
+    }
+
+    private boolean locationSystemSettingChanged(int newSetting) {
+        SharedPreferences prefs =
+            PreferenceManager.getDefaultSharedPreferences(mContext);
+        int oldSetting = 0;
+        oldSetting = prefs.getInt(LAST_KNOWN_LOCATION_SETTING, oldSetting);
+        if (oldSetting == newSetting) {
+            return false;
+        }
+        Editor ed = prefs.edit();
+        ed.putInt(LAST_KNOWN_LOCATION_SETTING, newSetting);
+        ed.commit();
+        return true;
+    }
+
+    private void setGearsPermissionForGoogleDomains(int systemPermission) {
+        // Transform the system permission into a Gears permission
+        int gearsPermission = (systemPermission == 1 ? 1 : 2);
+        // Build the path to the Gears library.
+
+        File file = new File(mPluginsPath).getParentFile();
+        if (file == null) {
+            return;
+        }
+        // Build the Gears database file name.
+        file = new File(file.getAbsolutePath() + File.separator 
+                + GEARS_DATABASE_DIR + File.separator + GEARS_DATABASE_FILE);
+        // Remember whether or not we need to create the LocationAccess table.
+        boolean needToCreateTables = !file.exists();
+        // Try opening the Gears database.
+        SQLiteDatabase permissions;
+        try {
+            permissions = SQLiteDatabase.openOrCreateDatabase(file, null);
+        } catch (SQLiteException e) {
+            if (LOGV_ENABLED) {
+                Log.v(LOGTAG, "Could not open Gears permission DB: " +
+                        e.getMessage());
+            }
+            // Just bail out.
+            return;
+        }
+        // We now have a database open. Begin a transaction.
+        permissions.beginTransaction();
+        try {
+            if (needToCreateTables) {
+                // Create the tables. Note that this creates the
+                // Gears tables for the permissions DB schema version 2.
+                // The Gears schema upgrade process will take care of the rest.
+                // First, the storage access table.
+                SQLiteStatement statement = permissions.compileStatement(
+                        "CREATE TABLE IF NOT EXISTS " +
+                        GEARS_STORAGE_ACCESS_TABLE_NAME +
+                        " (Name TEXT UNIQUE, Value)");
+                statement.execute();
+                // Next the location access table.
+                statement = permissions.compileStatement(
+                        "CREATE TABLE IF NOT EXISTS " +
+                        GEARS_LOCATION_ACCESS_TABLE_NAME +
+                        " (Name TEXT UNIQUE, Value)");
+                statement.execute();
+                // Finally, the schema version table.
+                statement = permissions.compileStatement(
+                        "CREATE TABLE IF NOT EXISTS " +
+                        GEARS_SCHEMA_VERSION_TABLE_NAME +
+                        " (Name TEXT UNIQUE, Value)");
+                statement.execute();
+                // Set the schema version to 2.
+                ContentValues schema = new ContentValues();
+                schema.put("Name", "Version");
+                schema.put("Value", 2);
+                permissions.insert(GEARS_SCHEMA_VERSION_TABLE_NAME, null,
+                        schema);
+            }
+
+            ContentValues permissionValues = new ContentValues();
+
+            for (String url : sGearsWhiteList) {
+                permissionValues.put("Name", url);
+                permissionValues.put("Value", gearsPermission);
+                permissions.replace(GEARS_LOCATION_ACCESS_TABLE_NAME, null,
+                        permissionValues);
+                permissionValues.clear();
+            }
+            // Commit the transaction.
+            permissions.setTransactionSuccessful();
+        } catch (SQLiteException e) {
+            if (LOGV_ENABLED) {
+                Log.v(LOGTAG, "Could not set the Gears permissions: " +
+                        e.getMessage());
+            }
+        } finally {
+            permissions.endTransaction();
+            permissions.close();
+        }
+    }
     /* Post a SYNC message to handle syncing the native settings. */
     private synchronized void postSync() {
         // Only post if a sync is not pending
diff --git a/core/jni/android_backup_BackupDataOutput.cpp b/core/jni/android_backup_BackupDataOutput.cpp
index d02590e..ce30aaa8 100644
--- a/core/jni/android_backup_BackupDataOutput.cpp
+++ b/core/jni/android_backup_BackupDataOutput.cpp
@@ -70,7 +70,7 @@
     int err;
     BackupDataWriter* writer = (BackupDataWriter*)w;
 
-    if (env->GetArrayLength(data) > size) {
+    if (env->GetArrayLength(data) < size) {
         // size mismatch
         return -1;
     }
diff --git a/core/jni/android_net_wifi_Wifi.cpp b/core/jni/android_net_wifi_Wifi.cpp
index 9f93e2f..ae744a8 100644
--- a/core/jni/android_net_wifi_Wifi.cpp
+++ b/core/jni/android_net_wifi_Wifi.cpp
@@ -27,6 +27,8 @@
 
 namespace android {
 
+static jboolean sScanModeActive = false;
+
 /*
  * The following remembers the jfieldID's of the fields
  * of the DhcpInfo Java object, so that we don't have
@@ -254,27 +256,29 @@
     return doBooleanCommand("REASSOCIATE", "OK");
 }
 
-static jboolean android_net_wifi_scanCommand(JNIEnv* env, jobject clazz)
+static jboolean doSetScanMode(jboolean setActive)
+{
+    return doBooleanCommand((setActive ? "DRIVER SCAN-ACTIVE" : "DRIVER SCAN-PASSIVE"), "OK");
+}
+
+static jboolean android_net_wifi_scanCommand(JNIEnv* env, jobject clazz, jboolean forceActive)
 {
     jboolean result;
+
     // Ignore any error from setting the scan mode.
     // The scan will still work.
-    (void)doBooleanCommand("DRIVER SCAN-ACTIVE", "OK");
+    if (forceActive && !sScanModeActive)
+        doSetScanMode(true);
     result = doBooleanCommand("SCAN", "OK");
-    (void)doBooleanCommand("DRIVER SCAN-PASSIVE", "OK");
+    if (forceActive && !sScanModeActive)
+        doSetScanMode(sScanModeActive);
     return result;
 }
 
 static jboolean android_net_wifi_setScanModeCommand(JNIEnv* env, jobject clazz, jboolean setActive)
 {
-    jboolean result;
-    // Ignore any error from setting the scan mode.
-    // The scan will still work.
-    if (setActive) {
-        return doBooleanCommand("DRIVER SCAN-ACTIVE", "OK");
-    } else {
-        return doBooleanCommand("DRIVER SCAN-PASSIVE", "OK");
-    }
+    sScanModeActive = setActive;
+    return doSetScanMode(setActive);
 }
 
 static jboolean android_net_wifi_startDriverCommand(JNIEnv* env, jobject clazz)
@@ -509,7 +513,7 @@
     { "disconnectCommand", "()Z",  (void *)android_net_wifi_disconnectCommand },
     { "reconnectCommand", "()Z",  (void *)android_net_wifi_reconnectCommand },
     { "reassociateCommand", "()Z",  (void *)android_net_wifi_reassociateCommand },
-    { "scanCommand", "()Z", (void*) android_net_wifi_scanCommand },
+    { "scanCommand", "(Z)Z", (void*) android_net_wifi_scanCommand },
     { "setScanModeCommand", "(Z)Z", (void*) android_net_wifi_setScanModeCommand },
     { "startDriverCommand", "()Z", (void*) android_net_wifi_startDriverCommand },
     { "stopDriverCommand", "()Z", (void*) android_net_wifi_stopDriverCommand },
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 599360f..23967f4 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -995,6 +995,29 @@
         android:description="@string/permdesc_changeBackgroundDataSetting"
         android:label="@string/permlab_changeBackgroundDataSetting" />
 
+    <!-- This permission can be used on content providers to allow the global
+         search system to access their data.  Typically it used when the
+         provider has some permissions protecting it (which global search
+         would not be expected to hold), and added as a read-only permission
+         to the path in the provider where global search queries are
+         performed.  This permission can not be held by regular applications;
+         it is used by applications to protect themselves from everyone else
+         besides global search. -->
+    <permission android:name="android.permission.GLOBAL_SEARCH"
+        android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
+        android:protectionLevel="signatureOrSystem" />
+
+    <!-- Internal permission protecting access to the global search
+         system: ensures that only the system can access the provider
+         to perform queries (since this otherwise provides unrestricted
+         access to a variety of content providers), and to write the
+         search statistics (to keep applications from gaming the source
+         ranking).
+         @hide -->
+    <permission android:name="android.permission.GLOBAL_SEARCH_CONTROL"
+        android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
+        android:protectionLevel="signature" />
+
     <application android:process="system"
                  android:persistent="true"
                  android:hasCode="false"
diff --git a/core/res/res/color/search_url_text.xml b/core/res/res/color/search_url_text.xml
new file mode 100644
index 0000000..449fdf0
--- /dev/null
+++ b/core/res/res/color/search_url_text.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true" android:color="@android:color/search_url_text_pressed"/>
+    <item android:state_selected="true" android:color="@android:color/search_url_text_selected"/>
+    <item android:color="@android:color/search_url_text_normal"/> <!-- not selected -->
+</selector>
diff --git a/core/res/res/values-no-rNO/arrays.xml b/core/res/res/values-nb-rNO/arrays.xml
similarity index 100%
rename from core/res/res/values-no-rNO/arrays.xml
rename to core/res/res/values-nb-rNO/arrays.xml
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 7571e24..12a76ba 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -953,6 +953,20 @@
         <attr name="pathPattern" format="string" />
     </declare-styleable>
     
+    <!-- Attributes that can be supplied in an AndroidManifest.xml
+         <code>path-permission</code> tag, a child of the
+         {@link #AndroidManifestProvider provider} tag, describing a permission
+         that allows access to a specific path in the provider.  This tag can be
+         specified multiple time to supply multiple paths. -->
+    <declare-styleable name="AndroidManifestPathPermission"  parent="AndroidManifestProvider">
+        <attr name="path" />
+        <attr name="pathPrefix" />
+        <attr name="pathPattern" />
+        <attr name="permission" />
+        <attr name="readPermission" />
+        <attr name="writePermission" />
+    </declare-styleable>
+    
     <!-- The <code>service</code> tag declares a
          {@link android.app.Service} class that is available
          as part of the package's application components, implementing
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index d284d0f..b7de997 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -74,7 +74,9 @@
     <color name="perms_normal_perm_color">#c0c0c0</color>
     
     <!-- For search-related UIs -->
-    <color name="search_url_text">#7fa87f</color>
+    <color name="search_url_text_normal">#7fa87f</color>
+    <color name="search_url_text_selected">@android:color/black</color>
+    <color name="search_url_text_pressed">@android:color/black</color>
     <color name="search_widget_corpus_item_background">@android:color/lighter_gray</color>
 
 </resources>
diff --git a/include/utils/String8.h b/include/utils/String8.h
index c49faf6..ecc5774 100644
--- a/include/utils/String8.h
+++ b/include/utils/String8.h
@@ -29,11 +29,107 @@
 
 // ---------------------------------------------------------------------------
 
+extern "C" {
+
+typedef uint32_t char32_t;
+
+size_t strlen32(const char32_t *);
+size_t strnlen32(const char32_t *, size_t);
+
+/*
+ * Returns the length of "src" when "src" is valid UTF-8 string.
+ * Returns 0 if src is NULL, 0-length string or non UTF-8 string.
+ * This function should be used to determine whether "src" is valid UTF-8
+ * characters with valid unicode codepoints. "src" must be null-terminated.
+ *
+ * If you are going to use other GetUtf... functions defined in this header
+ * with string which may not be valid UTF-8 with valid codepoint (form 0 to
+ * 0x10FFFF), you should use this function before calling others, since the
+ * other functions do not check whether the string is valid UTF-8 or not.
+ *
+ * If you do not care whether "src" is valid UTF-8 or not, you should use
+ * strlen() as usual, which should be much faster.
+ */
+size_t utf8_length(const char *src);
+
+/*
+ * Returns the UTF-32 length of "src".
+ */
+size_t utf32_length(const char *src, size_t src_len);
+
+/*
+ * Returns the UTF-8 length of "src".
+ */
+size_t utf8_length_from_utf32(const char32_t *src, size_t src_len);
+
+/*
+ * Returns the unicode value at "index".
+ * Returns -1 when the index is invalid (equals to or more than "src_len").
+ * If returned value is positive, it is able to be converted to char32_t, which
+ * is unsigned. Then, if "next_index" is not NULL, the next index to be used is
+ * stored in "next_index". "next_index" can be NULL.
+ */
+int32_t utf32_at(const char *src, size_t src_len,
+                 size_t index, size_t *next_index);
+
+/*
+ * Stores a UTF-32 string converted from "src" in "dst", if "dst_length" is not
+ * large enough to store the string, the part of the "src" string is stored
+ * into "dst".
+ * Returns the size actually used for storing the string.
+ * "dst" is not null-terminated when dst_len is fully used (like strncpy).
+ */
+size_t utf8_to_utf32(const char* src, size_t src_len,
+                     char32_t* dst, size_t dst_len);
+
+/*
+ * Stores a UTF-8 string converted from "src" in "dst", if "dst_length" is not
+ * large enough to store the string, the part of the "src" string is stored
+ * into "dst" as much as possible. See the examples for more detail.
+ * Returns the size actually used for storing the string.
+ * dst" is not null-terminated when dst_len is fully used (like strncpy).
+ *
+ * Example 1
+ * "src" == \u3042\u3044 (\xE3\x81\x82\xE3\x81\x84)
+ * "src_len" == 2
+ * "dst_len" >= 7
+ * ->
+ * Returned value == 6
+ * "dst" becomes \xE3\x81\x82\xE3\x81\x84\0
+ * (note that "dst" is null-terminated)
+ *
+ * Example 2
+ * "src" == \u3042\u3044 (\xE3\x81\x82\xE3\x81\x84)
+ * "src_len" == 2
+ * "dst_len" == 5
+ * ->
+ * Returned value == 3
+ * "dst" becomes \xE3\x81\x82\0
+ * (note that "dst" is null-terminated, but \u3044 is not stored in "dst"
+ * since "dst" does not have enough size to store the character)
+ *
+ * Example 3
+ * "src" == \u3042\u3044 (\xE3\x81\x82\xE3\x81\x84)
+ * "src_len" == 2
+ * "dst_len" == 6
+ * ->
+ * Returned value == 6
+ * "dst" becomes \xE3\x81\x82\xE3\x81\x84
+ * (note that "dst" is NOT null-terminated, like strncpy)
+ */
+size_t utf32_to_utf8(const char32_t* src, size_t src_len,
+                     char* dst, size_t dst_len);
+
+}
+
+// ---------------------------------------------------------------------------
+
 namespace android {
 
 class TextOutput;
 
-//! This is a string holding UTF-8 characters.
+//! This is a string holding UTF-8 characters. Does not allow the value more
+// than 0x10FFFF, which is not valid unicode codepoint.
 class String8
 {
 public:
@@ -45,7 +141,8 @@
     explicit                    String8(const String16& o);
     explicit                    String8(const char16_t* o);
     explicit                    String8(const char16_t* o, size_t numChars);
-    
+    explicit                    String8(const char32_t* o);
+    explicit                    String8(const char32_t* o, size_t numChars);
                                 ~String8();
     
     inline  const char*         string() const;
@@ -59,11 +156,20 @@
             status_t            setTo(const char* other);
             status_t            setTo(const char* other, size_t numChars);
             status_t            setTo(const char16_t* other, size_t numChars);
-    
+            status_t            setTo(const char32_t* other,
+                                      size_t length);
+
             status_t            append(const String8& other);
             status_t            append(const char* other);
             status_t            append(const char* other, size_t numChars);
 
+            // Note that this function takes O(N) time to calculate the value.
+            // No cache value is stored.
+            size_t              getUtf32Length() const;
+            int32_t             getUtf32At(size_t index,
+                                           size_t *next_index) const;
+            size_t              getUtf32(char32_t* dst, size_t dst_len) const;
+
     inline  String8&            operator=(const String8& other);
     inline  String8&            operator=(const char* other);
     
@@ -103,7 +209,7 @@
             void                toLower(size_t start, size_t numChars);
             void                toUpper();
             void                toUpper(size_t start, size_t numChars);
-            
+
     /*
      * These methods operate on the string as if it were a path name.
      */
@@ -346,7 +452,7 @@
     return mString;
 }
 
-}; // namespace android
+}  // namespace android
 
 // ---------------------------------------------------------------------------
 
diff --git a/libs/audioflinger/AudioFlinger.cpp b/libs/audioflinger/AudioFlinger.cpp
index 8a19fbd..f5bdeda 100644
--- a/libs/audioflinger/AudioFlinger.cpp
+++ b/libs/audioflinger/AudioFlinger.cpp
@@ -738,12 +738,13 @@
 
 bool AudioFlinger::isMusicActive() const
 {
+    Mutex::Autolock _l(mLock);
  #ifdef WITH_A2DP
      if (isA2dpEnabled()) {
-         return mA2dpMixerThread->isMusicActive();
+         return mA2dpMixerThread->isMusicActive_l();
      }
  #endif
-    return mHardwareMixerThread->isMusicActive();
+    return mHardwareMixerThread->isMusicActive_l();
 }
 
 status_t AudioFlinger::setParameter(const char* key, const char* value)
@@ -1444,7 +1445,8 @@
     return mStreamTypes[stream].mute;
 }
 
-bool AudioFlinger::MixerThread::isMusicActive() const
+// isMusicActive_l() must be called with AudioFlinger::mLock held
+bool AudioFlinger::MixerThread::isMusicActive_l() const
 {
     size_t count = mActiveTracks.size();
     for (size_t i = 0 ; i < count ; ++i) {
@@ -2030,7 +2032,10 @@
     inBuffer.i16 = data;
     
     if (mCblk->user == 0) {
-        if (mOutputMixerThread->isMusicActive()) {
+        mOutputMixerThread->mAudioFlinger->mLock.lock();
+        bool isMusicActive = mOutputMixerThread->isMusicActive_l();
+        mOutputMixerThread->mAudioFlinger->mLock.unlock();
+        if (isMusicActive) {
             mCblk->forceReady = 1;
             LOGV("OutputTrack::start() force ready");
         } else if (mCblk->frameCount > frames){
diff --git a/libs/audioflinger/AudioFlinger.h b/libs/audioflinger/AudioFlinger.h
index 8e47b29..634934e 100644
--- a/libs/audioflinger/AudioFlinger.h
+++ b/libs/audioflinger/AudioFlinger.h
@@ -463,7 +463,7 @@
         virtual     float       streamVolume(int stream) const;
         virtual     bool        streamMute(int stream) const;
 
-                    bool        isMusicActive() const;
+                    bool        isMusicActive_l() const;
         
                     
                     sp<Track>   createTrack_l(
diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp
index 109f28d..87edb01 100644
--- a/libs/utils/ResourceTypes.cpp
+++ b/libs/utils/ResourceTypes.cpp
@@ -1573,7 +1573,6 @@
 status_t ResTable::add(ResTable* src)
 {
     mError = src->mError;
-    mParams = src->mParams;
     
     for (size_t i=0; i<src->mHeaders.size(); i++) {
         mHeaders.add(src->mHeaders[i]);
diff --git a/libs/utils/String8.cpp b/libs/utils/String8.cpp
index c50d343..e908ec1 100644
--- a/libs/utils/String8.cpp
+++ b/libs/utils/String8.cpp
@@ -25,25 +25,39 @@
 
 #include <ctype.h>
 
-namespace android {
+/*
+ * Functions outside android is below the namespace android, since they use
+ * functions and constants in android namespace.
+ */
 
 // ---------------------------------------------------------------------------
 
-static const uint32_t kByteMask = 0x000000BF;
-static const uint32_t kByteMark = 0x00000080;
+namespace android {
+
+static const char32_t kByteMask = 0x000000BF;
+static const char32_t kByteMark = 0x00000080;
 
 // Surrogates aren't valid for UTF-32 characters, so define some
 // constants that will let us screen them out.
-static const uint32_t kUnicodeSurrogateHighStart  = 0x0000D800;
-static const uint32_t kUnicodeSurrogateHighEnd    = 0x0000DBFF;
-static const uint32_t kUnicodeSurrogateLowStart   = 0x0000DC00;
-static const uint32_t kUnicodeSurrogateLowEnd     = 0x0000DFFF;
-static const uint32_t kUnicodeSurrogateStart      = kUnicodeSurrogateHighStart;
-static const uint32_t kUnicodeSurrogateEnd        = kUnicodeSurrogateLowEnd;
+static const char32_t kUnicodeSurrogateHighStart  = 0x0000D800;
+static const char32_t kUnicodeSurrogateHighEnd    = 0x0000DBFF;
+static const char32_t kUnicodeSurrogateLowStart   = 0x0000DC00;
+static const char32_t kUnicodeSurrogateLowEnd     = 0x0000DFFF;
+static const char32_t kUnicodeSurrogateStart      = kUnicodeSurrogateHighStart;
+static const char32_t kUnicodeSurrogateEnd        = kUnicodeSurrogateLowEnd;
+static const char32_t kUnicodeMaxCodepoint        = 0x0010FFFF;
 
 // Mask used to set appropriate bits in first byte of UTF-8 sequence,
 // indexed by number of bytes in the sequence.
-static const uint32_t kFirstByteMark[] = {
+// 0xxxxxxx
+// -> (00-7f) 7bit. Bit mask for the first byte is 0x00000000
+// 110yyyyx 10xxxxxx
+// -> (c0-df)(80-bf) 11bit. Bit mask is 0x000000C0
+// 1110yyyy 10yxxxxx 10xxxxxx
+// -> (e0-ef)(80-bf)(80-bf) 16bit. Bit mask is 0x000000E0
+// 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx
+// -> (f0-f7)(80-bf)(80-bf)(80-bf) 21bit. Bit mask is 0x000000F0
+static const char32_t kFirstByteMark[] = {
     0x00000000, 0x00000000, 0x000000C0, 0x000000E0, 0x000000F0
 };
 
@@ -52,7 +66,7 @@
 #define RES_PATH_SEPARATOR '/'
 
 // Return number of utf8 bytes required for the character.
-static size_t utf32_to_utf8_bytes(uint32_t srcChar)
+static size_t utf32_to_utf8_bytes(char32_t srcChar)
 {
     size_t bytesToWrite;
 
@@ -79,7 +93,7 @@
         }
     }
     // Max code point for Unicode is 0x0010FFFF.
-    else if (srcChar < 0x00110000)
+    else if (srcChar <= kUnicodeMaxCodepoint)
     {
         bytesToWrite = 4;
     }
@@ -94,7 +108,7 @@
 
 // Write out the source character to <dstP>.
 
-static void utf32_to_utf8(uint8_t* dstP, uint32_t srcChar, size_t bytes)
+static void utf32_to_utf8(uint8_t* dstP, char32_t srcChar, size_t bytes)
 {
     dstP += bytes;
     switch (bytes)
@@ -126,7 +140,7 @@
 	  // Bite me, Darwin!
 		gDarwinIsReallyAnnoying = gDarwinCantLoadAllObjects;
 #endif
-			
+
     SharedBuffer* buf = SharedBuffer::alloc(1);
     char* str = (char*)buf->data();
     *str = 0;
@@ -160,20 +174,20 @@
     return getEmptyString();
 }
 
-// Note: not dealing with expanding surrogate pairs.
-static char* allocFromUTF16(const char16_t* in, size_t len)
+template<typename T, typename L>
+static char* allocFromUTF16OrUTF32(const T* in, L len)
 {
     if (len == 0) return getEmptyString();
-    
+
     size_t bytes = 0;
-    const char16_t* end = in+len;
-    const char16_t* p = in;
-    
+    const T* end = in+len;
+    const T* p = in;
+
     while (p < end) {
         bytes += utf32_to_utf8_bytes(*p);
         p++;
     }
-    
+
     SharedBuffer* buf = SharedBuffer::alloc(bytes+1);
     LOG_ASSERT(buf, "Unable to allocate shared buffer");
     if (buf) {
@@ -181,19 +195,30 @@
         char* str = (char*)buf->data();
         char* d = str;
         while (p < end) {
-            uint32_t c = *p++;
+            const T c = *p++;
             size_t len = utf32_to_utf8_bytes(c);
             utf32_to_utf8((uint8_t*)d, c, len);
             d += len;
         }
         *d = 0;
-        
+
         return str;
     }
-    
+
     return getEmptyString();
 }
 
+// Note: not dealing with expanding surrogate pairs.
+static char* allocFromUTF16(const char16_t* in, size_t len)
+{
+    return allocFromUTF16OrUTF32<char16_t, size_t>(in, len);
+}
+
+static char* allocFromUTF32(const char32_t* in, size_t len)
+{
+    return allocFromUTF16OrUTF32<char32_t, size_t>(in, len);
+}
+
 // ---------------------------------------------------------------------------
 
 String8::String8()
@@ -238,6 +263,16 @@
 {
 }
 
+String8::String8(const char32_t* o)
+    : mString(allocFromUTF32(o, strlen32(o)))
+{
+}
+
+String8::String8(const char32_t* o, size_t len)
+    : mString(allocFromUTF32(o, len))
+{
+}
+
 String8::~String8()
 {
     SharedBuffer::bufferFromData(mString)->release();
@@ -280,6 +315,16 @@
     return NO_MEMORY;
 }
 
+status_t String8::setTo(const char32_t* other, size_t len)
+{
+    SharedBuffer::bufferFromData(mString)->release();
+    mString = allocFromUTF32(other, len);
+    if (mString) return NO_ERROR;
+
+    mString = getEmptyString();
+    return NO_MEMORY;
+}
+
 status_t String8::append(const String8& other)
 {
     const size_t otherLen = other.bytes();
@@ -418,6 +463,21 @@
     unlockBuffer(len);
 }
 
+size_t String8::getUtf32Length() const
+{
+    return utf32_length(mString, length());
+}
+
+int32_t String8::getUtf32At(size_t index, size_t *next_index) const
+{
+    return utf32_at(mString, length(), index, next_index);
+}
+
+size_t String8::getUtf32(char32_t* dst, size_t dst_len) const
+{
+    return utf8_to_utf32(mString, length(), dst, dst_len);
+}
+
 TextOutput& operator<<(TextOutput& to, const String8& val)
 {
     to << val.string();
@@ -427,7 +487,6 @@
 // ---------------------------------------------------------------------------
 // Path functions
 
-
 void String8::setPathName(const char* name)
 {
     setPathName(name, strlen(name));
@@ -600,5 +659,192 @@
     return *this;
 }
 
-
 }; // namespace android
+
+// ---------------------------------------------------------------------------
+
+size_t strlen32(const char32_t *s)
+{
+  const char32_t *ss = s;
+  while ( *ss )
+    ss++;
+  return ss-s;
+}
+
+size_t strnlen32(const char32_t *s, size_t maxlen)
+{
+  const char32_t *ss = s;
+  while ((maxlen > 0) && *ss) {
+    ss++;
+    maxlen--;
+  }
+  return ss-s;
+}
+
+size_t utf8_length(const char *src)
+{
+    const char *cur = src;
+    size_t ret = 0;
+    while (*cur != '\0') {
+        const char first_char = *cur++;
+        if ((first_char & 0x80) == 0) { // ASCII
+            ret += 1;
+            continue;
+        }
+        // (UTF-8's character must not be like 10xxxxxx,
+        //  but 110xxxxx, 1110xxxx, ... or 1111110x)
+        if ((first_char & 0x40) == 0) {
+            return 0;
+        }
+
+        int32_t mask, to_ignore_mask;
+        size_t num_to_read = 0;
+        char32_t utf32 = 0;
+        for (num_to_read = 1, mask = 0x40, to_ignore_mask = 0x80;
+             num_to_read < 5 && (first_char & mask);
+             num_to_read++, to_ignore_mask |= mask, mask >>= 1) {
+            if ((*cur & 0xC0) != 0x80) { // must be 10xxxxxx
+                return 0;
+            }
+            // 0x3F == 00111111
+            utf32 = (utf32 << 6) + (*cur++ & 0x3F);
+        }
+        // "first_char" must be (110xxxxx - 11110xxx)
+        if (num_to_read == 5) {
+            return 0;
+        }
+        to_ignore_mask |= mask;
+        utf32 |= ((~to_ignore_mask) & first_char) << (6 * (num_to_read - 1));
+        if (utf32 > android::kUnicodeMaxCodepoint) {
+            return 0;
+        }
+
+        ret += num_to_read;
+    }
+    return ret;
+}
+
+size_t utf32_length(const char *src, size_t src_len)
+{
+    if (src == NULL || src_len == 0) {
+        return 0;
+    }
+    size_t ret = 0;
+    const char* cur;
+    const char* end;
+    size_t num_to_skip;
+    for (cur = src, end = src + src_len, num_to_skip = 1;
+         cur < end;
+         cur += num_to_skip, ret++) {
+        const char first_char = *cur;
+        num_to_skip = 1;
+        if ((first_char & 0x80) == 0) {  // ASCII
+            continue;
+        }
+        int32_t mask;
+
+        for (mask = 0x40; (first_char & mask); num_to_skip++, mask >>= 1) {
+        }
+    }
+    return ret;
+}
+
+size_t utf8_length_from_utf32(const char32_t *src, size_t src_len)
+{
+    if (src == NULL || src_len == 0) {
+        return 0;
+    }
+    size_t ret = 0;
+    const char32_t *end = src + src_len;
+    while (src < end) {
+        ret += android::utf32_to_utf8_bytes(*src++);
+    }
+    return ret;
+}
+
+static int32_t utf32_at_internal(const char* cur, size_t *num_read)
+{
+    const char first_char = *cur;
+    if ((first_char & 0x80) == 0) { // ASCII
+        *num_read = 1;
+        return *cur;
+    }
+    cur++;
+    char32_t mask, to_ignore_mask;
+    size_t num_to_read = 0;
+    char32_t utf32 = first_char;
+    for (num_to_read = 1, mask = 0x40, to_ignore_mask = 0xFFFFFF80;
+         (first_char & mask);
+         num_to_read++, to_ignore_mask |= mask, mask >>= 1) {
+        // 0x3F == 00111111
+        utf32 = (utf32 << 6) + (*cur++ & 0x3F);
+    }
+    to_ignore_mask |= mask;
+    utf32 &= ~(to_ignore_mask << (6 * (num_to_read - 1)));
+
+    *num_read = num_to_read;
+    return static_cast<int32_t>(utf32);
+}
+
+int32_t utf32_at(const char *src, size_t src_len,
+                 size_t index, size_t *next_index)
+{
+    if (index >= src_len) {
+        return -1;
+    }
+    size_t dummy_index;
+    if (next_index == NULL) {
+        next_index = &dummy_index;
+    }
+    size_t num_read;
+    int32_t ret = utf32_at_internal(src + index, &num_read);
+    if (ret >= 0) {
+        *next_index = index + num_read;
+    }
+
+    return ret;
+}
+
+size_t utf8_to_utf32(const char* src, size_t src_len,
+                     char32_t* dst, size_t dst_len)
+{
+    if (src == NULL || src_len == 0 || dst == NULL || dst_len == 0) {
+        return 0;
+    }
+
+    const char* cur = src;
+    const char* end = src + src_len;
+    char32_t* cur_utf32 = dst;
+    const char32_t* end_utf32 = dst + dst_len;
+    while (cur_utf32 < end_utf32 && cur < end) {
+        size_t num_read;
+        *cur_utf32++ =
+                static_cast<char32_t>(utf32_at_internal(cur, &num_read));
+        cur += num_read;
+    }
+    if (cur_utf32 < end_utf32) {
+        *cur_utf32 = 0;
+    }
+    return static_cast<size_t>(cur_utf32 - dst);
+}
+
+size_t utf32_to_utf8(const char32_t* src, size_t src_len,
+                     char* dst, size_t dst_len)
+{
+    if (src == NULL || src_len == 0 || dst == NULL || dst_len == 0) {
+        return 0;
+    }
+    const char32_t *cur_utf32 = src;
+    const char32_t *end_utf32 = src + src_len;
+    char *cur = dst;
+    const char *end = dst + dst_len;
+    while (cur_utf32 < end_utf32 && cur < end) {
+        size_t len = android::utf32_to_utf8_bytes(*cur_utf32);
+        android::utf32_to_utf8((uint8_t *)cur, *cur_utf32++, len);
+        cur += len;
+    }
+    if (cur < end) {
+        *cur = '\0';
+    }
+    return cur - dst;
+}
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
new file mode 100644
index 0000000..645f3f6
--- /dev/null
+++ b/media/java/android/media/ExifInterface.java
@@ -0,0 +1,398 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.util.Log;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Wrapper for native Exif library
+ * {@hide}
+ */
+public class ExifInterface {
+    private static final String TAG = "ExifInterface";
+    private String mFilename;
+
+    // Constants used for the Orientation Exif tag.
+    public static final int ORIENTATION_UNDEFINED = 0;
+    public static final int ORIENTATION_NORMAL = 1;
+
+    // Constants used for white balance
+    public static final int WHITEBALANCE_AUTO = 0;
+    public static final int WHITEBALANCE_MANUAL = 1;
+
+    // left right reversed mirror
+    public static final int ORIENTATION_FLIP_HORIZONTAL = 2;
+    public static final int ORIENTATION_ROTATE_180 = 3;
+
+    // upside down mirror
+    public static final int ORIENTATION_FLIP_VERTICAL = 4;
+
+    // flipped about top-left <--> bottom-right axis
+    public static final int ORIENTATION_TRANSPOSE = 5;
+
+    // rotate 90 cw to right it
+    public static final int ORIENTATION_ROTATE_90 = 6;
+
+    // flipped about top-right <--> bottom-left axis
+    public static final int ORIENTATION_TRANSVERSE = 7;
+
+    // rotate 270 to right it
+    public static final int ORIENTATION_ROTATE_270 = 8;
+
+    // The Exif tag names
+    public static final String TAG_ORIENTATION = "Orientation";
+
+    public static final String TAG_DATE_TIME_ORIGINAL = "DateTimeOriginal";
+    public static final String TAG_MAKE = "Make";
+    public static final String TAG_MODEL = "Model";
+    public static final String TAG_FLASH = "Flash";
+    public static final String TAG_IMAGE_WIDTH = "ImageWidth";
+    public static final String TAG_IMAGE_LENGTH = "ImageLength";
+
+    public static final String TAG_GPS_LATITUDE = "GPSLatitude";
+    public static final String TAG_GPS_LONGITUDE = "GPSLongitude";
+
+    public static final String TAG_GPS_LATITUDE_REF = "GPSLatitudeRef";
+    public static final String TAG_GPS_LONGITUDE_REF = "GPSLongitudeRef";
+    public static final String TAG_WHITE_BALANCE = "WhiteBalance";
+
+    private boolean mSavedAttributes = false;
+    private boolean mHasThumbnail = false;
+    private HashMap<String, String> mCachedAttributes = null;
+
+    static {
+        System.loadLibrary("exif");
+    }
+
+    private static ExifInterface sExifObj = null;
+    /**
+     * Since the underlying jhead native code is not thread-safe,
+     * ExifInterface should use singleton interface instead of public
+     * constructor.
+     */
+    private static synchronized ExifInterface instance() {
+        if (sExifObj == null) {
+            sExifObj = new ExifInterface();
+        }
+
+        return sExifObj;
+    }
+
+    /**
+     * The following 3 static methods are handy routines for atomic operation
+     * of underlying jhead library. It retrieves EXIF data and then release
+     * ExifInterface immediately.
+     */
+    public static synchronized HashMap<String, String> loadExifData(String filename) {
+        ExifInterface exif = instance();
+        HashMap<String, String> exifData = null;
+        if (exif != null) {
+            exif.setFilename(filename);
+            exifData = exif.getAttributes();
+        }
+        return exifData;
+    }
+
+    public static synchronized void saveExifData(String filename, HashMap<String, String> exifData) {
+        ExifInterface exif = instance();
+        if (exif != null) {
+            exif.setFilename(filename);
+            exif.saveAttributes(exifData);
+        }
+    }
+
+    public static synchronized byte[] getExifThumbnail(String filename) {
+        ExifInterface exif = instance();
+        if (exif != null) {
+            exif.setFilename(filename);
+            return exif.getThumbnail();
+        }
+        return null;
+    }
+
+    public void setFilename(String filename) {
+        mFilename = filename;
+    }
+
+    /**
+     * Given a HashMap of Exif tags and associated values, an Exif section in
+     * the JPG file is created and loaded with the tag data. saveAttributes()
+     * is expensive because it involves copying all the JPG data from one file
+     * to another and deleting the old file and renaming the other. It's best
+     * to collect all the attributes to write and make a single call rather
+     * than multiple calls for each attribute. You must call "commitChanges()"
+     * at some point to commit the changes.
+     */
+    public void saveAttributes(HashMap<String, String> attributes) {
+        // format of string passed to native C code:
+        // "attrCnt attr1=valueLen value1attr2=value2Len value2..."
+        // example:
+        // "4 attrPtr ImageLength=4 1024Model=6 FooImageWidth=4 1280Make=3 FOO"
+        StringBuilder sb = new StringBuilder();
+        int size = attributes.size();
+        if (attributes.containsKey("hasThumbnail")) {
+            --size;
+        }
+        sb.append(size + " ");
+        for (Map.Entry<String, String> iter : attributes.entrySet()) {
+            String key = iter.getKey();
+            if (key.equals("hasThumbnail")) {
+                // this is a fake attribute not saved as an exif tag
+                continue;
+            }
+            String val = iter.getValue();
+            sb.append(key + "=");
+            sb.append(val.length() + " ");
+            sb.append(val);
+        }
+        String s = sb.toString();
+        saveAttributesNative(mFilename, s);
+        commitChangesNative(mFilename);
+        mSavedAttributes = true;
+    }
+
+    /**
+     * Returns a HashMap loaded with the Exif attributes of the file. The key
+     * is the standard tag name and the value is the tag's value: e.g.
+     * Model -> Nikon. Numeric values are returned as strings.
+     */
+    public HashMap<String, String> getAttributes() {
+        if (mCachedAttributes != null) {
+            return mCachedAttributes;
+        }
+        // format of string passed from native C code:
+        // "attrCnt attr1=valueLen value1attr2=value2Len value2..."
+        // example:
+        // "4 attrPtr ImageLength=4 1024Model=6 FooImageWidth=4 1280Make=3 FOO"
+        mCachedAttributes = new HashMap<String, String>();
+
+        String attrStr = getAttributesNative(mFilename);
+
+        // get count
+        int ptr = attrStr.indexOf(' ');
+        int count = Integer.parseInt(attrStr.substring(0, ptr));
+        // skip past the space between item count and the rest of the attributes
+        ++ptr;
+
+        for (int i = 0; i < count; i++) {
+            // extract the attribute name
+            int equalPos = attrStr.indexOf('=', ptr);
+            String attrName = attrStr.substring(ptr, equalPos);
+            ptr = equalPos + 1;     // skip past =
+
+            // extract the attribute value length
+            int lenPos = attrStr.indexOf(' ', ptr);
+            int attrLen = Integer.parseInt(attrStr.substring(ptr, lenPos));
+            ptr = lenPos + 1;       // skip pas the space
+
+            // extract the attribute value
+            String attrValue = attrStr.substring(ptr, ptr + attrLen);
+            ptr += attrLen;
+
+            if (attrName.equals("hasThumbnail")) {
+                mHasThumbnail = attrValue.equalsIgnoreCase("true");
+            } else {
+                mCachedAttributes.put(attrName, attrValue);
+            }
+        }
+        return mCachedAttributes;
+    }
+
+    /**
+     * Given a numerical white balance value, return a
+     * human-readable string describing it.
+     */
+    public static String whiteBalanceToString(int whitebalance) {
+        switch (whitebalance) {
+            case WHITEBALANCE_AUTO:
+                return "Auto";
+            case WHITEBALANCE_MANUAL:
+                return "Manual";
+            default:
+                return "";
+        }
+    }
+
+    /**
+     * Given a numerical orientation, return a human-readable string describing
+     * the orientation.
+     */
+    public static String orientationToString(int orientation) {
+        // TODO: this function needs to be localized and use string resource ids
+        // rather than strings
+        String orientationString;
+        switch (orientation) {
+            case ORIENTATION_NORMAL:
+                orientationString = "Normal";
+                break;
+            case ORIENTATION_FLIP_HORIZONTAL:
+                orientationString = "Flipped horizontal";
+                break;
+            case ORIENTATION_ROTATE_180:
+                orientationString = "Rotated 180 degrees";
+                break;
+            case ORIENTATION_FLIP_VERTICAL:
+                orientationString = "Upside down mirror";
+                break;
+            case ORIENTATION_TRANSPOSE:
+                orientationString = "Transposed";
+                break;
+            case ORIENTATION_ROTATE_90:
+                orientationString = "Rotated 90 degrees";
+                break;
+            case ORIENTATION_TRANSVERSE:
+                orientationString = "Transversed";
+                break;
+            case ORIENTATION_ROTATE_270:
+                orientationString = "Rotated 270 degrees";
+                break;
+            default:
+                orientationString = "Undefined";
+                break;
+        }
+        return orientationString;
+    }
+
+    /**
+     * Copies the thumbnail data out of the filename and puts it in the Exif
+     * data associated with the file used to create this object. You must call
+     * "commitChanges()" at some point to commit the changes.
+     */
+    public boolean appendThumbnail(String thumbnailFileName) {
+        if (!mSavedAttributes) {
+            throw new RuntimeException("Must call saveAttributes "
+                    + "before calling appendThumbnail");
+        }
+        mHasThumbnail = appendThumbnailNative(mFilename, thumbnailFileName);
+        return mHasThumbnail;
+    }
+
+    public boolean hasThumbnail() {
+        if (!mSavedAttributes) {
+            getAttributes();
+        }
+        return mHasThumbnail;
+    }
+
+    public byte[] getThumbnail() {
+        return getThumbnailNative(mFilename);
+    }
+
+    public static float[] getLatLng(HashMap<String, String> exifData) {
+        if (exifData == null) {
+            return null;
+        }
+
+        String latValue = exifData.get(ExifInterface.TAG_GPS_LATITUDE);
+        String latRef = exifData.get(ExifInterface.TAG_GPS_LATITUDE_REF);
+        String lngValue = exifData.get(ExifInterface.TAG_GPS_LONGITUDE);
+        String lngRef = exifData.get(ExifInterface.TAG_GPS_LONGITUDE_REF);
+        float[] latlng = null;
+
+        if (latValue != null && latRef != null
+                && lngValue != null && lngRef != null) {
+            latlng = new float[2];
+            latlng[0] = ExifInterface.convertRationalLatLonToFloat(
+                    latValue, latRef);
+            latlng[1] = ExifInterface.convertRationalLatLonToFloat(
+                    lngValue, lngRef);
+        }
+
+        return latlng;
+    }
+
+    public static float convertRationalLatLonToFloat(
+            String rationalString, String ref) {
+        try {
+            String [] parts = rationalString.split(",");
+
+            String [] pair;
+            pair = parts[0].split("/");
+            int degrees = (int) (Float.parseFloat(pair[0].trim())
+                    / Float.parseFloat(pair[1].trim()));
+
+            pair = parts[1].split("/");
+            int minutes = (int) ((Float.parseFloat(pair[0].trim())
+                    / Float.parseFloat(pair[1].trim())));
+
+            pair = parts[2].split("/");
+            float seconds = Float.parseFloat(pair[0].trim())
+                    / Float.parseFloat(pair[1].trim());
+
+            float result = degrees + (minutes / 60F) + (seconds / (60F * 60F));
+            if ((ref.equals("S") || ref.equals("W"))) {
+                return -result;
+            }
+            return result;
+        } catch (RuntimeException ex) {
+            // if for whatever reason we can't parse the lat long then return
+            // null
+            return 0f;
+        }
+    }
+
+    public static String convertRationalLatLonToDecimalString(
+            String rationalString, String ref, boolean usePositiveNegative) {
+            float result = convertRationalLatLonToFloat(rationalString, ref);
+
+            String preliminaryResult = String.valueOf(result);
+            if (usePositiveNegative) {
+                String neg = (ref.equals("S") || ref.equals("E")) ? "-" : "";
+                return neg + preliminaryResult;
+            } else {
+                return preliminaryResult + String.valueOf((char) 186) + " "
+                        + ref;
+            }
+    }
+
+    public static String makeLatLongString(double d) {
+        d = Math.abs(d);
+
+        int degrees = (int) d;
+
+        double remainder = d - degrees;
+        int minutes = (int) (remainder * 60D);
+        // really seconds * 1000
+        int seconds = (int) (((remainder * 60D) - minutes) * 60D * 1000D);
+
+        String retVal = degrees + "/1," + minutes + "/1," + seconds + "/1000";
+        return retVal;
+    }
+
+    public static String makeLatStringRef(double lat) {
+        return lat >= 0D ? "N" : "S";
+    }
+
+    public static String makeLonStringRef(double lon) {
+        return lon >= 0D ? "W" : "E";
+    }
+
+    private native boolean appendThumbnailNative(String fileName,
+            String thumbnailFileName);
+
+    private native void saveAttributesNative(String fileName,
+            String compressedAttributes);
+
+    private native String getAttributesNative(String fileName);
+
+    private native void commitChangesNative(String fileName);
+
+    private native byte[] getThumbnailNative(String fileName);
+}
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index cccc0fc..6de7bc1 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -54,7 +54,7 @@
 
 /**
  * Internal service helper that no-one should use directly.
- * 
+ *
  * The way the scan currently works is:
  * - The Java MediaScannerService creates a MediaScanner (this class), and calls
  *   MediaScanner.scanDirectories on it.
@@ -96,7 +96,7 @@
  * {@hide}
  */
 public class MediaScanner
-{    
+{
     static {
         System.loadLibrary("media_jni");
     }
@@ -108,17 +108,17 @@
             Audio.Media.DATA, // 1
             Audio.Media.DATE_MODIFIED, // 2
     };
-    
+
     private static final int ID_AUDIO_COLUMN_INDEX = 0;
     private static final int PATH_AUDIO_COLUMN_INDEX = 1;
     private static final int DATE_MODIFIED_AUDIO_COLUMN_INDEX = 2;
- 
+
     private static final String[] VIDEO_PROJECTION = new String[] {
             Video.Media._ID, // 0
             Video.Media.DATA, // 1
             Video.Media.DATE_MODIFIED, // 2
     };
-    
+
     private static final int ID_VIDEO_COLUMN_INDEX = 0;
     private static final int PATH_VIDEO_COLUMN_INDEX = 1;
     private static final int DATE_MODIFIED_VIDEO_COLUMN_INDEX = 2;
@@ -128,11 +128,11 @@
             Images.Media.DATA, // 1
             Images.Media.DATE_MODIFIED, // 2
     };
-    
+
     private static final int ID_IMAGES_COLUMN_INDEX = 0;
     private static final int PATH_IMAGES_COLUMN_INDEX = 1;
     private static final int DATE_MODIFIED_IMAGES_COLUMN_INDEX = 2;
-    
+
     private static final String[] PLAYLISTS_PROJECTION = new String[] {
             Audio.Playlists._ID, // 0
             Audio.Playlists.DATA, // 1
@@ -157,7 +157,7 @@
     private static final String ALARMS_DIR = "/alarms/";
     private static final String MUSIC_DIR = "/music/";
     private static final String PODCAST_DIR = "/podcasts/";
-    
+
     private static final String[] ID3_GENRES = {
         // ID3v1 Genres
         "Blues",
@@ -317,11 +317,11 @@
      * to get the full system property.
      */
     private static final String DEFAULT_RINGTONE_PROPERTY_PREFIX = "ro.config.";
-    
+
     // set to true if file path comparisons should be case insensitive.
     // this should be set when scanning files on a case insensitive file system.
     private boolean mCaseInsensitivePaths;
-    
+
     private BitmapFactory.Options mBitmapOptions = new BitmapFactory.Options();
 
     private static class FileCacheEntry {
@@ -331,7 +331,7 @@
         long mLastModified;
         boolean mSeenInFileSystem;
         boolean mLastModifiedChanged;
-        
+
         FileCacheEntry(Uri tableUri, long rowId, String path, long lastModified) {
             mTableUri = tableUri;
             mRowId = rowId;
@@ -346,10 +346,10 @@
             return mPath;
         }
     }
-    
-    // hashes file path to FileCacheEntry.  
+
+    // hashes file path to FileCacheEntry.
     // path should be lower case if mCaseInsensitivePaths is true
-    private HashMap<String, FileCacheEntry> mFileCache; 
+    private HashMap<String, FileCacheEntry> mFileCache;
 
     private ArrayList<FileCacheEntry> mPlayLists;
     private HashMap<String, Uri> mGenreCache;
@@ -360,7 +360,7 @@
         mContext = c;
         mBitmapOptions.inSampleSize = 1;
         mBitmapOptions.inJustDecodeBounds = true;
-        
+
         setDefaultRingtoneFileNames();
     }
 
@@ -370,11 +370,11 @@
         mDefaultNotificationFilename = SystemProperties.get(DEFAULT_RINGTONE_PROPERTY_PREFIX
                 + Settings.System.NOTIFICATION_SOUND);
     }
-    
+
     private MyMediaScannerClient mClient = new MyMediaScannerClient();
-    
+
     private class MyMediaScannerClient implements MediaScannerClient {
-    
+
         private String mArtist;
         private String mAlbumArtist;    // use this if mArtist is missing
         private String mAlbum;
@@ -389,11 +389,11 @@
         private String mPath;
         private long mLastModified;
         private long mFileSize;
-    
+
         public FileCacheEntry beginFile(String path, String mimeType, long lastModified, long fileSize) {
-            
+
             // special case certain file names
-            // I use regionMatches() instead of substring() below 
+            // I use regionMatches() instead of substring() below
             // to avoid memory allocation
             int lastSlash = path.lastIndexOf('/');
             if (lastSlash >= 0 && lastSlash + 2 < path.length()) {
@@ -401,7 +401,7 @@
                 if (path.regionMatches(lastSlash + 1, "._", 0, 2)) {
                     return null;
                 }
-                
+
                 // ignore album art files created by Windows Media Player:
                 // Folder.jpg, AlbumArtSmall.jpg, AlbumArt_{...}_Large.jpg and AlbumArt_{...}_Small.jpg
                 if (path.regionMatches(true, path.length() - 4, ".jpg", 0, 4)) {
@@ -416,7 +416,7 @@
                     }
                 }
             }
-            
+
             mMimeType = null;
             // try mimeType first, if it is specified
             if (mimeType != null) {
@@ -435,7 +435,7 @@
                     mMimeType = mediaFileType.mimeType;
                 }
             }
-            
+
             String key = path;
             if (mCaseInsensitivePaths) {
                 key = path.toLowerCase();
@@ -446,20 +446,20 @@
                 mFileCache.put(key, entry);
             }
             entry.mSeenInFileSystem = true;
-            
+
             // add some slack to avoid a rounding error
             long delta = lastModified - entry.mLastModified;
             if (delta > 1 || delta < -1) {
                 entry.mLastModified = lastModified;
                 entry.mLastModifiedChanged = true;
             }
-                           
+
             if (mProcessPlaylists && MediaFile.isPlayListFileType(mFileType)) {
                 mPlayLists.add(entry);
                 // we don't process playlists in the main scan, so return null
                 return null;
             }
-            
+
             // clear all the metadata
             mArtist = null;
             mAlbumArtist = null;
@@ -472,10 +472,10 @@
             mDuration = 0;
             mPath = path;
             mLastModified = lastModified;
-            
+
             return entry;
         }
-        
+
         public void scanFile(String path, long lastModified, long fileSize) {
             doScanFile(path, null, lastModified, fileSize, false);
         }
@@ -513,7 +513,7 @@
                     } else if (MediaFile.isImageFileType(mFileType)) {
                         // we used to compute the width and height but it's not worth it
                     }
-                    
+
                     result = endFile(entry, ringtones, notifications, alarms, music, podcasts);
                 }
             } catch (RemoteException e) {
@@ -531,17 +531,17 @@
             char ch = s.charAt(start++);
             // return defaultValue if we have no integer at all
             if (ch < '0' || ch > '9') return defaultValue;
-            
+
             int result = ch - '0';
             while (start < length) {
                 ch = s.charAt(start++);
                 if (ch < '0' || ch > '9') return result;
                 result = result * 10 + (ch - '0');
             }
-            
+
             return result;
-        }                                
-                                
+        }
+
         public void handleStringTag(String name, String value) {
             if (name.equalsIgnoreCase("title") || name.startsWith("title;")) {
                 // Don't trim() here, to preserve the special \001 character
@@ -577,7 +577,7 @@
                 // track number might be of the form "2/12"
                 // we just read the number before the slash
                 int num = parseSubstring(value, 0, 0);
-                mTrack = (mTrack / 1000) * 1000 + num; 
+                mTrack = (mTrack / 1000) * 1000 + num;
             } else if (name.equalsIgnoreCase("discnumber") ||
                     name.equals("set") || name.startsWith("set;")) {
                 // set number might be of the form "1/3"
@@ -588,16 +588,16 @@
                 mDuration = parseSubstring(value, 0, 0);
             }
         }
-        
+
         public void setMimeType(String mimeType) {
             mMimeType = mimeType;
             mFileType = MediaFile.getFileTypeForMimeType(mimeType);
         }
-        
+
         /**
          * Formats the data into a values array suitable for use with the Media
          * Content Provider.
-         * 
+         *
          * @return a map of values
          */
         private ContentValues toValues() {
@@ -608,7 +608,7 @@
             map.put(MediaStore.MediaColumns.DATE_MODIFIED, mLastModified);
             map.put(MediaStore.MediaColumns.SIZE, mFileSize);
             map.put(MediaStore.MediaColumns.MIME_TYPE, mMimeType);
-            
+
             if (MediaFile.isVideoFileType(mFileType)) {
                 map.put(Video.Media.ARTIST, (mArtist != null && mArtist.length() > 0 ? mArtist : MediaFile.UNKNOWN_STRING));
                 map.put(Video.Media.ALBUM, (mAlbum != null && mAlbum.length() > 0 ? mAlbum : MediaFile.UNKNOWN_STRING));
@@ -629,9 +629,9 @@
             }
             return map;
         }
-    
+
         private Uri endFile(FileCacheEntry entry, boolean ringtones, boolean notifications,
-                boolean alarms, boolean music, boolean podcasts) 
+                boolean alarms, boolean music, boolean podcasts)
                 throws RemoteException {
             // update database
             Uri tableUri;
@@ -649,7 +649,7 @@
                 return null;
             }
             entry.mTableUri = tableUri;
-            
+
              // use album artist if artist is missing
             if (mArtist == null || mArtist.length() == 0) {
                 mArtist = mAlbumArtist;
@@ -680,10 +680,18 @@
                 values.put(Audio.Media.IS_ALARM, alarms);
                 values.put(Audio.Media.IS_MUSIC, music);
                 values.put(Audio.Media.IS_PODCAST, podcasts);
-            } else if (isImage) {
-                // nothing right now
+            } else if (mFileType == MediaFile.FILE_TYPE_JPEG) {
+                HashMap<String, String> exifData =
+                        ExifInterface.loadExifData(entry.mPath);
+                if (exifData != null) {
+                    float[] latlng = ExifInterface.getLatLng(exifData);
+                    if (latlng != null) {
+                        values.put(Images.Media.LATITUDE, latlng[0]);
+                        values.put(Images.Media.LONGITUDE, latlng[1]);
+                    }
+                }
             }
-            
+
             Uri result = null;
             long rowId = entry.mRowId;
             if (rowId == 0) {
@@ -730,15 +738,15 @@
                         }
                     }
                 }
-              
+
                 if (uri != null) {
-                    // add entry to audio_genre_map  
+                    // add entry to audio_genre_map
                     values.clear();
                     values.put(MediaStore.Audio.Genres.Members.AUDIO_ID, Long.valueOf(rowId));
                     mMediaProvider.insert(uri, values);
                 }
             }
-            
+
             if (notifications && !mDefaultNotificationSet) {
                 if (TextUtils.isEmpty(mDefaultNotificationFilename) ||
                         doesPathHaveFilename(entry.mPath, mDefaultNotificationFilename)) {
@@ -752,36 +760,36 @@
                     mDefaultRingtoneSet = true;
                 }
             }
-            
+
             return result;
         }
-        
+
         private boolean doesPathHaveFilename(String path, String filename) {
             int pathFilenameStart = path.lastIndexOf(File.separatorChar) + 1;
             int filenameLength = filename.length();
             return path.regionMatches(pathFilenameStart, filename, 0, filenameLength) &&
                     pathFilenameStart + filenameLength == path.length();
         }
-        
+
         private void setSettingIfNotSet(String settingName, Uri uri, long rowId) {
-            
+
             String existingSettingValue = Settings.System.getString(mContext.getContentResolver(),
                     settingName);
-            
+
             if (TextUtils.isEmpty(existingSettingValue)) {
                 // Set the setting to the given URI
                 Settings.System.putString(mContext.getContentResolver(), settingName,
                         ContentUris.withAppendedId(uri, rowId).toString());
             }
         }
-        
+
     }; // end of anonymous MediaScannerClient instance
-    
+
     private void prescan(String filePath) throws RemoteException {
         Cursor c = null;
         String where = null;
         String[] selectionArgs = null;
-         
+
         if (mFileCache == null) {
             mFileCache = new HashMap<String, FileCacheEntry>();
         } else {
@@ -792,7 +800,7 @@
         } else {
             mPlayLists.clear();
         }
-  
+
         // Build the list of files from the content provider
         try {
             // Read existing files from the audio table
@@ -801,14 +809,14 @@
                 selectionArgs = new String[] { filePath };
             }
             c = mMediaProvider.query(mAudioUri, AUDIO_PROJECTION, where, selectionArgs, null);
- 
+
             if (c != null) {
                 try {
                     while (c.moveToNext()) {
                         long rowId = c.getLong(ID_AUDIO_COLUMN_INDEX);
                         String path = c.getString(PATH_AUDIO_COLUMN_INDEX);
                         long lastModified = c.getLong(DATE_MODIFIED_AUDIO_COLUMN_INDEX);
-                        
+
                         String key = path;
                         if (mCaseInsensitivePaths) {
                             key = path.toLowerCase();
@@ -829,14 +837,14 @@
                 where = null;
             }
             c = mMediaProvider.query(mVideoUri, VIDEO_PROJECTION, where, selectionArgs, null);
- 
+
             if (c != null) {
                 try {
                     while (c.moveToNext()) {
                         long rowId = c.getLong(ID_VIDEO_COLUMN_INDEX);
                         String path = c.getString(PATH_VIDEO_COLUMN_INDEX);
                         long lastModified = c.getLong(DATE_MODIFIED_VIDEO_COLUMN_INDEX);
-                        
+
                         String key = path;
                         if (mCaseInsensitivePaths) {
                             key = path.toLowerCase();
@@ -858,7 +866,7 @@
             }
             mOriginalCount = 0;
             c = mMediaProvider.query(mImagesUri, IMAGES_PROJECTION, where, selectionArgs, null);
- 
+
             if (c != null) {
                 try {
                     mOriginalCount = c.getCount();
@@ -866,7 +874,7 @@
                         long rowId = c.getLong(ID_IMAGES_COLUMN_INDEX);
                         String path = c.getString(PATH_IMAGES_COLUMN_INDEX);
                        long lastModified = c.getLong(DATE_MODIFIED_IMAGES_COLUMN_INDEX);
-    
+
                         String key = path;
                         if (mCaseInsensitivePaths) {
                             key = path.toLowerCase();
@@ -879,7 +887,7 @@
                     c = null;
                 }
             }
-            
+
             if (mProcessPlaylists) {
                 // Read existing files from the playlists table
                 if (filePath != null) {
@@ -888,16 +896,16 @@
                     where = null;
                 }
                 c = mMediaProvider.query(mPlaylistsUri, PLAYLISTS_PROJECTION, where, selectionArgs, null);
-     
+
                 if (c != null) {
                     try {
                         while (c.moveToNext()) {
                             String path = c.getString(PATH_IMAGES_COLUMN_INDEX);
-            
+
                             if (path != null && path.length() > 0) {
                                 long rowId = c.getLong(ID_PLAYLISTS_COLUMN_INDEX);
                                 long lastModified = c.getLong(DATE_MODIFIED_PLAYLISTS_COLUMN_INDEX);
-    
+
                                 String key = path;
                                 if (mCaseInsensitivePaths) {
                                     key = path.toLowerCase();
@@ -919,7 +927,7 @@
             }
         }
     }
-    
+
     private boolean inScanDirectory(String path, String[] directories) {
         for (int i = 0; i < directories.length; i++) {
             if (path.startsWith(directories[i])) {
@@ -928,25 +936,25 @@
         }
         return false;
     }
-    
+
     private void pruneDeadThumbnailFiles() {
         HashSet<String> existingFiles = new HashSet<String>();
         String directory = "/sdcard/DCIM/.thumbnails";
         String [] files = (new File(directory)).list();
         if (files == null)
             files = new String[0];
-        
+
         for (int i = 0; i < files.length; i++) {
             String fullPathString = directory + "/" + files[i];
             existingFiles.add(fullPathString);
         }
-        
+
         try {
             Cursor c = mMediaProvider.query(
-                    mThumbsUri, 
-                    new String [] { "_data" }, 
-                    null, 
-                    null, 
+                    mThumbsUri,
+                    new String [] { "_data" },
+                    null,
+                    null,
                     null);
             Log.v(TAG, "pruneDeadThumbnailFiles... " + c);
             if (c != null && c.moveToFirst()) {
@@ -955,7 +963,7 @@
                     existingFiles.remove(fullPathString);
                 } while (c.moveToNext());
             }
-            
+
             for (String fileToDelete : existingFiles) {
                 if (Config.LOGV)
                     Log.v(TAG, "fileToDelete is " + fileToDelete);
@@ -964,7 +972,7 @@
                 } catch (SecurityException ex) {
                 }
             }
-            
+
             Log.v(TAG, "/pruneDeadThumbnailFiles... " + c);
             if (c != null) {
                 c.close();
@@ -980,10 +988,10 @@
         while (iterator.hasNext()) {
             FileCacheEntry entry = iterator.next();
             String path = entry.mPath;
-            
+
             // remove database entries for files that no longer exist.
             boolean fileMissing = false;
-            
+
             if (!entry.mSeenInFileSystem) {
                 if (inScanDirectory(path, directories)) {
                     // we didn't see this file in the scan directory.
@@ -997,7 +1005,7 @@
                     }
                 }
             }
-            
+
             if (fileMissing) {
                 // do not delete missing playlists, since they may have been modified by the user.
                 // the user can delete them in the media player instead.
@@ -1016,25 +1024,25 @@
                 }
             }
         }
-        
+
         // handle playlists last, after we know what media files are on the storage.
         if (mProcessPlaylists) {
             processPlayLists();
         }
-        
+
         if (mOriginalCount == 0 && mImagesUri.equals(Images.Media.getContentUri("external")))
             pruneDeadThumbnailFiles();
-        
+
         // allow GC to clean up
         mGenreCache = null;
         mPlayLists = null;
         mFileCache = null;
         mMediaProvider = null;
     }
-    
+
     private void initialize(String volumeName) {
         mMediaProvider = mContext.getContentResolver().acquireProvider("media");
-        
+
         mAudioUri = Audio.Media.getContentUri(volumeName);
         mVideoUri = Video.Media.getContentUri(volumeName);
         mImagesUri = Images.Media.getContentUri(volumeName);
@@ -1051,23 +1059,23 @@
             if ( Process.supportsProcesses()) {
                 mCaseInsensitivePaths = true;
             }
-        }          
+        }
     }
 
     public void scanDirectories(String[] directories, String volumeName) {
         try {
             long start = System.currentTimeMillis();
-            initialize(volumeName);    
+            initialize(volumeName);
             prescan(null);
             long prescan = System.currentTimeMillis();
-            
+
             for (int i = 0; i < directories.length; i++) {
                 processDirectory(directories[i], MediaFile.sFileExtensions, mClient);
             }
             long scan = System.currentTimeMillis();
             postscan(directories);
             long end = System.currentTimeMillis();
-            
+
             if (Config.LOGD) {
                 Log.d(TAG, " prescan time: " + (prescan - start) + "ms\n");
                 Log.d(TAG, "    scan time: " + (scan - prescan) + "ms\n");
@@ -1088,9 +1096,9 @@
     // this function is used to scan a single file
     public Uri scanSingleFile(String path, String volumeName, String mimeType) {
         try {
-            initialize(volumeName);        
+            initialize(volumeName);
             prescan(path);
-    
+
             File file = new File(path);
             // always scan the file, so we can return the content://media Uri for existing files
             return mClient.doScanFile(path, mimeType, file.lastModified(), file.length(), true);
@@ -1105,7 +1113,7 @@
         int result = 0;
         int end1 = path1.length();
         int end2 = path2.length();
-        
+
         while (end1 > 0 && end2 > 0) {
             int slash1 = path1.lastIndexOf('/', end1 - 1);
             int slash2 = path2.lastIndexOf('/', end2 - 1);
@@ -1123,13 +1131,13 @@
                 end2 = start2 - 1;
             } else break;
         }
-               
+
         return result;
     }
 
-    private boolean addPlayListEntry(String entry, String playListDirectory, 
+    private boolean addPlayListEntry(String entry, String playListDirectory,
             Uri uri, ContentValues values, int index) {
-        
+
         // watch for trailing whitespace
         int entryLength = entry.length();
         while (entryLength > 0 && Character.isWhitespace(entry.charAt(entryLength - 1))) entryLength--;
@@ -1146,36 +1154,36 @@
         // if we have a relative path, combine entry with playListDirectory
         if (!fullPath)
             entry = playListDirectory + entry;
-            
+
         //FIXME - should we look for "../" within the path?
-        
+
         // best matching MediaFile for the play list entry
         FileCacheEntry bestMatch = null;
-        
+
         // number of rightmost file/directory names for bestMatch
-        int bestMatchLength = 0;    
-                
+        int bestMatchLength = 0;
+
         Iterator<FileCacheEntry> iterator = mFileCache.values().iterator();
         while (iterator.hasNext()) {
             FileCacheEntry cacheEntry = iterator.next();
             String path = cacheEntry.mPath;
-        
+
             if (path.equalsIgnoreCase(entry)) {
                 bestMatch = cacheEntry;
                 break;    // don't bother continuing search
             }
-            
+
             int matchLength = matchPaths(path, entry);
             if (matchLength > bestMatchLength) {
                 bestMatch = cacheEntry;
                 bestMatchLength = matchLength;
             }
         }
-        
+
         if (bestMatch == null) {
             return false;
         }
-        
+
         try {
         // OK, now we need to add this to the database
             values.clear();
@@ -1189,7 +1197,7 @@
 
         return true;
     }
-    
+
     private void processM3uPlayList(String path, String playListDirectory, Uri uri, ContentValues values) {
         BufferedReader reader = null;
         try {
@@ -1266,7 +1274,7 @@
         public WplHandler(String playListDirectory, Uri uri) {
             this.playListDirectory = playListDirectory;
             this.uri = uri;
-            
+
             RootElement root = new RootElement("smil");
             Element body = root.getChild("body");
             Element seq = body.getChild("seq");
@@ -1316,12 +1324,12 @@
             }
         }
     }
-    
+
     private void processPlayLists() throws RemoteException {
         Iterator<FileCacheEntry> iterator = mPlayLists.iterator();
         while (iterator.hasNext()) {
             FileCacheEntry entry = iterator.next();
-            String path = entry.mPath;  
+            String path = entry.mPath;
 
             // only process playlist files if they are new or have been modified since the last scan
             if (entry.mLastModifiedChanged) {
@@ -1332,7 +1340,7 @@
                 long rowId = entry.mRowId;
                 if (rowId == 0) {
                     // Create a new playlist
-        
+
                     int lastDot = path.lastIndexOf('.');
                     String name = (lastDot < 0 ? path.substring(lastSlash + 1) : path.substring(lastSlash + 1, lastDot));
                     values.put(MediaStore.Audio.Playlists.NAME, name);
@@ -1343,7 +1351,7 @@
                     membersUri = Uri.withAppendedPath(uri, Playlists.Members.CONTENT_DIRECTORY);
                 } else {
                     uri = ContentUris.withAppendedId(mPlaylistsUri, rowId);
-                    
+
                     // update lastModified value of existing playlist
                     values.put(MediaStore.Audio.Playlists.DATE_MODIFIED, entry.mLastModified);
                     mMediaProvider.update(uri, values, null, null);
@@ -1352,7 +1360,7 @@
                     membersUri = Uri.withAppendedPath(uri, Playlists.Members.CONTENT_DIRECTORY);
                     mMediaProvider.delete(membersUri, null, null);
                 }
-               
+
                 String playListDirectory = path.substring(0, lastSlash + 1);
                 MediaFile.MediaFileType mediaFileType = MediaFile.getFileType(path);
                 int fileType = (mediaFileType == null ? 0 : mediaFileType.fileType);
@@ -1363,7 +1371,7 @@
                     processPlsPlayList(path, playListDirectory, membersUri, values);
                 else if (fileType == MediaFile.FILE_TYPE_WPL)
                     processWplPlayList(path, playListDirectory, membersUri);
-                    
+
                 Cursor cursor = mMediaProvider.query(membersUri, PLAYLIST_MEMBERS_PROJECTION, null,
                         null, null);
                 try {
@@ -1377,18 +1385,18 @@
             }
         }
     }
-    
+
     private native void processDirectory(String path, String extensions, MediaScannerClient client);
     private native void processFile(String path, String mimeType, MediaScannerClient client);
     public native void setLocale(String locale);
-    
+
     public native byte[] extractAlbumArt(FileDescriptor fd);
 
     private native final void native_setup();
     private native final void native_finalize();
     @Override
-    protected void finalize() { 
+    protected void finalize() {
         mContext.getContentResolver().releaseProvider(mMediaProvider);
-        native_finalize(); 
+        native_finalize();
     }
 }
diff --git a/media/libmedia/ToneGenerator.cpp b/media/libmedia/ToneGenerator.cpp
index c22cd53..5435da7 100644
--- a/media/libmedia/ToneGenerator.cpp
+++ b/media/libmedia/ToneGenerator.cpp
@@ -1225,6 +1225,8 @@
             LOGV("Cbk restarting track\n");
             if (lpToneGen->prepareWave()) {
                 lpToneGen->mState = TONE_STARTING;
+                // must reload lpToneDesc as prepareWave() may change mpToneDesc
+                lpToneDesc = lpToneGen->mpToneDesc;
             } else {
                 LOGW("Cbk restarting prepareWave() failed\n");
                 lpToneGen->mState = TONE_IDLE;
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index b6bc8a5..2b36904 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -16,12 +16,16 @@
 
 package com.android.providers.settings;
 
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.EOFException;
 import java.util.Arrays;
 import java.util.HashMap;
+import java.util.zip.CRC32;
 
 import android.backup.BackupDataInput;
 import android.backup.BackupDataOutput;
@@ -34,7 +38,9 @@
 import android.media.AudioManager;
 import android.net.Uri;
 import android.net.wifi.WifiManager;
+import android.os.FileUtils;
 import android.os.ParcelFileDescriptor;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.provider.Settings;
@@ -52,6 +58,13 @@
     private static final String KEY_SYNC = "sync_providers";
     private static final String KEY_LOCALE = "locale";
 
+    private static final int STATE_SYSTEM = 0;
+    private static final int STATE_SECURE = 1;
+    private static final int STATE_SYNC   = 2;
+    private static final int STATE_LOCALE = 3;
+    private static final int STATE_WIFI   = 4;
+    private static final int STATE_SIZE   = 5; // The number of state items
+
     private static String[] sortedSystemKeys = null;
     private static String[] sortedSecureKeys = null;
 
@@ -87,20 +100,22 @@
         byte[] secureSettingsData = getSecureSettings();
         byte[] syncProviders = mSettingsHelper.getSyncProviders();
         byte[] locale = mSettingsHelper.getLocaleData();
-        
-        data.writeEntityHeader(KEY_SYSTEM, systemSettingsData.length);
-        data.writeEntityData(systemSettingsData, systemSettingsData.length);
+        byte[] wifiData = getFileData(FILE_WIFI_SUPPLICANT);
 
-        data.writeEntityHeader(KEY_SECURE, secureSettingsData.length);
-        data.writeEntityData(secureSettingsData, secureSettingsData.length);
+        long[] stateChecksums = readOldChecksums(oldState);
 
-        data.writeEntityHeader(KEY_SYNC, syncProviders.length);
-        data.writeEntityData(syncProviders, syncProviders.length);
+        stateChecksums[STATE_SYSTEM] =
+                writeIfChanged(stateChecksums[STATE_SYSTEM], KEY_SYSTEM, systemSettingsData, data);
+        stateChecksums[STATE_SECURE] =
+                writeIfChanged(stateChecksums[STATE_SECURE], KEY_SECURE, secureSettingsData, data);
+        stateChecksums[STATE_SYNC] =
+                writeIfChanged(stateChecksums[STATE_SYNC], KEY_SYNC, syncProviders, data);
+        stateChecksums[STATE_LOCALE] =
+                writeIfChanged(stateChecksums[STATE_LOCALE], KEY_LOCALE, locale, data);
+        stateChecksums[STATE_WIFI] =
+                writeIfChanged(stateChecksums[STATE_WIFI], FILE_WIFI_SUPPLICANT, wifiData, data);
 
-        data.writeEntityHeader(KEY_LOCALE, locale.length);
-        data.writeEntityData(locale, locale.length);
-
-        backupFile(FILE_WIFI_SUPPLICANT, data);
+        writeNewChecksums(stateChecksums, newState);
     }
 
     @Override
@@ -115,11 +130,15 @@
             final int size = data.getDataSize();
             if (KEY_SYSTEM.equals(key)) {
                 restoreSettings(data, Settings.System.CONTENT_URI);
+                mSettingsHelper.applyAudioSettings();
             } else if (KEY_SECURE.equals(key)) {
                 restoreSettings(data, Settings.Secure.CONTENT_URI);
-// TODO: Re-enable WIFI restore when we figure out a solution for the permissions
-//            } else if (FILE_WIFI_SUPPLICANT.equals(key)) {
-//                restoreFile(FILE_WIFI_SUPPLICANT, data);
+            } else if (FILE_WIFI_SUPPLICANT.equals(key)) {
+                restoreFile(FILE_WIFI_SUPPLICANT, data);
+                FileUtils.setPermissions(FILE_WIFI_SUPPLICANT,
+                        FileUtils.S_IRUSR | FileUtils.S_IWUSR |
+                        FileUtils.S_IRGRP | FileUtils.S_IWGRP,
+                        Process.myUid(), Process.WIFI_UID);
             } else if (KEY_SYNC.equals(key)) {
                 mSettingsHelper.setSyncProviders(data);
             } else if (KEY_LOCALE.equals(key)) {
@@ -132,6 +151,49 @@
         }
     }
 
+    private long[] readOldChecksums(ParcelFileDescriptor oldState) throws IOException {
+        long[] stateChecksums = new long[STATE_SIZE];
+
+        DataInputStream dataInput = new DataInputStream(
+                new FileInputStream(oldState.getFileDescriptor()));
+        for (int i = 0; i < STATE_SIZE; i++) {
+            try {
+                stateChecksums[i] = dataInput.readLong();
+            } catch (EOFException eof) {
+                break;
+            }
+        }
+        dataInput.close();
+        return stateChecksums;
+    }
+
+    private void writeNewChecksums(long[] checksums, ParcelFileDescriptor newState)
+            throws IOException {
+        DataOutputStream dataOutput = new DataOutputStream(
+                new FileOutputStream(newState.getFileDescriptor()));
+        for (int i = 0; i < STATE_SIZE; i++) {
+            dataOutput.writeLong(checksums[i]);
+        }
+        dataOutput.close();
+    }
+
+    private long writeIfChanged(long oldChecksum, String key, byte[] data,
+            BackupDataOutput output) {
+        CRC32 checkSummer = new CRC32();
+        checkSummer.update(data);
+        long newChecksum = checkSummer.getValue();
+        if (oldChecksum == newChecksum) {
+            return oldChecksum;
+        }
+        try {
+            output.writeEntityHeader(key, data.length);
+            output.writeEntityData(data, data.length);
+        } catch (IOException ioe) {
+            // Bail
+        }
+        return newChecksum;
+    }
+
     private byte[] getSystemSettings() {
         Cursor sortedCursor = getContentResolver().query(Settings.System.CONTENT_URI, PROJECTION,
                 null, null, Settings.NameValueTable.NAME);
@@ -248,7 +310,7 @@
         return result;
     }
 
-    private void backupFile(String filename, BackupDataOutput data) {
+    private byte[] getFileData(String filename) {
         try {
             File file = new File(filename);
             if (file.exists()) {
@@ -260,14 +322,13 @@
                     got = fis.read(bytes, offset, bytes.length - offset);
                     if (got > 0) offset += got;
                 } while (offset < bytes.length && got > 0);
-                data.writeEntityHeader(filename, bytes.length);
-                data.writeEntityData(bytes, bytes.length);
+                return bytes;
             } else {
-                data.writeEntityHeader(filename, 0);
-                data.writeEntityData(EMPTY_DATA, 0);
+                return EMPTY_DATA;
             }
         } catch (IOException ioe) {
             Log.w(TAG, "Couldn't backup " + filename);
+            return EMPTY_DATA;
         }
     }
 
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
index 2c5775a..ca739e6 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
@@ -167,6 +167,9 @@
         // Check if locale was set by the user:
         Configuration conf = mContext.getResources().getConfiguration();
         Locale loc = conf.locale;
+        // TODO: The following is not working as intended because the network is forcing a locale
+        // change after registering. Need to find some other way to detect if the user manually
+        // changed the locale
         if (conf.userSetLocale) return; // Don't change if user set it in the SetupWizard
 
         final String[] availableLocales = mContext.getAssets().getLocales();
@@ -193,6 +196,14 @@
         } catch (RemoteException e) {
             // Intentionally left blank
         }
+    }
 
+    /**
+     * Informs the audio service of changes to the settings so that
+     * they can be re-read and applied.
+     */
+    void applyAudioSettings() {
+        AudioManager am = new AudioManager(mContext);
+        am.reloadAudioSettings();
     }
 }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 2abf8b3..c0de9a5 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -126,11 +126,14 @@
         // a notification and then using the contract class to get their data,
         // the system property will be updated and they'll get the new data.
 
+        boolean backedUpDataChanged = false;
         String property = null, table = uri.getPathSegments().get(0);
         if (table.equals("system")) {
             property = Settings.System.SYS_PROP_SETTING_VERSION;
+            backedUpDataChanged = true;
         } else if (table.equals("secure")) {
             property = Settings.Secure.SYS_PROP_SETTING_VERSION;
+            backedUpDataChanged = true;
         } else if (table.equals("gservices")) {
             property = Settings.Gservices.SYS_PROP_SETTING_VERSION;
         }
@@ -142,7 +145,9 @@
         }
 
         // Inform the backup manager about a data change
-        mBackupManager.dataChanged();
+        if (backedUpDataChanged) {
+            mBackupManager.dataChanged();
+        }
         // Now send the notification through the content framework.
 
         String notify = uri.getQueryParameter("notify");
diff --git a/packages/TtsService/src/android/tts/TtsService.java b/packages/TtsService/src/android/tts/TtsService.java
index a713edf..1eba469 100755
--- a/packages/TtsService/src/android/tts/TtsService.java
+++ b/packages/TtsService/src/android/tts/TtsService.java
@@ -38,6 +38,8 @@
 import java.util.HashMap;
 import java.util.Locale;
 import java.util.concurrent.locks.ReentrantLock;
+import java.util.concurrent.TimeUnit;
+
 
 /**
  * @hide Synthesizes speech from text. This is implemented as a service so that
@@ -358,20 +360,38 @@
      * Stops all speech output and removes any utterances still in the queue.
      */
     private int stop() {
-        Log.i("TTS", "Stopping");
-        mSpeechQueue.clear();
+        int result = TextToSpeech.TTS_ERROR;
+        boolean speechQueueAvailable = false;
+        try{
+            // If the queue is locked for more than 1 second,
+            // something has gone very wrong with processSpeechQueue.
+            speechQueueAvailable = speechQueueLock.tryLock(1000, TimeUnit.MILLISECONDS);
+            if (speechQueueAvailable) {
+                Log.i("TTS", "Stopping");
+                mSpeechQueue.clear();
 
-        int result = nativeSynth.stop();
-        mIsSpeaking = false;
-        if (mPlayer != null) {
-            try {
-                mPlayer.stop();
-            } catch (IllegalStateException e) {
-                // Do nothing, the player is already stopped.
+                result = nativeSynth.stop();
+                mIsSpeaking = false;
+                if (mPlayer != null) {
+                    try {
+                        mPlayer.stop();
+                    } catch (IllegalStateException e) {
+                        // Do nothing, the player is already stopped.
+                    }
+                }
+                Log.i("TTS", "Stopped");
             }
+        } catch (InterruptedException e) {
+          Log.e("TTS stop", "tryLock interrupted");
+          e.printStackTrace();
+        } finally {
+            // This check is needed because finally will always run; even if the
+            // method returns somewhere in the try block.
+            if (speechQueueAvailable) {
+                speechQueueLock.unlock();
+            }
+            return result;
         }
-        Log.i("TTS", "Stopped");
-        return result;
     }
 
     public void onCompletion(MediaPlayer arg0) {
@@ -443,6 +463,7 @@
                     }
                     nativeSynth.speak(text);
                 } catch (InterruptedException e) {
+                    Log.e("TTS speakInternalOnly", "tryLock interrupted");
                     e.printStackTrace();
                 } finally {
                     // This check is needed because finally will always run;
@@ -497,6 +518,7 @@
                     }
                     nativeSynth.synthesizeToFile(text, filename);
                 } catch (InterruptedException e) {
+                    Log.e("TTS synthToFileInternalOnly", "tryLock interrupted");
                     e.printStackTrace();
                 } finally {
                     // This check is needed because finally will always run;
diff --git a/packages/VpnServices/src/com/android/server/vpn/VpnService.java b/packages/VpnServices/src/com/android/server/vpn/VpnService.java
index a60788a..22669d2 100644
--- a/packages/VpnServices/src/com/android/server/vpn/VpnService.java
+++ b/packages/VpnServices/src/com/android/server/vpn/VpnService.java
@@ -189,7 +189,7 @@
 
             mServiceHelper.stop();
         } catch (Throwable e) {
-            Log.e(TAG, "onError()", e);
+            Log.e(TAG, "onDisconnect()", e);
             onFinalCleanUp();
         }
     }
@@ -219,21 +219,28 @@
     }
 
     private void waitUntilConnectedOrTimedout() {
-        sleep(2000); // 2 seconds
-        for (int i = 0; i < 60; i++) {
-            if (VPN_IS_UP.equals(SystemProperties.get(VPN_UP))) {
-                onConnected();
-                return;
-            }
-            sleep(500); // 0.5 second
-        }
+        // Run this in the background thread to not block UI
+        new Thread(new Runnable() {
+            public void run() {
+                sleep(2000); // 2 seconds
+                for (int i = 0; i < 60; i++) {
+                    if (VPN_IS_UP.equals(SystemProperties.get(VPN_UP))) {
+                        onConnected();
+                        return;
+                    } else if (mState != VpnState.CONNECTING) {
+                        break;
+                    }
+                    sleep(500); // 0.5 second
+                }
 
-        synchronized (this) {
-            if (mState == VpnState.CONNECTING) {
-                Log.d(TAG, "       connecting timed out !!");
-                onError();
+                synchronized (VpnService.this) {
+                    if (mState == VpnState.CONNECTING) {
+                        Log.d(TAG, "       connecting timed out !!");
+                        onError();
+                    }
+                }
             }
-        }
+        }).start();
     }
 
     private synchronized void onConnected() {
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 190d3e6..38fb7c9 100644
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -49,6 +49,7 @@
 import android.os.Message;
 import android.os.Power;
 import android.os.RemoteException;
+import android.os.SystemProperties;
 import android.os.Vibrator;
 import android.provider.Settings;
 import android.text.TextUtils;
@@ -945,6 +946,9 @@
     // to accidentally lose.
     private void updateAdbNotification() {
         if (mAdbEnabled && mBatteryPlugged == BatteryManager.BATTERY_PLUGGED_USB) {
+            if ("0".equals(SystemProperties.get("persist.adb.notify"))) {
+                return;
+            }
             if (!mAdbNotificationShown) {
                 NotificationManager notificationManager = (NotificationManager) mContext
                         .getSystemService(Context.NOTIFICATION_SERVICE);
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index a940af3..01394ad 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -436,7 +436,7 @@
      * see {@link android.net.wifi.WifiManager#startScan()}
      * @return {@code true} if the operation succeeds
      */
-    public boolean startScan() {
+    public boolean startScan(boolean forceActive) {
         enforceChangePermission();
         synchronized (mWifiStateTracker) {
             switch (mWifiStateTracker.getSupplicantState()) {
@@ -450,7 +450,7 @@
                             WifiStateTracker.SUPPL_SCAN_HANDLING_LIST_ONLY);
                     break;
             }
-            return WifiNative.scanCommand();
+            return WifiNative.scanCommand(forceActive);
         }
     }
 
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 2fe4dd4..aad542a 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -56,6 +56,7 @@
 import android.content.pm.IPackageManager;
 import android.content.pm.InstrumentationInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.PathPermission;
 import android.content.pm.ProviderInfo;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
@@ -7072,6 +7073,27 @@
                 == PackageManager.PERMISSION_GRANTED) {
             return null;
         }
+        
+        PathPermission[] pps = cpi.pathPermissions;
+        if (pps != null) {
+            int i = pps.length;
+            while (i > 0) {
+                i--;
+                PathPermission pp = pps[i];
+                if (checkComponentPermission(pp.getReadPermission(), callingPid, callingUid,
+                        cpi.exported ? -1 : cpi.applicationInfo.uid)
+                        == PackageManager.PERMISSION_GRANTED
+                        && mode == ParcelFileDescriptor.MODE_READ_ONLY || mode == -1) {
+                    return null;
+                }
+                if (checkComponentPermission(pp.getWritePermission(), callingPid, callingUid,
+                        cpi.exported ? -1 : cpi.applicationInfo.uid)
+                        == PackageManager.PERMISSION_GRANTED) {
+                    return null;
+                }
+            }
+        }
+        
         String msg = "Permission Denial: opening provider " + cpi.name
                 + " from " + (r != null ? r : "(null)") + " (pid=" + callingPid
                 + ", uid=" + callingUid + ") requires "
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/LoadTestsAutoTest.java b/tests/DumpRenderTree/src/com/android/dumprendertree/LoadTestsAutoTest.java
index cbcac6c..39bbf16 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/LoadTestsAutoTest.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/LoadTestsAutoTest.java
@@ -72,6 +72,7 @@
         // Run tests
         runTestAndWaitUntilDone(activity, runner.mTestPath, runner.mTimeoutInMillis);
 
+        activity.clearCache();
         dumpMemoryInfo();
 
         // Kill activity
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
index 0d22eca..09f7cbc 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
@@ -87,6 +87,10 @@
         }
     }
 
+    public void clearCache() {
+      mWebView.clearCache(true);
+    }
+
     @Override
     protected void onCreate(Bundle icicle) {
         super.onCreate(icicle);
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 3d65d3c..fa328e8 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -40,7 +40,7 @@
 
     boolean pingSupplicant();
 
-    boolean startScan();
+    boolean startScan(boolean forceActive);
 
     List<ScanResult> getScanResults();
 
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 7a15f27..1f73bec 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -476,9 +476,27 @@
      * on completion of the scan.
      * @return {@code true} if the operation succeeded, i.e., the scan was initiated
      */
-    public boolean  startScan() {
+    public boolean startScan() {
         try {
-            return mService.startScan();
+            return mService.startScan(false);
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Request a scan for access points. Returns immediately. The availability
+     * of the results is made known later by means of an asynchronous event sent
+     * on completion of the scan.
+     * This is a variant of startScan that forces an active scan, even if passive
+     * scans are the current default
+     * @return {@code true} if the operation succeeded, i.e., the scan was initiated
+     *
+     * @hide
+     */
+    public boolean startScanActive() {
+        try {
+            return mService.startScan(true);
         } catch (RemoteException e) {
             return false;
         }
diff --git a/wifi/java/android/net/wifi/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java
index 3851ac0..0799f5f 100644
--- a/wifi/java/android/net/wifi/WifiNative.java
+++ b/wifi/java/android/net/wifi/WifiNative.java
@@ -51,7 +51,7 @@
 
     public native static boolean pingCommand();
 
-    public native static boolean scanCommand();
+    public native static boolean scanCommand(boolean forceActive);
     
     public native static boolean setScanModeCommand(boolean setActive);
 
diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java
index 2fbc779..63687b3 100644
--- a/wifi/java/android/net/wifi/WifiStateTracker.java
+++ b/wifi/java/android/net/wifi/WifiStateTracker.java
@@ -1121,7 +1121,7 @@
                             } else {
                                 // In some situations, supplicant needs to be kickstarted to
                                 // start the background scanning
-                                WifiNative.scanCommand();
+                                WifiNative.scanCommand(true);
                             }
                         }
                     }