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=""android.permission.GLOBAL_SEARCH""
+ 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<android.gesture.GestureStroke>"
+ 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<java.lang.String>"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getGestures"
+ return="java.util.ArrayList<android.gesture.Gesture>"
+ 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<android.gesture.Prediction>"
+ 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<android.gesture.GesturePoint>"
+ 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<java.lang.String>"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getGestures"
+ return="java.util.ArrayList<android.gesture.Gesture>"
+ 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<android.gesture.Prediction>"
+ 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<android.gesture.GesturePoint>">
+</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);
}
}
}